blob: 2d57628ea242ee1545678e4f0f1c24c5bab2458f [file] [log] [blame]
Sami Kyostila865d1d32017-12-12 18:37:04 +00001#!/usr/bin/env python
2# Copyright (C) 2017 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16# This tool translates a collection of BUILD.gn files into a mostly equivalent
17# Android.bp file for the Android Soong build system. The input to the tool is a
18# JSON description of the GN build definition generated with the following
19# command:
20#
21# gn desc out --format=json --all-toolchains "//*" > desc.json
22#
23# The tool is then given a list of GN labels for which to generate Android.bp
24# build rules. The dependencies for the GN labels are squashed to the generated
25# Android.bp target, except for actions which get their own genrule. Some
26# libraries are also mapped to their Android equivalents -- see |builtin_deps|.
27
28import argparse
29import json
30import os
31import re
32import sys
33
Sami Kyostila3c88a1d2019-05-22 18:29:42 +010034import gn_utils
35
Sami Kyostilab27619f2017-12-13 19:22:16 +000036# Default targets to translate to the blueprint file.
Primiano Tucci4e49c022017-12-21 18:22:44 +010037default_targets = [
Primiano Tuccibdb2a592018-10-11 15:59:29 +010038 '//:libperfetto',
Primiano Tucci0b651b82019-06-03 17:16:23 +010039 '//:libperfetto_client_experimental',
Primiano Tucci676f0cc2018-12-03 20:03:26 +010040 '//:libperfetto_android_internal',
Lalit Maganti79f2d7b2018-01-23 18:27:33 +000041 '//:perfetto_integrationtests',
Primiano Tucci6aa75572018-03-21 05:33:14 -070042 '//:perfetto_trace_protos',
Lalit Maganti79f2d7b2018-01-23 18:27:33 +000043 '//:perfetto_unittests',
Primiano Tucci3b729102018-01-08 18:16:36 +000044 '//:perfetto',
Lalit Magantiedace412019-06-18 13:28:28 +010045 '//:trace_processor_shell',
Primiano Tucci4e49c022017-12-21 18:22:44 +010046 '//:traced',
Primiano Tucci6067e732018-01-08 16:19:40 +000047 '//:traced_probes',
Primiano Tucci21c19d82018-03-29 12:35:08 +010048 '//:trace_to_text',
Florian Mayerb6a921f2018-10-18 18:55:23 +010049 '//:heapprofd_client',
50 '//:heapprofd',
Hector Dearman696ff772019-04-23 18:38:53 +010051 '//:trigger_perfetto',
Primiano Tucci4e49c022017-12-21 18:22:44 +010052]
Sami Kyostilab27619f2017-12-13 19:22:16 +000053
Lalit Magantiedace412019-06-18 13:28:28 +010054# Targets which are testonly but should still be a cc_binary.
55non_test_binaries = [
56 '//:trace_processor_shell',
57]
58
Primiano Tucci6067e732018-01-08 16:19:40 +000059# Defines a custom init_rc argument to be applied to the corresponding output
60# blueprint target.
Primiano Tucci5a304532018-01-09 14:15:43 +000061target_initrc = {
62 '//:traced': 'perfetto.rc',
Florian Mayer7a5d83b2018-10-18 18:57:02 +010063 '//:heapprofd': 'heapprofd.rc',
Primiano Tucci5a304532018-01-09 14:15:43 +000064}
Primiano Tucci6067e732018-01-08 16:19:40 +000065
Primiano Tucci6aa75572018-03-21 05:33:14 -070066target_host_supported = [
67 '//:perfetto_trace_protos',
68]
69
Primiano Tucci21c19d82018-03-29 12:35:08 +010070target_host_only = [
71 '//:trace_to_text',
72]
73
Sami Kyostilab27619f2017-12-13 19:22:16 +000074# Arguments for the GN output directory.
Primiano Tucci9c411652019-08-27 07:13:59 +020075gn_args = ' '.join([
76 'is_debug=false',
Primiano Tucci7e05fc12019-08-27 17:29:47 +020077 'is_perfetto_build_generator=true',
Primiano Tucci9c411652019-08-27 07:13:59 +020078 'perfetto_build_with_android=true',
79 'target_cpu="arm"',
80 'target_os="android"',
81])
Sami Kyostilab27619f2017-12-13 19:22:16 +000082
Sami Kyostila865d1d32017-12-12 18:37:04 +000083# All module names are prefixed with this string to avoid collisions.
84module_prefix = 'perfetto_'
85
86# Shared libraries which are directly translated to Android system equivalents.
87library_whitelist = [
Lalit Maganti8eba3092019-03-27 13:25:29 +000088 "android.hardware.atrace@1.0",
Primiano Tucci676f0cc2018-12-03 20:03:26 +010089 'android.hardware.health@2.0',
Esteban Talavera1fecac92019-01-09 16:06:29 +000090 "android.hardware.power.stats@1.0",
Sami Kyostila865d1d32017-12-12 18:37:04 +000091 'android',
Primiano Tucci676f0cc2018-12-03 20:03:26 +010092 'base',
Sami Kyostilab5b71692018-01-12 12:16:44 +000093 'binder',
Primiano Tucci676f0cc2018-12-03 20:03:26 +010094 'hidlbase',
95 'hidltransport',
96 'hwbinder',
Ryan Savitski53ca60b2019-06-03 13:04:40 +010097 'incident',
Sami Kyostila865d1d32017-12-12 18:37:04 +000098 'log',
Sami Kyostilab5b71692018-01-12 12:16:44 +000099 'services',
Primiano Tucciedf099c2018-01-08 18:27:56 +0000100 'utils',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000101]
102
103# Name of the module which settings such as compiler flags for all other
104# modules.
105defaults_module = module_prefix + 'defaults'
106
107# Location of the project in the Android source tree.
108tree_path = 'external/perfetto'
109
Primiano Tucciedf099c2018-01-08 18:27:56 +0000110# Compiler flags which are passed through to the blueprint.
111cflag_whitelist = r'^-DPERFETTO.*$'
112
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000113# Compiler defines which are passed through to the blueprint.
Hector Dearman554627f2019-06-04 17:58:22 +0100114define_whitelist = r'^(GOOGLE_PROTO.*)|(PERFETTO_BUILD_WITH_ANDROID)|(ZLIB_.*)|(USE_MMAP)|(HAVE_HIDDEN)$'
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000115
Logan Chien9bfaaf92018-02-13 18:49:24 +0800116# Shared libraries which are not in PDK.
117library_not_in_pdk = {
118 'libandroid',
119 'libservices',
120}
121
Florian Mayerb6a921f2018-10-18 18:55:23 +0100122# Additional arguments to apply to Android.bp rules.
123additional_args = {
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100124 'heapprofd_client': [
125 ('include_dirs', ['bionic/libc']),
Ryan Savitskie65beca2019-01-29 18:29:13 +0000126 ('static_libs', ['libasync_safe']),
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100127 ],
128 'traced_probes': [
Hector Dearman696ff772019-04-23 18:38:53 +0100129 ('required', ['libperfetto_android_internal', 'trigger_perfetto']),
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100130 ],
131 'libperfetto_android_internal': [
132 ('static_libs', ['libhealthhalutils']),
Florian Mayerb6a921f2018-10-18 18:55:23 +0100133 ],
134}
135
Sami Kyostila865d1d32017-12-12 18:37:04 +0000136
137def enable_gmock(module):
138 module.static_libs.append('libgmock')
139
140
Hector Dearman3e712a02017-12-19 16:39:59 +0000141def enable_gtest_prod(module):
142 module.static_libs.append('libgtest_prod')
143
144
Sami Kyostila865d1d32017-12-12 18:37:04 +0000145def enable_gtest(module):
146 assert module.type == 'cc_test'
147
148
149def enable_protobuf_full(module):
150 module.shared_libs.append('libprotobuf-cpp-full')
151
152
153def enable_protobuf_lite(module):
154 module.shared_libs.append('libprotobuf-cpp-lite')
155
156
157def enable_protoc_lib(module):
158 module.shared_libs.append('libprotoc')
159
Florian Mayera2fae262018-08-31 12:10:01 -0700160def enable_libunwindstack(module):
161 module.shared_libs.append('libunwindstack')
162 module.shared_libs.append('libprocinfo')
163 module.shared_libs.append('libbase')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000164
165def enable_libunwind(module):
Sami Kyostilafc074d42017-12-15 10:33:42 +0000166 # libunwind is disabled on Darwin so we cannot depend on it.
167 pass
Sami Kyostila865d1d32017-12-12 18:37:04 +0000168
Lalit Maganti17aa2732019-02-08 15:47:26 +0000169def enable_sqlite(module):
Lalit Magantiedace412019-06-18 13:28:28 +0100170 if module.type == 'cc_binary_host':
171 module.static_libs.append('libsqlite')
172 else:
173 # Copy what the sqlite3 command line tool does.
174 module.android.shared_libs.append('libsqlite')
175 module.android.shared_libs.append('libandroidicu')
176 module.android.shared_libs.append('liblog')
177 module.android.shared_libs.append('libutils')
178 module.host.static_libs.append('libsqlite')
Lalit Maganti17aa2732019-02-08 15:47:26 +0000179
Hector Dearmane0b993f2019-05-24 18:48:16 +0100180def enable_zlib(module):
181 module.shared_libs.append('libz')
182
Sami Kyostila865d1d32017-12-12 18:37:04 +0000183# Android equivalents for third-party libraries that the upstream project
184# depends on.
185builtin_deps = {
186 '//buildtools:gmock': enable_gmock,
187 '//buildtools:gtest': enable_gtest,
188 '//buildtools:gtest_main': enable_gtest,
189 '//buildtools:libunwind': enable_libunwind,
190 '//buildtools:protobuf_full': enable_protobuf_full,
191 '//buildtools:protobuf_lite': enable_protobuf_lite,
192 '//buildtools:protoc_lib': enable_protoc_lib,
Florian Mayera2fae262018-08-31 12:10:01 -0700193 '//buildtools:libunwindstack': enable_libunwindstack,
Lalit Maganti17aa2732019-02-08 15:47:26 +0000194 '//buildtools:sqlite': enable_sqlite,
Hector Dearmane0b993f2019-05-24 18:48:16 +0100195 '//buildtools:zlib': enable_zlib,
Sami Kyostila865d1d32017-12-12 18:37:04 +0000196}
197
198# ----------------------------------------------------------------------------
199# End of configuration.
200# ----------------------------------------------------------------------------
201
202
203class Error(Exception):
204 pass
205
206
207class ThrowingArgumentParser(argparse.ArgumentParser):
208 def __init__(self, context):
209 super(ThrowingArgumentParser, self).__init__()
210 self.context = context
211
212 def error(self, message):
213 raise Error('%s: %s' % (self.context, message))
214
215
Lalit Magantiedace412019-06-18 13:28:28 +0100216def write_blueprint_key_value(output, name, value, sort=True):
217 """Writes an Blueprint key-value pair to the output"""
218
219 if not value:
220 return
221 if isinstance(value, set):
222 value = sorted(value)
223 if isinstance(value, list):
224 output.append(' %s: [' % name)
225 for item in sorted(value) if sort else value:
226 output.append(' "%s",' % item)
227 output.append(' ],')
228 return
229 if isinstance(value, bool):
230 output.append(' %s: true,' % name)
231 return
232 if isinstance(value, Target):
233 value.to_string(output)
234 return
235 output.append(' %s: "%s",' % (name, value))
236
237class Target(object):
238 """A target-scoped part of a module"""
239
240 def __init__(self, name):
241 self.name = name
242 self.shared_libs = []
243 self.static_libs = []
244 self.cflags = set()
245
246 def to_string(self, output):
247 nested_out = []
248 self._output_field(nested_out, 'shared_libs')
249 self._output_field(nested_out, 'static_libs')
250 self._output_field(nested_out, 'cflags')
251
252 if nested_out:
253 output.append(' %s: {' % self.name)
254 for line in nested_out:
255 output.append(' %s' % line)
256 output.append(' },')
257
258 def _output_field(self, output, name, sort=True):
259 value = getattr(self, name)
260 return write_blueprint_key_value(output, name, value, sort)
261
Sami Kyostila865d1d32017-12-12 18:37:04 +0000262class Module(object):
263 """A single module (e.g., cc_binary, cc_test) in a blueprint."""
264
265 def __init__(self, mod_type, name):
266 self.type = mod_type
267 self.name = name
268 self.srcs = []
269 self.comment = None
270 self.shared_libs = []
271 self.static_libs = []
272 self.tools = []
273 self.cmd = None
Primiano Tucci6aa75572018-03-21 05:33:14 -0700274 self.host_supported = False
Primiano Tucci6067e732018-01-08 16:19:40 +0000275 self.init_rc = []
Sami Kyostila865d1d32017-12-12 18:37:04 +0000276 self.out = []
277 self.export_include_dirs = []
278 self.generated_headers = []
Lalit Magantic5bcd792018-01-12 18:38:11 +0000279 self.export_generated_headers = []
Sami Kyostila865d1d32017-12-12 18:37:04 +0000280 self.defaults = []
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000281 self.cflags = set()
Sami Kyostila865d1d32017-12-12 18:37:04 +0000282 self.local_include_dirs = []
Ryan Savitskie65beca2019-01-29 18:29:13 +0000283 self.include_dirs = []
284 self.required = []
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100285 self.user_debug_flag = False
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100286 self.tool_files = None
Lalit Magantiedace412019-06-18 13:28:28 +0100287 self.android = Target('android')
288 self.host = Target('host')
Florian Mayer19f734f2019-07-05 12:08:01 +0100289 self.lto = None
Sami Kyostila865d1d32017-12-12 18:37:04 +0000290
291 def to_string(self, output):
292 if self.comment:
293 output.append('// %s' % self.comment)
294 output.append('%s {' % self.type)
295 self._output_field(output, 'name')
296 self._output_field(output, 'srcs')
297 self._output_field(output, 'shared_libs')
298 self._output_field(output, 'static_libs')
299 self._output_field(output, 'tools')
300 self._output_field(output, 'cmd', sort=False)
Primiano Tucci6aa75572018-03-21 05:33:14 -0700301 self._output_field(output, 'host_supported')
Primiano Tucci6067e732018-01-08 16:19:40 +0000302 self._output_field(output, 'init_rc')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000303 self._output_field(output, 'out')
304 self._output_field(output, 'export_include_dirs')
305 self._output_field(output, 'generated_headers')
Lalit Magantic5bcd792018-01-12 18:38:11 +0000306 self._output_field(output, 'export_generated_headers')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000307 self._output_field(output, 'defaults')
308 self._output_field(output, 'cflags')
309 self._output_field(output, 'local_include_dirs')
Ryan Savitskie65beca2019-01-29 18:29:13 +0000310 self._output_field(output, 'include_dirs')
311 self._output_field(output, 'required')
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100312 self._output_field(output, 'tool_files')
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100313
Lalit Magantiedace412019-06-18 13:28:28 +0100314 target_out = []
315 self._output_field(target_out, 'android')
316 self._output_field(target_out, 'host')
317 if target_out:
318 output.append(' target: {')
319 for line in target_out:
320 output.append(' %s' % line)
321 output.append(' },')
322
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100323 disable_pdk = any(name in library_not_in_pdk for name in self.shared_libs)
324 if self.user_debug_flag or disable_pdk:
Logan Chien9bfaaf92018-02-13 18:49:24 +0800325 output.append(' product_variables: {')
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100326 if disable_pdk:
327 output.append(' pdk: {')
328 output.append(' enabled: false,')
329 output.append(' },')
330 if self.user_debug_flag:
331 output.append(' debuggable: {')
332 output.append(' cflags: ["-DPERFETTO_BUILD_WITH_ANDROID_USERDEBUG"],')
333 output.append(' },')
Logan Chien9bfaaf92018-02-13 18:49:24 +0800334 output.append(' },')
Florian Mayer19f734f2019-07-05 12:08:01 +0100335 if self.lto is not None:
336 output.append(' target: {')
337 output.append(' android: {')
338 output.append(' lto: {')
339 output.append(' thin: %s,' % 'true' if self.lto else 'false')
340 output.append(' },')
341 output.append(' },')
342 output.append(' },')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000343 output.append('}')
344 output.append('')
345
346 def _output_field(self, output, name, sort=True):
347 value = getattr(self, name)
Lalit Magantiedace412019-06-18 13:28:28 +0100348 return write_blueprint_key_value(output, name, value, sort)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000349
350
351class Blueprint(object):
352 """In-memory representation of an Android.bp file."""
353
354 def __init__(self):
355 self.modules = {}
356
357 def add_module(self, module):
358 """Adds a new module to the blueprint, replacing any existing module
359 with the same name.
360
361 Args:
362 module: Module instance.
363 """
364 self.modules[module.name] = module
365
366 def to_string(self, output):
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000367 for m in sorted(self.modules.itervalues(), key=lambda m: m.name):
Sami Kyostila865d1d32017-12-12 18:37:04 +0000368 m.to_string(output)
369
370
Sami Kyostila865d1d32017-12-12 18:37:04 +0000371def label_to_module_name(label):
372 """Turn a GN label (e.g., //:perfetto_tests) into a module name."""
Primiano Tucci4e49c022017-12-21 18:22:44 +0100373 module = re.sub(r'^//:?', '', label)
374 module = re.sub(r'[^a-zA-Z0-9_]', '_', module)
375 if not module.startswith(module_prefix) and label not in default_targets:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000376 return module_prefix + module
377 return module
378
379
Sami Kyostila865d1d32017-12-12 18:37:04 +0000380def is_supported_source_file(name):
381 """Returns True if |name| can appear in a 'srcs' list."""
382 return os.path.splitext(name)[1] in ['.c', '.cc', '.proto']
383
384
385def is_generated_by_action(desc, label):
386 """Checks if a label is generated by an action.
387
388 Returns True if a GN output label |label| is an output for any action,
389 i.e., the file is generated dynamically.
390 """
391 for target in desc.itervalues():
392 if target['type'] == 'action' and label in target['outputs']:
393 return True
394 return False
395
396
397def apply_module_dependency(blueprint, desc, module, dep_name):
398 """Recursively collect dependencies for a given module.
399
400 Walk the transitive dependencies for a GN target and apply them to a given
401 module. This effectively flattens the dependency tree so that |module|
402 directly contains all the sources, libraries, etc. in the corresponding GN
403 dependency tree.
404
405 Args:
406 blueprint: Blueprint instance which is being generated.
407 desc: JSON GN description.
408 module: Module to which dependencies should be added.
409 dep_name: GN target of the dependency.
410 """
Sami Kyostila865d1d32017-12-12 18:37:04 +0000411 # If the dependency refers to a library which we can replace with an Android
412 # equivalent, stop recursing and patch the dependency in.
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100413 if gn_utils.label_without_toolchain(dep_name) in builtin_deps:
414 builtin_deps[gn_utils.label_without_toolchain(dep_name)](module)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000415 return
416
417 # Similarly some shared libraries are directly mapped to Android
418 # equivalents.
419 target = desc[dep_name]
420 for lib in target.get('libs', []):
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100421 # Generally library names sould be mangled as 'libXXX', unless they are
422 # HAL libraries (e.g., android.hardware.health@2.0).
423 android_lib = lib if '@' in lib else 'lib' + lib
Sami Kyostila865d1d32017-12-12 18:37:04 +0000424 if lib in library_whitelist and not android_lib in module.shared_libs:
425 module.shared_libs.append(android_lib)
426
427 type = target['type']
428 if type == 'action':
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100429 if "gen_merged_sql_metrics" in dep_name:
430 dep_mod = create_merged_sql_metrics_target(blueprint, desc, dep_name)
431 module.generated_headers.append(dep_mod.name)
432 else:
433 create_modules_from_target(blueprint, desc, dep_name)
434 # Depend both on the generated sources and headers -- see
435 # make_genrules_for_action.
436 module.srcs.append(':' + label_to_module_name(dep_name))
437 module.generated_headers.append(
438 label_to_module_name(dep_name) + '_headers')
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000439 elif type == 'static_library' and label_to_module_name(
440 dep_name) != module.name:
441 create_modules_from_target(blueprint, desc, dep_name)
442 module.static_libs.append(label_to_module_name(dep_name))
Primiano Tucci6067e732018-01-08 16:19:40 +0000443 elif type == 'shared_library' and label_to_module_name(
444 dep_name) != module.name:
445 module.shared_libs.append(label_to_module_name(dep_name))
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000446 elif type in ['group', 'source_set', 'executable', 'static_library'
447 ] and 'sources' in target:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000448 # Ignore source files that are generated by actions since they will be
449 # implicitly added by the genrule dependencies.
450 module.srcs.extend(
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100451 gn_utils.label_to_path(src) for src in target['sources']
Sami Kyostila865d1d32017-12-12 18:37:04 +0000452 if is_supported_source_file(src)
453 and not is_generated_by_action(desc, src))
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000454 module.cflags |= _get_cflags(target)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000455
456
457def make_genrules_for_action(blueprint, desc, target_name):
458 """Generate genrules for a GN action.
459
460 GN actions are used to dynamically generate files during the build. The
461 Soong equivalent is a genrule. This function turns a specific kind of
462 genrule which turns .proto files into source and header files into a pair
Sami Kyostila71625d72017-12-18 10:29:49 +0000463 equivalent genrules.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000464
465 Args:
466 blueprint: Blueprint instance which is being generated.
467 desc: JSON GN description.
468 target_name: GN target for genrule generation.
469
470 Returns:
471 A (source_genrule, header_genrule) module tuple.
472 """
473 target = desc[target_name]
474
475 # We only support genrules which call protoc (with or without a plugin) to
476 # turn .proto files into header and source files.
477 args = target['args']
Lalit Magantie87838f2019-06-25 18:31:49 +0100478 if '/protoc' not in args[0]:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000479 raise Error('Unsupported action in target %s: %s' % (target_name,
480 target['args']))
Primiano Tucci20b760c2018-01-19 12:36:12 +0000481 parser = ThrowingArgumentParser('Action in target %s (%s)' %
482 (target_name, ' '.join(target['args'])))
483 parser.add_argument('--proto_path')
484 parser.add_argument('--cpp_out')
485 parser.add_argument('--plugin')
486 parser.add_argument('--plugin_out')
Florian Mayerab55e3d2018-04-19 11:56:01 +0100487 parser.add_argument('--descriptor_set_out')
Lalit Magantidfe69ca2018-10-30 12:18:23 +0000488 parser.add_argument('--include_imports', action='store_true')
Primiano Tucci20b760c2018-01-19 12:36:12 +0000489 parser.add_argument('protos', nargs=argparse.REMAINDER)
Lalit Magantie87838f2019-06-25 18:31:49 +0100490 args = parser.parse_args(args[2:])
Primiano Tucci20b760c2018-01-19 12:36:12 +0000491
492 # Depending on whether we are using the default protoc C++ generator or the
493 # protozero plugin, the output dir is passed as:
494 # --cpp_out=gen/xxx or
495 # --plugin_out=:gen/xxx or
496 # --plugin_out=wrapper_namespace=pbzero:gen/xxx
497 gen_dir = args.cpp_out if args.cpp_out else args.plugin_out.split(':')[1]
498 assert gen_dir.startswith('gen/')
499 gen_dir = gen_dir[4:]
500 cpp_out_dir = ('$(genDir)/%s/%s' % (tree_path, gen_dir)).rstrip('/')
501
502 # TODO(skyostil): Is there a way to avoid hardcoding the tree path here?
503 # TODO(skyostil): Find a way to avoid creating the directory.
504 cmd = [
505 'mkdir -p %s &&' % cpp_out_dir,
506 '$(location aprotoc)',
507 '--cpp_out=%s' % cpp_out_dir
508 ]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000509
510 # We create two genrules for each action: one for the protobuf headers and
511 # another for the sources. This is because the module that depends on the
512 # generated files needs to declare two different types of dependencies --
513 # source files in 'srcs' and headers in 'generated_headers' -- and it's not
514 # valid to generate .h files from a source dependency and vice versa.
Sami Kyostila71625d72017-12-18 10:29:49 +0000515 source_module = Module('genrule', label_to_module_name(target_name))
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100516 source_module.srcs.extend(
517 gn_utils.label_to_path(src) for src in target['sources'])
Sami Kyostila865d1d32017-12-12 18:37:04 +0000518 source_module.tools = ['aprotoc']
519
Sami Kyostila71625d72017-12-18 10:29:49 +0000520 header_module = Module('genrule',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000521 label_to_module_name(target_name) + '_headers')
522 header_module.srcs = source_module.srcs[:]
523 header_module.tools = source_module.tools[:]
Primiano Tucci20b760c2018-01-19 12:36:12 +0000524 header_module.export_include_dirs = [gen_dir or '.']
Sami Kyostila865d1d32017-12-12 18:37:04 +0000525
Primiano Tucci20b760c2018-01-19 12:36:12 +0000526 # In GN builds the proto path is always relative to the output directory
527 # (out/tmp.xxx).
528 assert args.proto_path.startswith('../../')
529 cmd += [ '--proto_path=%s/%s' % (tree_path, args.proto_path[6:])]
530
Sami Kyostila865d1d32017-12-12 18:37:04 +0000531 namespaces = ['pb']
Sami Kyostila865d1d32017-12-12 18:37:04 +0000532 if args.plugin:
533 _, plugin = os.path.split(args.plugin)
534 # TODO(skyostil): Can we detect this some other way?
535 if plugin == 'ipc_plugin':
536 namespaces.append('ipc')
Primiano Tucci764c5042019-06-22 18:28:45 +0100537 elif plugin == 'protozero_plugin':
Sami Kyostila865d1d32017-12-12 18:37:04 +0000538 namespaces = ['pbzero']
539 for dep in target['deps']:
540 if desc[dep]['type'] != 'executable':
541 continue
542 _, executable = os.path.split(desc[dep]['outputs'][0])
543 if executable == plugin:
544 cmd += [
545 '--plugin=protoc-gen-plugin=$(location %s)' %
546 label_to_module_name(dep)
547 ]
548 source_module.tools.append(label_to_module_name(dep))
549 # Also make sure the module for the tool is generated.
550 create_modules_from_target(blueprint, desc, dep)
551 break
552 else:
553 raise Error('Unrecognized protoc plugin in target %s: %s' %
554 (target_name, args[i]))
555 if args.plugin_out:
556 plugin_args = args.plugin_out.split(':')[0]
Primiano Tucci20b760c2018-01-19 12:36:12 +0000557 cmd += ['--plugin_out=%s:%s' % (plugin_args, cpp_out_dir)]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000558
559 cmd += ['$(in)']
560 source_module.cmd = ' '.join(cmd)
561 header_module.cmd = source_module.cmd
562 header_module.tools = source_module.tools[:]
563
564 for ns in namespaces:
565 source_module.out += [
566 '%s/%s' % (tree_path, src.replace('.proto', '.%s.cc' % ns))
567 for src in source_module.srcs
568 ]
569 header_module.out += [
570 '%s/%s' % (tree_path, src.replace('.proto', '.%s.h' % ns))
571 for src in header_module.srcs
572 ]
573 return source_module, header_module
574
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100575
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100576def create_merged_sql_metrics_target(blueprint, desc, gn_target_name):
577 target_desc = desc[gn_target_name]
578 module = Module(
579 'genrule',
580 'gen_merged_sql_metrics',
581 )
582 module.tool_files = [
Lalit Maganti697cc482019-05-01 14:39:11 +0100583 'tools/gen_merged_sql_metrics.py',
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100584 ]
Lalit Maganti697cc482019-05-01 14:39:11 +0100585 module.cmd = ' '.join([
586 '$(location tools/gen_merged_sql_metrics.py)',
587 '--cpp_out=$(out)',
588 '$(in)',
589 ])
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100590 module.out = set(
591 src[src.index('gen/') + len('gen/'):]
592 for src in target_desc.get('outputs', [])
593 )
594 module.srcs.extend(
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100595 gn_utils.label_to_path(src)
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100596 for src in target_desc.get('inputs', [])
597 )
598 blueprint.add_module(module)
599 return module
Sami Kyostila865d1d32017-12-12 18:37:04 +0000600
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100601
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000602def _get_cflags(target):
603 cflags = set(flag for flag in target.get('cflags', [])
604 if re.match(cflag_whitelist, flag))
605 cflags |= set("-D%s" % define for define in target.get('defines', [])
606 if re.match(define_whitelist, define))
607 return cflags
608
609
Sami Kyostila865d1d32017-12-12 18:37:04 +0000610def create_modules_from_target(blueprint, desc, target_name):
611 """Generate module(s) for a given GN target.
612
613 Given a GN target name, generate one or more corresponding modules into a
614 blueprint.
615
616 Args:
617 blueprint: Blueprint instance which is being generated.
618 desc: JSON GN description.
619 target_name: GN target for module generation.
620 """
621 target = desc[target_name]
622 if target['type'] == 'executable':
Primiano Tucci21c19d82018-03-29 12:35:08 +0100623 if 'host' in target['toolchain'] or target_name in target_host_only:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000624 module_type = 'cc_binary_host'
Lalit Magantiedace412019-06-18 13:28:28 +0100625 elif target.get('testonly') and target_name not in non_test_binaries:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000626 module_type = 'cc_test'
627 else:
628 module_type = 'cc_binary'
629 modules = [Module(module_type, label_to_module_name(target_name))]
630 elif target['type'] == 'action':
631 modules = make_genrules_for_action(blueprint, desc, target_name)
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000632 elif target['type'] == 'static_library':
Lalit Magantic5bcd792018-01-12 18:38:11 +0000633 module = Module('cc_library_static', label_to_module_name(target_name))
634 module.export_include_dirs = ['include']
635 modules = [module]
Primiano Tucci6067e732018-01-08 16:19:40 +0000636 elif target['type'] == 'shared_library':
637 modules = [
638 Module('cc_library_shared', label_to_module_name(target_name))
639 ]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000640 else:
641 raise Error('Unknown target type: %s' % target['type'])
642
643 for module in modules:
644 module.comment = 'GN target: %s' % target_name
Primiano Tucci6067e732018-01-08 16:19:40 +0000645 if target_name in target_initrc:
646 module.init_rc = [target_initrc[target_name]]
Primiano Tucci6aa75572018-03-21 05:33:14 -0700647 if target_name in target_host_supported:
648 module.host_supported = True
Primiano Tucci6067e732018-01-08 16:19:40 +0000649
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000650 # Don't try to inject library/source dependencies into genrules because
651 # they are not compiled in the traditional sense.
Sami Kyostila71625d72017-12-18 10:29:49 +0000652 if module.type != 'genrule':
Sami Kyostila865d1d32017-12-12 18:37:04 +0000653 module.defaults = [defaults_module]
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000654 apply_module_dependency(blueprint, desc, module, target_name)
655 for dep in resolve_dependencies(desc, target_name):
656 apply_module_dependency(blueprint, desc, module, dep)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000657
Lalit Magantic5bcd792018-01-12 18:38:11 +0000658 # If the module is a static library, export all the generated headers.
659 if module.type == 'cc_library_static':
660 module.export_generated_headers = module.generated_headers
661
Ryan Savitskie65beca2019-01-29 18:29:13 +0000662 # Merge in additional hardcoded arguments.
663 for key, add_val in additional_args.get(module.name, []):
664 curr = getattr(module, key)
665 if add_val and isinstance(add_val, list) and isinstance(curr, list):
666 curr.extend(add_val)
667 else:
668 raise Error('Unimplemented type of additional_args')
669
Sami Kyostila865d1d32017-12-12 18:37:04 +0000670 blueprint.add_module(module)
671
672
673def resolve_dependencies(desc, target_name):
674 """Return the transitive set of dependent-on targets for a GN target.
675
676 Args:
677 blueprint: Blueprint instance which is being generated.
678 desc: JSON GN description.
679
680 Returns:
681 A set of transitive dependencies in the form of GN targets.
682 """
683
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100684 if gn_utils.label_without_toolchain(target_name) in builtin_deps:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000685 return set()
686 target = desc[target_name]
687 resolved_deps = set()
688 for dep in target.get('deps', []):
689 resolved_deps.add(dep)
690 # Ignore the transitive dependencies of actions because they are
691 # explicitly converted to genrules.
692 if desc[dep]['type'] == 'action':
693 continue
Primiano Tucci6067e732018-01-08 16:19:40 +0000694 # Dependencies on shared libraries shouldn't propagate any transitive
695 # dependencies but only depend on the shared library target
696 if desc[dep]['type'] == 'shared_library':
697 continue
Sami Kyostila865d1d32017-12-12 18:37:04 +0000698 resolved_deps.update(resolve_dependencies(desc, dep))
699 return resolved_deps
700
701
Ryan Savitski160b5822019-02-28 14:42:50 +0000702def create_blueprint_for_targets(desc, targets):
Sami Kyostila865d1d32017-12-12 18:37:04 +0000703 """Generate a blueprint for a list of GN targets."""
704 blueprint = Blueprint()
705
706 # Default settings used by all modules.
707 defaults = Module('cc_defaults', defaults_module)
708 defaults.local_include_dirs = ['include']
709 defaults.cflags = [
710 '-Wno-error=return-type',
711 '-Wno-sign-compare',
712 '-Wno-sign-promo',
713 '-Wno-unused-parameter',
Florian Mayercc424fd2018-01-15 11:19:01 +0000714 '-fvisibility=hidden',
Florian Mayerc2a38ea2018-01-19 11:48:43 +0000715 '-Oz',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000716 ]
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100717 defaults.user_debug_flag = True
Florian Mayer19f734f2019-07-05 12:08:01 +0100718 defaults.lto = True
Sami Kyostila865d1d32017-12-12 18:37:04 +0000719
720 blueprint.add_module(defaults)
721 for target in targets:
722 create_modules_from_target(blueprint, desc, target)
723 return blueprint
724
725
726def main():
727 parser = argparse.ArgumentParser(
728 description='Generate Android.bp from a GN description.')
729 parser.add_argument(
Primiano Tucci9c411652019-08-27 07:13:59 +0200730 '--check-only', help='Don\'t keep the generated files',
731 action='store_true')
732 parser.add_argument(
Sami Kyostilab27619f2017-12-13 19:22:16 +0000733 '--desc',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000734 help=
735 'GN description (e.g., gn desc out --format=json --all-toolchains "//*"'
736 )
737 parser.add_argument(
Lalit Magantic5bcd792018-01-12 18:38:11 +0000738 '--extras',
739 help='Extra targets to include at the end of the Blueprint file',
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100740 default=os.path.join(gn_utils.repo_root(), 'Android.bp.extras'),
Lalit Magantic5bcd792018-01-12 18:38:11 +0000741 )
742 parser.add_argument(
Sami Kyostilab27619f2017-12-13 19:22:16 +0000743 '--output',
744 help='Blueprint file to create',
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100745 default=os.path.join(gn_utils.repo_root(), 'Android.bp'),
Sami Kyostilab27619f2017-12-13 19:22:16 +0000746 )
747 parser.add_argument(
Sami Kyostila865d1d32017-12-12 18:37:04 +0000748 'targets',
749 nargs=argparse.REMAINDER,
750 help='Targets to include in the blueprint (e.g., "//:perfetto_tests")')
751 args = parser.parse_args()
752
Sami Kyostilab27619f2017-12-13 19:22:16 +0000753 if args.desc:
754 with open(args.desc) as f:
755 desc = json.load(f)
756 else:
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100757 desc = gn_utils.create_build_description(gn_args)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000758
Ryan Savitski160b5822019-02-28 14:42:50 +0000759 blueprint = create_blueprint_for_targets(desc, args.targets or
760 default_targets)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000761 output = [
762 """// Copyright (C) 2017 The Android Open Source Project
763//
764// Licensed under the Apache License, Version 2.0 (the "License");
765// you may not use this file except in compliance with the License.
766// You may obtain a copy of the License at
767//
768// http://www.apache.org/licenses/LICENSE-2.0
769//
770// Unless required by applicable law or agreed to in writing, software
771// distributed under the License is distributed on an "AS IS" BASIS,
772// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
773// See the License for the specific language governing permissions and
774// limitations under the License.
775//
776// This file is automatically generated by %s. Do not edit.
777""" % (__file__)
778 ]
779 blueprint.to_string(output)
Lalit Magantic5bcd792018-01-12 18:38:11 +0000780 with open(args.extras, 'r') as r:
781 for line in r:
782 output.append(line.rstrip("\n\r"))
Primiano Tucci9c411652019-08-27 07:13:59 +0200783
784 out_files = []
785
786 # Generate the Android.bp file.
787 out_files.append(args.output + '.swp')
788 with open(out_files[-1], 'w') as f:
Sami Kyostilab27619f2017-12-13 19:22:16 +0000789 f.write('\n'.join(output))
Sami Kyostila865d1d32017-12-12 18:37:04 +0000790
791
Primiano Tucci9c411652019-08-27 07:13:59 +0200792 # Either check the contents or move the files to their final destination.
793 return gn_utils.check_or_commit_generated_files (out_files, args.check_only)
794
795
Sami Kyostila865d1d32017-12-12 18:37:04 +0000796if __name__ == '__main__':
797 sys.exit(main())