Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 1 | # Copyright (C) 2022 The Android Open Source Project |
| 2 | # |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | # you may not use this file except in compliance with the License. |
| 5 | # You may obtain a copy of the License at |
| 6 | # |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | # |
| 9 | # Unless required by applicable law or agreed to in writing, software |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | # See the License for the specific language governing permissions and |
| 13 | # limitations under the License. |
| 14 | |
| 15 | # A collection of utilities for extracting build rule information from GN |
| 16 | # projects. |
| 17 | |
Motomu Utsumi | 5fde8e1 | 2022-12-16 16:59:46 +0900 | [diff] [blame] | 18 | import copy |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 19 | import json |
Patrick Rohr | af92fa6 | 2022-11-04 14:27:04 -0700 | [diff] [blame] | 20 | import logging as log |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 21 | import os |
| 22 | import re |
Motomu Utsumi | ea729d3 | 2022-12-23 17:06:41 +0900 | [diff] [blame] | 23 | import collections |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 24 | |
| 25 | BUILDFLAGS_TARGET = '//gn:gen_buildflags' |
| 26 | GEN_VERSION_TARGET = '//src/base:version_gen_h' |
Mohannad Farrag | 7f29d83 | 2022-11-23 19:52:41 +0000 | [diff] [blame] | 27 | LINKER_UNIT_TYPES = ('executable', 'shared_library', 'static_library', 'source_set') |
Mohannad Farrag | 6a2d88a | 2022-11-28 19:33:48 +0000 | [diff] [blame] | 28 | JAVA_BANNED_SCRIPTS = [ |
| 29 | "//build/android/gyp/turbine.py", |
| 30 | "//build/android/gyp/compile_java.py", |
| 31 | "//build/android/gyp/filter_zip.py", |
| 32 | "//build/android/gyp/dex.py", |
| 33 | "//build/android/gyp/write_build_config.py", |
| 34 | "//build/android/gyp/create_r_java.py", |
| 35 | "//build/android/gyp/ijar.py", |
| 36 | "//build/android/gyp/create_r_java.py", |
| 37 | "//build/android/gyp/bytecode_processor.py", |
| 38 | "//build/android/gyp/prepare_resources.py", |
| 39 | "//build/android/gyp/aar.py", |
| 40 | "//build/android/gyp/zip.py", |
| 41 | ] |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 42 | # TODO(primiano): investigate these, they require further componentization. |
| 43 | ODR_VIOLATION_IGNORE_TARGETS = { |
| 44 | '//test/cts:perfetto_cts_deps', |
| 45 | '//:perfetto_integrationtests', |
| 46 | } |
Mohannad Farrag | 4bea14a | 2022-11-22 15:38:30 +0000 | [diff] [blame] | 47 | ARCH_REGEX = r'(android_x86_64|android_x86|android_arm|android_arm64|host)' |
Mohannad Farrag | a0e37c1 | 2022-12-02 14:46:08 +0000 | [diff] [blame] | 48 | RESPONSE_FILE = '{{response_file_name}}' |
Mohannad Farrag | edb2fd0 | 2023-02-10 14:53:41 +0000 | [diff] [blame] | 49 | TESTING_SUFFIX = "__testing" |
Mohannad Farrag | cdbfeb8 | 2023-01-16 13:35:20 +0000 | [diff] [blame] | 50 | AIDL_INCLUDE_DIRS_REGEX = r'--includes=\[(.*)\]' |
Mohannad Farrag | a0e37c1 | 2022-12-02 14:46:08 +0000 | [diff] [blame] | 51 | |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 52 | def repo_root(): |
| 53 | """Returns an absolute path to the repository root.""" |
| 54 | return os.path.join( |
| 55 | os.path.realpath(os.path.dirname(__file__)), os.path.pardir) |
| 56 | |
| 57 | |
Mohannad Farrag | cdbfeb8 | 2023-01-16 13:35:20 +0000 | [diff] [blame] | 58 | def _clean_string(str): |
| 59 | return str.replace('\\', '').replace('../../', '').replace('"', '').strip() |
| 60 | |
| 61 | |
| 62 | def _extract_includes_from_aidl_args(args): |
| 63 | for arg in args: |
| 64 | is_match = re.match(AIDL_INCLUDE_DIRS_REGEX, arg) |
| 65 | if is_match: |
| 66 | local_includes = is_match.group(1).split(",") |
| 67 | return [_clean_string(local_include) for local_include in local_includes] |
| 68 | return [] |
| 69 | |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 70 | def label_to_path(label): |
| 71 | """Turn a GN output label (e.g., //some_dir/file.cc) into a path.""" |
| 72 | assert label.startswith('//') |
Patrick Rohr | c6331c8 | 2022-10-25 11:34:20 -0700 | [diff] [blame] | 73 | return label[2:] or "./" |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 74 | |
| 75 | |
| 76 | def label_without_toolchain(label): |
| 77 | """Strips the toolchain from a GN label. |
| 78 | |
| 79 | Return a GN label (e.g //buildtools:protobuf(//gn/standalone/toolchain: |
| 80 | gcc_like_host) without the parenthesised toolchain part. |
| 81 | """ |
| 82 | return label.split('(')[0] |
| 83 | |
| 84 | |
| 85 | def label_to_target_name_with_path(label): |
| 86 | """ |
| 87 | Turn a GN label into a target name involving the full path. |
| 88 | e.g., //src/perfetto:tests -> src_perfetto_tests |
| 89 | """ |
| 90 | name = re.sub(r'^//:?', '', label) |
| 91 | name = re.sub(r'[^a-zA-Z0-9_]', '_', name) |
| 92 | return name |
| 93 | |
Mohannad Farrag | a0db68b | 2022-11-24 12:28:09 +0000 | [diff] [blame] | 94 | def _is_java_source(src): |
Motomu Utsumi | e5ea57b | 2023-01-06 11:30:33 +0900 | [diff] [blame] | 95 | return os.path.splitext(src)[1] == '.java' and not src.startswith("//out/") |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 96 | |
Mohannad Farrag | 6a2d88a | 2022-11-28 19:33:48 +0000 | [diff] [blame] | 97 | def is_java_action(script, outputs): |
| 98 | return (script != "" and script not in JAVA_BANNED_SCRIPTS) and any( |
| 99 | [file.endswith(".srcjar") or file.endswith(".java") |
| 100 | for file in outputs]) |
| 101 | |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 102 | class GnParser(object): |
| 103 | """A parser with some cleverness for GN json desc files |
| 104 | |
| 105 | The main goals of this parser are: |
| 106 | 1) Deal with the fact that other build systems don't have an equivalent |
| 107 | notion to GN's source_set. Conversely to Bazel's and Soong's filegroups, |
| 108 | GN source_sets expect that dependencies, cflags and other source_set |
| 109 | properties propagate up to the linker unit (static_library, executable or |
| 110 | shared_library). This parser simulates the same behavior: when a |
| 111 | source_set is encountered, some of its variables (cflags and such) are |
| 112 | copied up to the dependent targets. This is to allow gen_xxx to create |
| 113 | one filegroup for each source_set and then squash all the other flags |
| 114 | onto the linker unit. |
| 115 | 2) Detect and special-case protobuf targets, figuring out the protoc-plugin |
| 116 | being used. |
| 117 | """ |
| 118 | |
| 119 | class Target(object): |
| 120 | """Reperesents A GN target. |
| 121 | |
| 122 | Maked properties are propagated up the dependency chain when a |
| 123 | source_set dependency is encountered. |
| 124 | """ |
Patrick Rohr | 02ad51f | 2022-11-15 13:54:07 -0800 | [diff] [blame] | 125 | class Arch(): |
| 126 | """Architecture-dependent properties |
| 127 | """ |
| 128 | def __init__(self): |
| 129 | self.sources = set() |
Motomu Utsumi | 1c64e44 | 2022-11-17 21:51:37 +0900 | [diff] [blame] | 130 | self.cflags = set() |
Motomu Utsumi | 80e0447 | 2022-11-17 21:54:06 +0900 | [diff] [blame] | 131 | self.defines = set() |
Motomu Utsumi | 778f830 | 2022-11-17 22:12:17 +0900 | [diff] [blame] | 132 | self.include_dirs = set() |
Patrick Rohr | 297f979 | 2022-11-16 23:40:40 -0800 | [diff] [blame] | 133 | self.deps = set() |
| 134 | self.transitive_static_libs_deps = set() |
Mohannad Farrag | 7f29d83 | 2022-11-23 19:52:41 +0000 | [diff] [blame] | 135 | self.source_set_deps = set() |
Motomu Utsumi | 9a21955 | 2023-02-09 17:18:13 +0900 | [diff] [blame] | 136 | self.ldflags = set() |
Patrick Rohr | 02ad51f | 2022-11-15 13:54:07 -0800 | [diff] [blame] | 137 | |
Motomu Utsumi | 2165ccd | 2022-12-16 17:20:24 +0900 | [diff] [blame] | 138 | # These are valid only for type == 'action' |
| 139 | self.inputs = set() |
| 140 | self.outputs = set() |
| 141 | self.args = [] |
| 142 | self.script = '' |
| 143 | self.response_file_contents = '' |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 144 | |
| 145 | def __init__(self, name, type): |
| 146 | self.name = name # e.g. //src/ipc:ipc |
| 147 | |
| 148 | VALID_TYPES = ('static_library', 'shared_library', 'executable', 'group', |
Patrick Rohr | da778a0 | 2022-10-25 16:17:31 -0700 | [diff] [blame] | 149 | 'action', 'source_set', 'proto_library', 'copy', 'action_foreach') |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 150 | assert (type in VALID_TYPES) |
| 151 | self.type = type |
| 152 | self.testonly = False |
| 153 | self.toolchain = None |
| 154 | |
| 155 | # These are valid only for type == proto_library. |
| 156 | # This is typically: 'proto', 'protozero', 'ipc'. |
| 157 | self.proto_plugin = None |
| 158 | self.proto_paths = set() |
| 159 | self.proto_exports = set() |
Motomu Utsumi | d7e0e42 | 2022-11-08 17:49:52 +0900 | [diff] [blame] | 160 | self.proto_in_dir = "" |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 161 | |
| 162 | self.sources = set() |
| 163 | # TODO(primiano): consider whether the public section should be part of |
| 164 | # bubbled-up sources. |
| 165 | self.public_headers = set() # 'public' |
| 166 | |
| 167 | # These are valid only for type == 'action' |
| 168 | self.inputs = set() |
| 169 | self.outputs = set() |
Motomu Utsumi | 27bf596 | 2022-12-16 16:54:58 +0900 | [diff] [blame] | 170 | self.script = '' |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 171 | self.args = [] |
Motomu Utsumi | 27bf596 | 2022-12-16 16:54:58 +0900 | [diff] [blame] | 172 | self.response_file_contents = '' |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 173 | |
| 174 | # These variables are propagated up when encountering a dependency |
| 175 | # on a source_set target. |
| 176 | self.cflags = set() |
| 177 | self.defines = set() |
| 178 | self.deps = set() |
| 179 | self.libs = set() |
| 180 | self.include_dirs = set() |
| 181 | self.ldflags = set() |
| 182 | self.source_set_deps = set() # Transitive set of source_set deps. |
| 183 | self.proto_deps = set() |
| 184 | self.transitive_proto_deps = set() |
Mohannad Farrag | baf0d57 | 2022-11-22 11:53:54 +0000 | [diff] [blame] | 185 | self.rtti = False |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 186 | |
Patrick Rohr | 7091356 | 2022-11-15 21:49:28 -0800 | [diff] [blame] | 187 | # TODO: come up with a better way to only run this once. |
| 188 | # is_finalized tracks whether finalize() was called on this target. |
| 189 | self.is_finalized = False |
Patrick Rohr | 02ad51f | 2022-11-15 13:54:07 -0800 | [diff] [blame] | 190 | self.arch = dict() |
| 191 | |
Motomu Utsumi | ee47af6 | 2022-11-30 16:41:15 +0900 | [diff] [blame] | 192 | # This is used to get the name/version of libcronet |
| 193 | self.output_name = None |
| 194 | |
Patrick Rohr | c8f41cd | 2022-11-15 22:46:10 -0800 | [diff] [blame] | 195 | def host_supported(self): |
| 196 | return 'host' in self.arch |
| 197 | |
| 198 | def device_supported(self): |
| 199 | return any([name.startswith('android') for name in self.arch.keys()]) |
| 200 | |
Patrick Rohr | 94c2490 | 2022-12-14 19:44:44 -0800 | [diff] [blame] | 201 | def is_linker_unit_type(self): |
| 202 | return self.type in LINKER_UNIT_TYPES |
| 203 | |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 204 | def __lt__(self, other): |
| 205 | if isinstance(other, self.__class__): |
| 206 | return self.name < other.name |
| 207 | raise TypeError( |
| 208 | '\'<\' not supported between instances of \'%s\' and \'%s\'' % |
| 209 | (type(self).__name__, type(other).__name__)) |
| 210 | |
| 211 | def __repr__(self): |
| 212 | return json.dumps({ |
| 213 | k: (list(sorted(v)) if isinstance(v, set) else v) |
Patrick Rohr | 23f2619 | 2022-10-25 09:45:22 -0700 | [diff] [blame] | 214 | for (k, v) in self.__dict__.items() |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 215 | }, |
| 216 | indent=4, |
| 217 | sort_keys=True) |
| 218 | |
Motomu Utsumi | 1c64e44 | 2022-11-17 21:51:37 +0900 | [diff] [blame] | 219 | def update(self, other, arch): |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 220 | for key in ('cflags', 'defines', 'deps', 'include_dirs', 'ldflags', |
| 221 | 'source_set_deps', 'proto_deps', 'transitive_proto_deps', |
| 222 | 'libs', 'proto_paths'): |
| 223 | self.__dict__[key].update(other.__dict__.get(key, [])) |
| 224 | |
Motomu Utsumi | 9a21955 | 2023-02-09 17:18:13 +0900 | [diff] [blame] | 225 | for key_in_arch in ('cflags', 'defines', 'include_dirs', 'source_set_deps', 'ldflags'): |
Motomu Utsumi | 1c64e44 | 2022-11-17 21:51:37 +0900 | [diff] [blame] | 226 | self.arch[arch].__dict__[key_in_arch].update( |
| 227 | other.arch[arch].__dict__.get(key_in_arch, [])) |
| 228 | |
Motomu Utsumi | 2648df0 | 2022-12-16 15:05:25 +0900 | [diff] [blame] | 229 | def _finalize_set_attribute(self, key): |
| 230 | # Target contains the intersection of arch-dependent properties |
| 231 | getattr(self, key)\ |
| 232 | .update(set.intersection(*[getattr(arch, key) for arch in self.arch.values()])) |
| 233 | |
| 234 | # Deduplicate arch-dependent properties |
| 235 | for arch in self.arch.values(): |
| 236 | getattr(arch, key).difference_update(getattr(self, key)) |
| 237 | |
Motomu Utsumi | 5fde8e1 | 2022-12-16 16:59:46 +0900 | [diff] [blame] | 238 | def _finalize_non_set_attribute(self, key): |
| 239 | # Only when all the arch has the same non empty value, move the value to the target common |
| 240 | val = getattr(list(self.arch.values())[0], key) |
| 241 | if val and all([val == getattr(arch, key) for arch in self.arch.values()]): |
| 242 | setattr(self, key, copy.deepcopy(val)) |
| 243 | for arch in self.arch.values(): |
| 244 | getattr(arch, key, None) |
| 245 | |
Motomu Utsumi | 2648df0 | 2022-12-16 15:05:25 +0900 | [diff] [blame] | 246 | def _finalize_attribute(self, key): |
| 247 | val = getattr(self, key) |
| 248 | if isinstance(val, set): |
| 249 | self._finalize_set_attribute(key) |
Motomu Utsumi | 5fde8e1 | 2022-12-16 16:59:46 +0900 | [diff] [blame] | 250 | elif isinstance(val, (list, str)): |
| 251 | self._finalize_non_set_attribute(key) |
Motomu Utsumi | 2648df0 | 2022-12-16 15:05:25 +0900 | [diff] [blame] | 252 | else: |
| 253 | raise TypeError(f'Unsupported type: {type(val)}') |
| 254 | |
Patrick Rohr | 7091356 | 2022-11-15 21:49:28 -0800 | [diff] [blame] | 255 | def finalize(self): |
| 256 | """Move common properties out of arch-dependent subobjects to Target object. |
| 257 | |
| 258 | TODO: find a better name for this function. |
| 259 | """ |
| 260 | if self.is_finalized: |
| 261 | return |
| 262 | self.is_finalized = True |
| 263 | |
Motomu Utsumi | 5fde8e1 | 2022-12-16 16:59:46 +0900 | [diff] [blame] | 264 | if len(self.arch) == 0: |
| 265 | return |
| 266 | |
Motomu Utsumi | 2165ccd | 2022-12-16 17:20:24 +0900 | [diff] [blame] | 267 | for key in ('sources', 'cflags', 'defines', 'include_dirs', 'deps', 'source_set_deps', |
Motomu Utsumi | 9a21955 | 2023-02-09 17:18:13 +0900 | [diff] [blame] | 268 | 'inputs', 'outputs', 'args', 'script', 'response_file_contents', 'ldflags'): |
Motomu Utsumi | 2648df0 | 2022-12-16 15:05:25 +0900 | [diff] [blame] | 269 | self._finalize_attribute(key) |
Patrick Rohr | 297f979 | 2022-11-16 23:40:40 -0800 | [diff] [blame] | 270 | |
Mohannad Farrag | b5aa8ea | 2023-01-16 16:20:25 +0000 | [diff] [blame] | 271 | def get_target_name(self): |
| 272 | return self.name[self.name.find(":") + 1:] |
| 273 | |
Patrick Rohr | 7091356 | 2022-11-15 21:49:28 -0800 | [diff] [blame] | 274 | |
Patrick Rohr | 0913f0b | 2022-12-13 09:13:20 -0800 | [diff] [blame] | 275 | def __init__(self, builtin_deps): |
| 276 | self.builtin_deps = builtin_deps |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 277 | self.all_targets = {} |
| 278 | self.linker_units = {} # Executables, shared or static libraries. |
| 279 | self.source_sets = {} |
| 280 | self.actions = {} |
| 281 | self.proto_libs = {} |
Motomu Utsumi | ea729d3 | 2022-12-23 17:06:41 +0900 | [diff] [blame] | 282 | self.java_sources = collections.defaultdict(set) |
Mohannad Farrag | cdbfeb8 | 2023-01-16 13:35:20 +0000 | [diff] [blame] | 283 | self.aidl_local_include_dirs = set() |
Motomu Utsumi | ea729d3 | 2022-12-23 17:06:41 +0900 | [diff] [blame] | 284 | self.java_actions = collections.defaultdict(set) |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 285 | |
Patrick Rohr | 09716f5 | 2022-10-27 13:02:36 -0700 | [diff] [blame] | 286 | def _get_response_file_contents(self, action_desc): |
Patrick Rohr | c20887d | 2022-10-28 12:59:20 -0700 | [diff] [blame] | 287 | # response_file_contents are formatted as: |
| 288 | # ['--flags', '--flag=true && false'] and need to be formatted as: |
| 289 | # '--flags --flag=\"true && false\"' |
| 290 | flags = action_desc.get('response_file_contents', []) |
| 291 | formatted_flags = [] |
| 292 | for flag in flags: |
| 293 | if '=' in flag: |
| 294 | key, val = flag.split('=') |
| 295 | formatted_flags.append('%s=\\"%s\\"' % (key, val)) |
| 296 | else: |
| 297 | formatted_flags.append(flag) |
| 298 | |
| 299 | return ' '.join(formatted_flags) |
Patrick Rohr | 09716f5 | 2022-10-27 13:02:36 -0700 | [diff] [blame] | 300 | |
Patrick Rohr | df3c20c | 2022-12-12 20:19:26 -0800 | [diff] [blame] | 301 | def _is_java_group(self, type_, target_name): |
Patrick Rohr | af92fa6 | 2022-11-04 14:27:04 -0700 | [diff] [blame] | 302 | # Per https://chromium.googlesource.com/chromium/src/build/+/HEAD/android/docs/java_toolchain.md |
| 303 | # java target names must end in "_java". |
| 304 | # TODO: There are some other possible variations we might need to support. |
Patrick Rohr | 689a9c0 | 2022-12-13 14:55:27 -0800 | [diff] [blame] | 305 | return type_ == 'group' and target_name.endswith('_java') |
Patrick Rohr | af92fa6 | 2022-11-04 14:27:04 -0700 | [diff] [blame] | 306 | |
Patrick Rohr | 81a4ac3 | 2022-11-15 14:38:21 -0800 | [diff] [blame] | 307 | def _get_arch(self, toolchain): |
Patrick Rohr | d938d53 | 2022-11-15 22:17:08 -0800 | [diff] [blame] | 308 | if toolchain == '//build/toolchain/android:android_clang_x86': |
Patrick Rohr | 4eff210 | 2022-11-15 22:21:52 -0800 | [diff] [blame] | 309 | return 'android_x86' |
Patrick Rohr | 81a4ac3 | 2022-11-15 14:38:21 -0800 | [diff] [blame] | 310 | elif toolchain == '//build/toolchain/android:android_clang_x64': |
Patrick Rohr | 4eff210 | 2022-11-15 22:21:52 -0800 | [diff] [blame] | 311 | return 'android_x86_64' |
Patrick Rohr | 81a4ac3 | 2022-11-15 14:38:21 -0800 | [diff] [blame] | 312 | elif toolchain == '//build/toolchain/android:android_clang_arm': |
Patrick Rohr | 4eff210 | 2022-11-15 22:21:52 -0800 | [diff] [blame] | 313 | return 'android_arm' |
Patrick Rohr | 81a4ac3 | 2022-11-15 14:38:21 -0800 | [diff] [blame] | 314 | elif toolchain == '//build/toolchain/android:android_clang_arm64': |
Patrick Rohr | 4eff210 | 2022-11-15 22:21:52 -0800 | [diff] [blame] | 315 | return 'android_arm64' |
Patrick Rohr | 81a4ac3 | 2022-11-15 14:38:21 -0800 | [diff] [blame] | 316 | else: |
| 317 | return 'host' |
| 318 | |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 319 | def get_target(self, gn_target_name): |
| 320 | """Returns a Target object from the fully qualified GN target name. |
| 321 | |
Patrick Rohr | d0077b7 | 2022-11-15 12:43:26 -0800 | [diff] [blame] | 322 | get_target() requires that parse_gn_desc() has already been called. |
| 323 | """ |
Patrick Rohr | 7091356 | 2022-11-15 21:49:28 -0800 | [diff] [blame] | 324 | # Run this every time as parse_gn_desc can be called at any time. |
| 325 | for target in self.all_targets.values(): |
| 326 | target.finalize() |
| 327 | |
Patrick Rohr | 7705bdb | 2022-11-15 13:26:30 -0800 | [diff] [blame] | 328 | return self.all_targets[label_without_toolchain(gn_target_name)] |
Patrick Rohr | d0077b7 | 2022-11-15 12:43:26 -0800 | [diff] [blame] | 329 | |
Mohannad Farrag | fc05430 | 2023-01-17 10:48:23 +0000 | [diff] [blame] | 330 | def parse_gn_desc(self, gn_desc, gn_target_name, java_group_name=None, is_test_target=False): |
Patrick Rohr | d0077b7 | 2022-11-15 12:43:26 -0800 | [diff] [blame] | 331 | """Parses a gn desc tree and resolves all target dependencies. |
| 332 | |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 333 | It bubbles up variables from source_set dependencies as described in the |
| 334 | class-level comments. |
| 335 | """ |
Patrick Rohr | 7705bdb | 2022-11-15 13:26:30 -0800 | [diff] [blame] | 336 | # Use name without toolchain for targets to support targets built for |
| 337 | # multiple archs. |
| 338 | target_name = label_without_toolchain(gn_target_name) |
Patrick Rohr | 02ad51f | 2022-11-15 13:54:07 -0800 | [diff] [blame] | 339 | desc = gn_desc[gn_target_name] |
Patrick Rohr | 9860068 | 2022-11-18 18:29:15 -0800 | [diff] [blame] | 340 | type_ = desc['type'] |
Patrick Rohr | d938d53 | 2022-11-15 22:17:08 -0800 | [diff] [blame] | 341 | arch = self._get_arch(desc['toolchain']) |
Patrick Rohr | 9860068 | 2022-11-18 18:29:15 -0800 | [diff] [blame] | 342 | |
Motomu Utsumi | 940cc56 | 2022-12-23 16:44:27 +0900 | [diff] [blame] | 343 | if self._is_java_group(type_, target_name): |
| 344 | java_group_name = target_name |
Patrick Rohr | 0c7ef52 | 2022-12-12 20:29:19 -0800 | [diff] [blame] | 345 | |
Mohannad Farrag | edb2fd0 | 2023-02-10 14:53:41 +0000 | [diff] [blame] | 346 | if is_test_target: |
| 347 | target_name += TESTING_SUFFIX |
| 348 | |
Patrick Rohr | 9860068 | 2022-11-18 18:29:15 -0800 | [diff] [blame] | 349 | target = self.all_targets.get(target_name) |
Patrick Rohr | 02ad51f | 2022-11-15 13:54:07 -0800 | [diff] [blame] | 350 | if target is None: |
Patrick Rohr | 9860068 | 2022-11-18 18:29:15 -0800 | [diff] [blame] | 351 | target = GnParser.Target(target_name, type_) |
Patrick Rohr | 02ad51f | 2022-11-15 13:54:07 -0800 | [diff] [blame] | 352 | self.all_targets[target_name] = target |
| 353 | |
| 354 | if arch not in target.arch: |
| 355 | target.arch[arch] = GnParser.Target.Arch() |
| 356 | else: |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 357 | return target # Target already processed. |
| 358 | |
Patrick Rohr | 0913f0b | 2022-12-13 09:13:20 -0800 | [diff] [blame] | 359 | if target.name in self.builtin_deps: |
Patrick Rohr | 6cd0f25 | 2022-12-15 10:37:55 -0800 | [diff] [blame] | 360 | # return early, no need to parse any further as the module is a builtin. |
| 361 | return target |
Patrick Rohr | 0913f0b | 2022-12-13 09:13:20 -0800 | [diff] [blame] | 362 | |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 363 | target.testonly = desc.get('testonly', False) |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 364 | |
Patrick Rohr | 0d40da3 | 2022-11-15 13:08:12 -0800 | [diff] [blame] | 365 | proto_target_type, proto_desc = self.get_proto_target_type(gn_desc, gn_target_name) |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 366 | if proto_target_type is not None: |
| 367 | self.proto_libs[target.name] = target |
| 368 | target.type = 'proto_library' |
| 369 | target.proto_plugin = proto_target_type |
| 370 | target.proto_paths.update(self.get_proto_paths(proto_desc)) |
| 371 | target.proto_exports.update(self.get_proto_exports(proto_desc)) |
Motomu Utsumi | d7e0e42 | 2022-11-08 17:49:52 +0900 | [diff] [blame] | 372 | target.proto_in_dir = self.get_proto_in_dir(proto_desc) |
Patrick Rohr | ad7a29c | 2022-11-16 21:48:09 -0800 | [diff] [blame] | 373 | for gn_proto_deps_name in proto_desc.get('deps', []): |
| 374 | dep = self.parse_gn_desc(gn_desc, gn_proto_deps_name) |
| 375 | target.deps.add(dep.name) |
Patrick Rohr | 53dcd10 | 2022-11-15 21:53:02 -0800 | [diff] [blame] | 376 | target.arch[arch].sources.update(proto_desc.get('sources', [])) |
| 377 | assert (all(x.endswith('.proto') for x in target.arch[arch].sources)) |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 378 | elif target.type == 'source_set': |
| 379 | self.source_sets[gn_target_name] = target |
Patrick Rohr | 53dcd10 | 2022-11-15 21:53:02 -0800 | [diff] [blame] | 380 | target.arch[arch].sources.update(desc.get('sources', [])) |
Patrick Rohr | 94c2490 | 2022-12-14 19:44:44 -0800 | [diff] [blame] | 381 | elif target.is_linker_unit_type(): |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 382 | self.linker_units[gn_target_name] = target |
Patrick Rohr | 53dcd10 | 2022-11-15 21:53:02 -0800 | [diff] [blame] | 383 | target.arch[arch].sources.update(desc.get('sources', [])) |
Patrick Rohr | df3c20c | 2022-12-12 20:19:26 -0800 | [diff] [blame] | 384 | elif (desc.get("script", "") in JAVA_BANNED_SCRIPTS |
| 385 | or self._is_java_group(target.type, target.name)): |
Mohannad Farrag | 6a2d88a | 2022-11-28 19:33:48 +0000 | [diff] [blame] | 386 | # java_group identifies the group target generated by the android_library |
Patrick Rohr | e71a061 | 2022-12-13 14:58:14 -0800 | [diff] [blame] | 387 | # or java_library template. A java_group must not be added as a |
| 388 | # dependency, but sources are collected. |
Mohannad Farrag | 6a2d88a | 2022-11-28 19:33:48 +0000 | [diff] [blame] | 389 | log.debug('Found java target %s', target.name) |
| 390 | if target.type == "action": |
| 391 | # Convert java actions into java_group and keep the inputs for collection. |
| 392 | target.inputs.update(desc.get('inputs', [])) |
| 393 | target.type = 'java_group' |
Patrick Rohr | da778a0 | 2022-10-25 16:17:31 -0700 | [diff] [blame] | 394 | elif target.type in ['action', 'action_foreach']: |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 395 | self.actions[gn_target_name] = target |
Motomu Utsumi | 3029ac9 | 2022-12-16 18:04:57 +0900 | [diff] [blame] | 396 | target.arch[arch].inputs.update(desc.get('inputs', [])) |
Patrick Rohr | 53dcd10 | 2022-11-15 21:53:02 -0800 | [diff] [blame] | 397 | target.arch[arch].sources.update(desc.get('sources', [])) |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 398 | outs = [re.sub('^//out/.+?/gen/', '', x) for x in desc['outputs']] |
Motomu Utsumi | 3029ac9 | 2022-12-16 18:04:57 +0900 | [diff] [blame] | 399 | target.arch[arch].outputs.update(outs) |
| 400 | target.arch[arch].script = desc['script'] |
| 401 | target.arch[arch].args = desc['args'] |
| 402 | target.arch[arch].response_file_contents = self._get_response_file_contents(desc) |
Patrick Rohr | da778a0 | 2022-10-25 16:17:31 -0700 | [diff] [blame] | 403 | elif target.type == 'copy': |
| 404 | # TODO: copy rules are not currently implemented. |
| 405 | self.actions[gn_target_name] = target |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 406 | |
| 407 | # Default for 'public' is //* - all headers in 'sources' are public. |
| 408 | # TODO(primiano): if a 'public' section is specified (even if empty), then |
| 409 | # the rest of 'sources' is considered inaccessible by gn. Consider |
| 410 | # emulating that, so that generated build files don't end up with overly |
| 411 | # accessible headers. |
| 412 | public_headers = [x for x in desc.get('public', []) if x != '*'] |
| 413 | target.public_headers.update(public_headers) |
| 414 | |
Motomu Utsumi | 1c64e44 | 2022-11-17 21:51:37 +0900 | [diff] [blame] | 415 | target.arch[arch].cflags.update(desc.get('cflags', []) + desc.get('cflags_cc', [])) |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 416 | target.libs.update(desc.get('libs', [])) |
Motomu Utsumi | 9a21955 | 2023-02-09 17:18:13 +0900 | [diff] [blame] | 417 | target.arch[arch].ldflags.update(desc.get('ldflags', [])) |
Motomu Utsumi | 80e0447 | 2022-11-17 21:54:06 +0900 | [diff] [blame] | 418 | target.arch[arch].defines.update(desc.get('defines', [])) |
Motomu Utsumi | 778f830 | 2022-11-17 22:12:17 +0900 | [diff] [blame] | 419 | target.arch[arch].include_dirs.update(desc.get('include_dirs', [])) |
Motomu Utsumi | ee47af6 | 2022-11-30 16:41:15 +0900 | [diff] [blame] | 420 | target.output_name = desc.get('output_name', None) |
Mohannad Farrag | baf0d57 | 2022-11-22 11:53:54 +0000 | [diff] [blame] | 421 | if "-frtti" in target.arch[arch].cflags: |
| 422 | target.rtti = True |
Mohannad Farrag | 7f29d83 | 2022-11-23 19:52:41 +0000 | [diff] [blame] | 423 | |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 424 | # Recurse in dependencies. |
Patrick Rohr | 7f4631e | 2022-11-15 14:35:03 -0800 | [diff] [blame] | 425 | for gn_dep_name in desc.get('deps', []): |
Mohannad Farrag | fc05430 | 2023-01-17 10:48:23 +0000 | [diff] [blame] | 426 | dep = self.parse_gn_desc(gn_desc, gn_dep_name, java_group_name, is_test_target) |
Patrick Rohr | 6cd0f25 | 2022-12-15 10:37:55 -0800 | [diff] [blame] | 427 | if dep.type == 'proto_library': |
Patrick Rohr | 9006b36 | 2022-11-16 21:49:53 -0800 | [diff] [blame] | 428 | target.proto_deps.add(dep.name) |
| 429 | target.transitive_proto_deps.add(dep.name) |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 430 | target.proto_paths.update(dep.proto_paths) |
| 431 | target.transitive_proto_deps.update(dep.transitive_proto_deps) |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 432 | elif dep.type == 'group': |
Motomu Utsumi | 1c64e44 | 2022-11-17 21:51:37 +0900 | [diff] [blame] | 433 | target.update(dep, arch) # Bubble up groups's cflags/ldflags etc. |
Patrick Rohr | da778a0 | 2022-10-25 16:17:31 -0700 | [diff] [blame] | 434 | elif dep.type in ['action', 'action_foreach', 'copy']: |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 435 | if proto_target_type is None: |
Mohannad Farrag | 1de6cb1 | 2022-11-28 12:27:26 +0000 | [diff] [blame] | 436 | target.arch[arch].deps.add(dep.name) |
Patrick Rohr | 94c2490 | 2022-12-14 19:44:44 -0800 | [diff] [blame] | 437 | elif dep.is_linker_unit_type(): |
Patrick Rohr | 297f979 | 2022-11-16 23:40:40 -0800 | [diff] [blame] | 438 | target.arch[arch].deps.add(dep.name) |
Patrick Rohr | 3624f95 | 2022-11-04 14:30:18 -0700 | [diff] [blame] | 439 | elif dep.type == 'java_group': |
| 440 | # Explicitly break dependency chain when a java_group is added. |
| 441 | # Java sources are collected and eventually compiled as one large |
| 442 | # java_library. |
| 443 | pass |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 444 | |
Mohannad Farrag | 379d79d | 2023-02-22 15:43:00 +0000 | [diff] [blame^] | 445 | if dep.type in ['static_library', 'source_set']: |
| 446 | # Bubble up static_libs and source_set. Necessary, since soong does not propagate |
Patrick Rohr | 5de9f2e | 2022-11-11 15:33:20 -0800 | [diff] [blame] | 447 | # static_libs up the build tree. |
Mohannad Farrag | 379d79d | 2023-02-22 15:43:00 +0000 | [diff] [blame^] | 448 | # Source sets are later translated to static_libraries, so it makes sense |
| 449 | # to reuse transitive_static_libs_deps. |
Patrick Rohr | 297f979 | 2022-11-16 23:40:40 -0800 | [diff] [blame] | 450 | target.arch[arch].transitive_static_libs_deps.add(dep.name) |
Patrick Rohr | a9c1dda | 2022-11-14 19:02:40 -0800 | [diff] [blame] | 451 | |
Patrick Rohr | 297f979 | 2022-11-16 23:40:40 -0800 | [diff] [blame] | 452 | if arch in dep.arch: |
| 453 | target.arch[arch].transitive_static_libs_deps.update( |
| 454 | dep.arch[arch].transitive_static_libs_deps) |
| 455 | target.arch[arch].deps.update(target.arch[arch].transitive_static_libs_deps) |
Patrick Rohr | 5de9f2e | 2022-11-11 15:33:20 -0800 | [diff] [blame] | 456 | |
Patrick Rohr | b27587e | 2022-11-04 14:57:24 -0700 | [diff] [blame] | 457 | # Collect java sources. Java sources are kept inside the __compile_java target. |
| 458 | # This target can be used for both host and target compilation; only add |
| 459 | # the sources if they are destined for the target (i.e. they are a |
| 460 | # dependency of the __dex target) |
| 461 | # Note: this skips prebuilt java dependencies. These will have to be |
| 462 | # added manually when building the jar. |
Patrick Rohr | 0c7ef52 | 2022-12-12 20:29:19 -0800 | [diff] [blame] | 463 | if target.name.endswith('__dex'): |
| 464 | if dep.name.endswith('__compile_java'): |
Patrick Rohr | b27587e | 2022-11-04 14:57:24 -0700 | [diff] [blame] | 465 | log.debug('Adding java sources for %s', dep.name) |
Mohannad Farrag | a0db68b | 2022-11-24 12:28:09 +0000 | [diff] [blame] | 466 | java_srcs = [src for src in dep.inputs if _is_java_source(src)] |
Mohannad Farrag | fc05430 | 2023-01-17 10:48:23 +0000 | [diff] [blame] | 467 | if not is_test_target: |
| 468 | # TODO(aymanm): Fix collecting sources for testing modules for java. |
| 469 | # Don't collect java source files for test targets. |
| 470 | # We only need a specific set of java sources which are hardcoded in gen_android_bp |
| 471 | self.java_sources[java_group_name].update(java_srcs) |
Mohannad Farrag | 6a2d88a | 2022-11-28 19:33:48 +0000 | [diff] [blame] | 472 | if dep.type in ["action"] and target.type == "java_group": |
Mohannad Farrag | cdbfeb8 | 2023-01-16 13:35:20 +0000 | [diff] [blame] | 473 | # GN uses an action to compile aidl files. However, this is not needed in soong |
| 474 | # as soong can directly have .aidl files in srcs. So adding .aidl files to the java_sources. |
Motomu Utsumi | 6e51412 | 2022-12-05 17:51:40 +0900 | [diff] [blame] | 475 | # TODO: Find a better way/place to do this. |
Mohannad Farrag | cdbfeb8 | 2023-01-16 13:35:20 +0000 | [diff] [blame] | 476 | if '_aidl' in dep.name: |
Motomu Utsumi | ea729d3 | 2022-12-23 17:06:41 +0900 | [diff] [blame] | 477 | self.java_sources[java_group_name].update(dep.arch[arch].sources) |
Mohannad Farrag | cdbfeb8 | 2023-01-16 13:35:20 +0000 | [diff] [blame] | 478 | self.aidl_local_include_dirs.update(_extract_includes_from_aidl_args(dep.arch[arch].args)) |
Motomu Utsumi | 6e51412 | 2022-12-05 17:51:40 +0900 | [diff] [blame] | 479 | else: |
Mohannad Farrag | fc05430 | 2023-01-17 10:48:23 +0000 | [diff] [blame] | 480 | if not is_test_target: |
| 481 | # TODO(aymanm): Fix collecting actions for testing modules for java. |
| 482 | # Don't collect java actions for test targets. |
| 483 | self.java_actions[java_group_name].add(dep.name) |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 484 | return target |
| 485 | |
| 486 | def get_proto_exports(self, proto_desc): |
| 487 | # exports in metadata will be available for source_set targets. |
| 488 | metadata = proto_desc.get('metadata', {}) |
| 489 | return metadata.get('exports', []) |
| 490 | |
| 491 | def get_proto_paths(self, proto_desc): |
| 492 | # import_dirs in metadata will be available for source_set targets. |
| 493 | metadata = proto_desc.get('metadata', {}) |
| 494 | return metadata.get('import_dirs', []) |
| 495 | |
Motomu Utsumi | d7e0e42 | 2022-11-08 17:49:52 +0900 | [diff] [blame] | 496 | |
| 497 | def get_proto_in_dir(self, proto_desc): |
| 498 | args = proto_desc.get('args') |
| 499 | return re.sub('^\.\./\.\./', '', args[args.index('--proto-in-dir') + 1]) |
| 500 | |
Patrick Rohr | 0d40da3 | 2022-11-15 13:08:12 -0800 | [diff] [blame] | 501 | def get_proto_target_type(self, gn_desc, gn_target_name): |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 502 | """ Checks if the target is a proto library and return the plugin. |
| 503 | |
| 504 | Returns: |
| 505 | (None, None): if the target is not a proto library. |
| 506 | (plugin, proto_desc) where |plugin| is 'proto' in the default (lite) |
| 507 | case or 'protozero' or 'ipc' or 'descriptor'; |proto_desc| is the GN |
| 508 | json desc of the target with the .proto sources (_gen target for |
| 509 | non-descriptor types or the target itself for descriptor type). |
| 510 | """ |
Patrick Rohr | 0d40da3 | 2022-11-15 13:08:12 -0800 | [diff] [blame] | 511 | parts = gn_target_name.split('(', 1) |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 512 | name = parts[0] |
| 513 | toolchain = '(' + parts[1] if len(parts) > 1 else '' |
| 514 | |
| 515 | # Descriptor targets don't have a _gen target; instead we look for the |
| 516 | # characteristic flag in the args of the target itself. |
Patrick Rohr | 0d40da3 | 2022-11-15 13:08:12 -0800 | [diff] [blame] | 517 | desc = gn_desc.get(gn_target_name) |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 518 | if '--descriptor_set_out' in desc.get('args', []): |
| 519 | return 'descriptor', desc |
| 520 | |
| 521 | # Source set proto targets have a non-empty proto_library_sources in the |
| 522 | # metadata of the description. |
| 523 | metadata = desc.get('metadata', {}) |
| 524 | if 'proto_library_sources' in metadata: |
| 525 | return 'source_set', desc |
| 526 | |
| 527 | # In all other cases, we want to look at the _gen target as that has the |
| 528 | # important information. |
Patrick Rohr | 564d6be | 2022-11-15 12:57:57 -0800 | [diff] [blame] | 529 | gen_desc = gn_desc.get('%s_gen%s' % (name, toolchain)) |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 530 | if gen_desc is None or gen_desc['type'] != 'action': |
| 531 | return None, None |
Patrick Rohr | c598078 | 2022-11-07 16:34:03 -0800 | [diff] [blame] | 532 | if gen_desc['script'] != '//tools/protoc_wrapper/protoc_wrapper.py': |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 533 | return None, None |
| 534 | plugin = 'proto' |
Patrick Rohr | c598078 | 2022-11-07 16:34:03 -0800 | [diff] [blame] | 535 | args = gen_desc.get('args', []) |
Patrick Rohr | 92d7412 | 2022-10-21 15:50:52 -0700 | [diff] [blame] | 536 | for arg in (arg for arg in args if arg.startswith('--plugin=')): |
| 537 | # |arg| at this point looks like: |
| 538 | # --plugin=protoc-gen-plugin=gcc_like_host/protozero_plugin |
| 539 | # or |
| 540 | # --plugin=protoc-gen-plugin=protozero_plugin |
| 541 | plugin = arg.split('=')[-1].split('/')[-1].replace('_plugin', '') |
| 542 | return plugin, gen_desc |