blob: b07568cd3205434aa429eb3e120a29de8e6e6859 [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.
Primiano Tucci8e627442019-08-28 07:58:38 +0200114define_whitelist = r'^(GOOGLE_PROTO.*)|(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
Primiano Tucci8e627442019-08-28 07:58:38 +0200122# The directory where the generated perfetto_build_flags.h will be copied into.
123buildflags_dir = 'include/perfetto/base/build_configs/android_tree'
124
Florian Mayerb6a921f2018-10-18 18:55:23 +0100125# Additional arguments to apply to Android.bp rules.
126additional_args = {
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100127 'heapprofd_client': [
128 ('include_dirs', ['bionic/libc']),
Ryan Savitskie65beca2019-01-29 18:29:13 +0000129 ('static_libs', ['libasync_safe']),
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100130 ],
131 'traced_probes': [
Hector Dearman696ff772019-04-23 18:38:53 +0100132 ('required', ['libperfetto_android_internal', 'trigger_perfetto']),
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100133 ],
134 'libperfetto_android_internal': [
135 ('static_libs', ['libhealthhalutils']),
Florian Mayerb6a921f2018-10-18 18:55:23 +0100136 ],
137}
138
Sami Kyostila865d1d32017-12-12 18:37:04 +0000139def enable_gmock(module):
140 module.static_libs.append('libgmock')
141
142
Hector Dearman3e712a02017-12-19 16:39:59 +0000143def enable_gtest_prod(module):
144 module.static_libs.append('libgtest_prod')
145
146
Sami Kyostila865d1d32017-12-12 18:37:04 +0000147def enable_gtest(module):
148 assert module.type == 'cc_test'
149
150
151def enable_protobuf_full(module):
152 module.shared_libs.append('libprotobuf-cpp-full')
153
154
155def enable_protobuf_lite(module):
156 module.shared_libs.append('libprotobuf-cpp-lite')
157
158
159def enable_protoc_lib(module):
160 module.shared_libs.append('libprotoc')
161
Florian Mayera2fae262018-08-31 12:10:01 -0700162def enable_libunwindstack(module):
163 module.shared_libs.append('libunwindstack')
164 module.shared_libs.append('libprocinfo')
165 module.shared_libs.append('libbase')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000166
167def enable_libunwind(module):
Sami Kyostilafc074d42017-12-15 10:33:42 +0000168 # libunwind is disabled on Darwin so we cannot depend on it.
169 pass
Sami Kyostila865d1d32017-12-12 18:37:04 +0000170
Lalit Maganti17aa2732019-02-08 15:47:26 +0000171def enable_sqlite(module):
Lalit Magantiedace412019-06-18 13:28:28 +0100172 if module.type == 'cc_binary_host':
173 module.static_libs.append('libsqlite')
174 else:
175 # Copy what the sqlite3 command line tool does.
176 module.android.shared_libs.append('libsqlite')
177 module.android.shared_libs.append('libandroidicu')
178 module.android.shared_libs.append('liblog')
179 module.android.shared_libs.append('libutils')
180 module.host.static_libs.append('libsqlite')
Lalit Maganti17aa2732019-02-08 15:47:26 +0000181
Hector Dearmane0b993f2019-05-24 18:48:16 +0100182def enable_zlib(module):
183 module.shared_libs.append('libz')
184
Sami Kyostila865d1d32017-12-12 18:37:04 +0000185# Android equivalents for third-party libraries that the upstream project
186# depends on.
187builtin_deps = {
188 '//buildtools:gmock': enable_gmock,
189 '//buildtools:gtest': enable_gtest,
190 '//buildtools:gtest_main': enable_gtest,
191 '//buildtools:libunwind': enable_libunwind,
192 '//buildtools:protobuf_full': enable_protobuf_full,
193 '//buildtools:protobuf_lite': enable_protobuf_lite,
194 '//buildtools:protoc_lib': enable_protoc_lib,
Florian Mayera2fae262018-08-31 12:10:01 -0700195 '//buildtools:libunwindstack': enable_libunwindstack,
Lalit Maganti17aa2732019-02-08 15:47:26 +0000196 '//buildtools:sqlite': enable_sqlite,
Hector Dearmane0b993f2019-05-24 18:48:16 +0100197 '//buildtools:zlib': enable_zlib,
Sami Kyostila865d1d32017-12-12 18:37:04 +0000198}
199
200# ----------------------------------------------------------------------------
201# End of configuration.
202# ----------------------------------------------------------------------------
203
204
205class Error(Exception):
206 pass
207
208
209class ThrowingArgumentParser(argparse.ArgumentParser):
210 def __init__(self, context):
211 super(ThrowingArgumentParser, self).__init__()
212 self.context = context
213
214 def error(self, message):
215 raise Error('%s: %s' % (self.context, message))
216
217
Lalit Magantiedace412019-06-18 13:28:28 +0100218def write_blueprint_key_value(output, name, value, sort=True):
219 """Writes an Blueprint key-value pair to the output"""
220
221 if not value:
222 return
223 if isinstance(value, set):
224 value = sorted(value)
225 if isinstance(value, list):
226 output.append(' %s: [' % name)
227 for item in sorted(value) if sort else value:
228 output.append(' "%s",' % item)
229 output.append(' ],')
230 return
231 if isinstance(value, bool):
232 output.append(' %s: true,' % name)
233 return
234 if isinstance(value, Target):
235 value.to_string(output)
236 return
237 output.append(' %s: "%s",' % (name, value))
238
239class Target(object):
240 """A target-scoped part of a module"""
241
242 def __init__(self, name):
243 self.name = name
244 self.shared_libs = []
245 self.static_libs = []
246 self.cflags = set()
247
248 def to_string(self, output):
249 nested_out = []
250 self._output_field(nested_out, 'shared_libs')
251 self._output_field(nested_out, 'static_libs')
252 self._output_field(nested_out, 'cflags')
253
254 if nested_out:
255 output.append(' %s: {' % self.name)
256 for line in nested_out:
257 output.append(' %s' % line)
258 output.append(' },')
259
260 def _output_field(self, output, name, sort=True):
261 value = getattr(self, name)
262 return write_blueprint_key_value(output, name, value, sort)
263
Sami Kyostila865d1d32017-12-12 18:37:04 +0000264class Module(object):
265 """A single module (e.g., cc_binary, cc_test) in a blueprint."""
266
267 def __init__(self, mod_type, name):
268 self.type = mod_type
269 self.name = name
270 self.srcs = []
271 self.comment = None
272 self.shared_libs = []
273 self.static_libs = []
274 self.tools = []
275 self.cmd = None
Primiano Tucci6aa75572018-03-21 05:33:14 -0700276 self.host_supported = False
Primiano Tucci6067e732018-01-08 16:19:40 +0000277 self.init_rc = []
Sami Kyostila865d1d32017-12-12 18:37:04 +0000278 self.out = []
279 self.export_include_dirs = []
280 self.generated_headers = []
Lalit Magantic5bcd792018-01-12 18:38:11 +0000281 self.export_generated_headers = []
Sami Kyostila865d1d32017-12-12 18:37:04 +0000282 self.defaults = []
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000283 self.cflags = set()
Sami Kyostila865d1d32017-12-12 18:37:04 +0000284 self.local_include_dirs = []
Ryan Savitskie65beca2019-01-29 18:29:13 +0000285 self.include_dirs = []
286 self.required = []
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100287 self.user_debug_flag = False
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100288 self.tool_files = None
Lalit Magantiedace412019-06-18 13:28:28 +0100289 self.android = Target('android')
290 self.host = Target('host')
Florian Mayer19f734f2019-07-05 12:08:01 +0100291 self.lto = None
Sami Kyostila865d1d32017-12-12 18:37:04 +0000292
293 def to_string(self, output):
294 if self.comment:
295 output.append('// %s' % self.comment)
296 output.append('%s {' % self.type)
297 self._output_field(output, 'name')
298 self._output_field(output, 'srcs')
299 self._output_field(output, 'shared_libs')
300 self._output_field(output, 'static_libs')
301 self._output_field(output, 'tools')
302 self._output_field(output, 'cmd', sort=False)
Primiano Tucci6aa75572018-03-21 05:33:14 -0700303 self._output_field(output, 'host_supported')
Primiano Tucci6067e732018-01-08 16:19:40 +0000304 self._output_field(output, 'init_rc')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000305 self._output_field(output, 'out')
306 self._output_field(output, 'export_include_dirs')
307 self._output_field(output, 'generated_headers')
Lalit Magantic5bcd792018-01-12 18:38:11 +0000308 self._output_field(output, 'export_generated_headers')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000309 self._output_field(output, 'defaults')
310 self._output_field(output, 'cflags')
311 self._output_field(output, 'local_include_dirs')
Ryan Savitskie65beca2019-01-29 18:29:13 +0000312 self._output_field(output, 'include_dirs')
313 self._output_field(output, 'required')
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100314 self._output_field(output, 'tool_files')
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100315
Lalit Magantiedace412019-06-18 13:28:28 +0100316 target_out = []
317 self._output_field(target_out, 'android')
318 self._output_field(target_out, 'host')
319 if target_out:
320 output.append(' target: {')
321 for line in target_out:
322 output.append(' %s' % line)
323 output.append(' },')
324
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100325 disable_pdk = any(name in library_not_in_pdk for name in self.shared_libs)
326 if self.user_debug_flag or disable_pdk:
Logan Chien9bfaaf92018-02-13 18:49:24 +0800327 output.append(' product_variables: {')
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100328 if disable_pdk:
329 output.append(' pdk: {')
330 output.append(' enabled: false,')
331 output.append(' },')
332 if self.user_debug_flag:
333 output.append(' debuggable: {')
334 output.append(' cflags: ["-DPERFETTO_BUILD_WITH_ANDROID_USERDEBUG"],')
335 output.append(' },')
Logan Chien9bfaaf92018-02-13 18:49:24 +0800336 output.append(' },')
Florian Mayer19f734f2019-07-05 12:08:01 +0100337 if self.lto is not None:
338 output.append(' target: {')
339 output.append(' android: {')
340 output.append(' lto: {')
341 output.append(' thin: %s,' % 'true' if self.lto else 'false')
342 output.append(' },')
343 output.append(' },')
344 output.append(' },')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000345 output.append('}')
346 output.append('')
347
348 def _output_field(self, output, name, sort=True):
349 value = getattr(self, name)
Lalit Magantiedace412019-06-18 13:28:28 +0100350 return write_blueprint_key_value(output, name, value, sort)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000351
352
353class Blueprint(object):
354 """In-memory representation of an Android.bp file."""
355
356 def __init__(self):
357 self.modules = {}
358
359 def add_module(self, module):
360 """Adds a new module to the blueprint, replacing any existing module
361 with the same name.
362
363 Args:
364 module: Module instance.
365 """
366 self.modules[module.name] = module
367
368 def to_string(self, output):
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000369 for m in sorted(self.modules.itervalues(), key=lambda m: m.name):
Sami Kyostila865d1d32017-12-12 18:37:04 +0000370 m.to_string(output)
371
372
Sami Kyostila865d1d32017-12-12 18:37:04 +0000373def label_to_module_name(label):
374 """Turn a GN label (e.g., //:perfetto_tests) into a module name."""
Primiano Tucci4e49c022017-12-21 18:22:44 +0100375 module = re.sub(r'^//:?', '', label)
376 module = re.sub(r'[^a-zA-Z0-9_]', '_', module)
377 if not module.startswith(module_prefix) and label not in default_targets:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000378 return module_prefix + module
379 return module
380
381
Sami Kyostila865d1d32017-12-12 18:37:04 +0000382def is_supported_source_file(name):
383 """Returns True if |name| can appear in a 'srcs' list."""
384 return os.path.splitext(name)[1] in ['.c', '.cc', '.proto']
385
386
387def is_generated_by_action(desc, label):
388 """Checks if a label is generated by an action.
389
390 Returns True if a GN output label |label| is an output for any action,
391 i.e., the file is generated dynamically.
392 """
393 for target in desc.itervalues():
394 if target['type'] == 'action' and label in target['outputs']:
395 return True
396 return False
397
398
399def apply_module_dependency(blueprint, desc, module, dep_name):
400 """Recursively collect dependencies for a given module.
401
402 Walk the transitive dependencies for a GN target and apply them to a given
403 module. This effectively flattens the dependency tree so that |module|
404 directly contains all the sources, libraries, etc. in the corresponding GN
405 dependency tree.
406
407 Args:
408 blueprint: Blueprint instance which is being generated.
409 desc: JSON GN description.
410 module: Module to which dependencies should be added.
411 dep_name: GN target of the dependency.
412 """
Sami Kyostila865d1d32017-12-12 18:37:04 +0000413 # If the dependency refers to a library which we can replace with an Android
414 # equivalent, stop recursing and patch the dependency in.
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100415 if gn_utils.label_without_toolchain(dep_name) in builtin_deps:
416 builtin_deps[gn_utils.label_without_toolchain(dep_name)](module)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000417 return
418
419 # Similarly some shared libraries are directly mapped to Android
420 # equivalents.
421 target = desc[dep_name]
422 for lib in target.get('libs', []):
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100423 # Generally library names sould be mangled as 'libXXX', unless they are
424 # HAL libraries (e.g., android.hardware.health@2.0).
425 android_lib = lib if '@' in lib else 'lib' + lib
Sami Kyostila865d1d32017-12-12 18:37:04 +0000426 if lib in library_whitelist and not android_lib in module.shared_libs:
427 module.shared_libs.append(android_lib)
428
429 type = target['type']
430 if type == 'action':
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100431 if "gen_merged_sql_metrics" in dep_name:
432 dep_mod = create_merged_sql_metrics_target(blueprint, desc, dep_name)
433 module.generated_headers.append(dep_mod.name)
Primiano Tucci8e627442019-08-28 07:58:38 +0200434 elif dep_name.startswith(gn_utils.BUILDFLAGS_TARGET):
435 # Ignore the dependency on the gen_buildflags genrule. That is run
436 # separately in this generator and the generated file is copied over
437 # into the repo (see usage of |buildflags_dir| in this script).
438 return
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100439 else:
440 create_modules_from_target(blueprint, desc, dep_name)
441 # Depend both on the generated sources and headers -- see
442 # make_genrules_for_action.
443 module.srcs.append(':' + label_to_module_name(dep_name))
444 module.generated_headers.append(
445 label_to_module_name(dep_name) + '_headers')
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000446 elif type == 'static_library' and label_to_module_name(
447 dep_name) != module.name:
448 create_modules_from_target(blueprint, desc, dep_name)
449 module.static_libs.append(label_to_module_name(dep_name))
Primiano Tucci6067e732018-01-08 16:19:40 +0000450 elif type == 'shared_library' and label_to_module_name(
451 dep_name) != module.name:
452 module.shared_libs.append(label_to_module_name(dep_name))
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000453 elif type in ['group', 'source_set', 'executable', 'static_library'
454 ] and 'sources' in target:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000455 # Ignore source files that are generated by actions since they will be
456 # implicitly added by the genrule dependencies.
457 module.srcs.extend(
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100458 gn_utils.label_to_path(src) for src in target['sources']
Sami Kyostila865d1d32017-12-12 18:37:04 +0000459 if is_supported_source_file(src)
460 and not is_generated_by_action(desc, src))
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000461 module.cflags |= _get_cflags(target)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000462
463
464def make_genrules_for_action(blueprint, desc, target_name):
465 """Generate genrules for a GN action.
466
467 GN actions are used to dynamically generate files during the build. The
468 Soong equivalent is a genrule. This function turns a specific kind of
469 genrule which turns .proto files into source and header files into a pair
Sami Kyostila71625d72017-12-18 10:29:49 +0000470 equivalent genrules.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000471
472 Args:
473 blueprint: Blueprint instance which is being generated.
474 desc: JSON GN description.
475 target_name: GN target for genrule generation.
476
477 Returns:
478 A (source_genrule, header_genrule) module tuple.
479 """
480 target = desc[target_name]
481
482 # We only support genrules which call protoc (with or without a plugin) to
483 # turn .proto files into header and source files.
484 args = target['args']
Lalit Magantie87838f2019-06-25 18:31:49 +0100485 if '/protoc' not in args[0]:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000486 raise Error('Unsupported action in target %s: %s' % (target_name,
487 target['args']))
Primiano Tucci20b760c2018-01-19 12:36:12 +0000488 parser = ThrowingArgumentParser('Action in target %s (%s)' %
489 (target_name, ' '.join(target['args'])))
490 parser.add_argument('--proto_path')
491 parser.add_argument('--cpp_out')
492 parser.add_argument('--plugin')
493 parser.add_argument('--plugin_out')
Florian Mayerab55e3d2018-04-19 11:56:01 +0100494 parser.add_argument('--descriptor_set_out')
Lalit Magantidfe69ca2018-10-30 12:18:23 +0000495 parser.add_argument('--include_imports', action='store_true')
Primiano Tucci20b760c2018-01-19 12:36:12 +0000496 parser.add_argument('protos', nargs=argparse.REMAINDER)
Lalit Magantie87838f2019-06-25 18:31:49 +0100497 args = parser.parse_args(args[2:])
Primiano Tucci20b760c2018-01-19 12:36:12 +0000498
499 # Depending on whether we are using the default protoc C++ generator or the
500 # protozero plugin, the output dir is passed as:
501 # --cpp_out=gen/xxx or
502 # --plugin_out=:gen/xxx or
503 # --plugin_out=wrapper_namespace=pbzero:gen/xxx
504 gen_dir = args.cpp_out if args.cpp_out else args.plugin_out.split(':')[1]
505 assert gen_dir.startswith('gen/')
506 gen_dir = gen_dir[4:]
507 cpp_out_dir = ('$(genDir)/%s/%s' % (tree_path, gen_dir)).rstrip('/')
508
509 # TODO(skyostil): Is there a way to avoid hardcoding the tree path here?
510 # TODO(skyostil): Find a way to avoid creating the directory.
511 cmd = [
512 'mkdir -p %s &&' % cpp_out_dir,
513 '$(location aprotoc)',
514 '--cpp_out=%s' % cpp_out_dir
515 ]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000516
517 # We create two genrules for each action: one for the protobuf headers and
518 # another for the sources. This is because the module that depends on the
519 # generated files needs to declare two different types of dependencies --
520 # source files in 'srcs' and headers in 'generated_headers' -- and it's not
521 # valid to generate .h files from a source dependency and vice versa.
Sami Kyostila71625d72017-12-18 10:29:49 +0000522 source_module = Module('genrule', label_to_module_name(target_name))
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100523 source_module.srcs.extend(
524 gn_utils.label_to_path(src) for src in target['sources'])
Sami Kyostila865d1d32017-12-12 18:37:04 +0000525 source_module.tools = ['aprotoc']
526
Sami Kyostila71625d72017-12-18 10:29:49 +0000527 header_module = Module('genrule',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000528 label_to_module_name(target_name) + '_headers')
529 header_module.srcs = source_module.srcs[:]
530 header_module.tools = source_module.tools[:]
Primiano Tucci20b760c2018-01-19 12:36:12 +0000531 header_module.export_include_dirs = [gen_dir or '.']
Sami Kyostila865d1d32017-12-12 18:37:04 +0000532
Primiano Tucci20b760c2018-01-19 12:36:12 +0000533 # In GN builds the proto path is always relative to the output directory
534 # (out/tmp.xxx).
535 assert args.proto_path.startswith('../../')
536 cmd += [ '--proto_path=%s/%s' % (tree_path, args.proto_path[6:])]
537
Sami Kyostila865d1d32017-12-12 18:37:04 +0000538 namespaces = ['pb']
Sami Kyostila865d1d32017-12-12 18:37:04 +0000539 if args.plugin:
540 _, plugin = os.path.split(args.plugin)
541 # TODO(skyostil): Can we detect this some other way?
542 if plugin == 'ipc_plugin':
543 namespaces.append('ipc')
Primiano Tucci764c5042019-06-22 18:28:45 +0100544 elif plugin == 'protozero_plugin':
Sami Kyostila865d1d32017-12-12 18:37:04 +0000545 namespaces = ['pbzero']
546 for dep in target['deps']:
547 if desc[dep]['type'] != 'executable':
548 continue
549 _, executable = os.path.split(desc[dep]['outputs'][0])
550 if executable == plugin:
551 cmd += [
552 '--plugin=protoc-gen-plugin=$(location %s)' %
553 label_to_module_name(dep)
554 ]
555 source_module.tools.append(label_to_module_name(dep))
556 # Also make sure the module for the tool is generated.
557 create_modules_from_target(blueprint, desc, dep)
558 break
559 else:
560 raise Error('Unrecognized protoc plugin in target %s: %s' %
561 (target_name, args[i]))
562 if args.plugin_out:
563 plugin_args = args.plugin_out.split(':')[0]
Primiano Tucci20b760c2018-01-19 12:36:12 +0000564 cmd += ['--plugin_out=%s:%s' % (plugin_args, cpp_out_dir)]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000565
566 cmd += ['$(in)']
567 source_module.cmd = ' '.join(cmd)
568 header_module.cmd = source_module.cmd
569 header_module.tools = source_module.tools[:]
570
571 for ns in namespaces:
572 source_module.out += [
573 '%s/%s' % (tree_path, src.replace('.proto', '.%s.cc' % ns))
574 for src in source_module.srcs
575 ]
576 header_module.out += [
577 '%s/%s' % (tree_path, src.replace('.proto', '.%s.h' % ns))
578 for src in header_module.srcs
579 ]
580 return source_module, header_module
581
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100582
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100583def create_merged_sql_metrics_target(blueprint, desc, gn_target_name):
584 target_desc = desc[gn_target_name]
585 module = Module(
586 'genrule',
587 'gen_merged_sql_metrics',
588 )
589 module.tool_files = [
Lalit Maganti697cc482019-05-01 14:39:11 +0100590 'tools/gen_merged_sql_metrics.py',
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100591 ]
Lalit Maganti697cc482019-05-01 14:39:11 +0100592 module.cmd = ' '.join([
593 '$(location tools/gen_merged_sql_metrics.py)',
594 '--cpp_out=$(out)',
595 '$(in)',
596 ])
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100597 module.out = set(
598 src[src.index('gen/') + len('gen/'):]
599 for src in target_desc.get('outputs', [])
600 )
601 module.srcs.extend(
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100602 gn_utils.label_to_path(src)
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100603 for src in target_desc.get('inputs', [])
604 )
605 blueprint.add_module(module)
606 return module
Sami Kyostila865d1d32017-12-12 18:37:04 +0000607
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100608
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000609def _get_cflags(target):
610 cflags = set(flag for flag in target.get('cflags', [])
611 if re.match(cflag_whitelist, flag))
612 cflags |= set("-D%s" % define for define in target.get('defines', [])
613 if re.match(define_whitelist, define))
614 return cflags
615
616
Sami Kyostila865d1d32017-12-12 18:37:04 +0000617def create_modules_from_target(blueprint, desc, target_name):
618 """Generate module(s) for a given GN target.
619
620 Given a GN target name, generate one or more corresponding modules into a
621 blueprint.
622
623 Args:
624 blueprint: Blueprint instance which is being generated.
625 desc: JSON GN description.
626 target_name: GN target for module generation.
627 """
628 target = desc[target_name]
Primiano Tucci8e627442019-08-28 07:58:38 +0200629 export_include_dirs = [ 'include', buildflags_dir ]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000630 if target['type'] == 'executable':
Primiano Tucci21c19d82018-03-29 12:35:08 +0100631 if 'host' in target['toolchain'] or target_name in target_host_only:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000632 module_type = 'cc_binary_host'
Lalit Magantiedace412019-06-18 13:28:28 +0100633 elif target.get('testonly') and target_name not in non_test_binaries:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000634 module_type = 'cc_test'
635 else:
636 module_type = 'cc_binary'
637 modules = [Module(module_type, label_to_module_name(target_name))]
638 elif target['type'] == 'action':
639 modules = make_genrules_for_action(blueprint, desc, target_name)
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000640 elif target['type'] == 'static_library':
Lalit Magantic5bcd792018-01-12 18:38:11 +0000641 module = Module('cc_library_static', label_to_module_name(target_name))
Primiano Tucci8e627442019-08-28 07:58:38 +0200642 module.export_include_dirs = export_include_dirs
Lalit Magantic5bcd792018-01-12 18:38:11 +0000643 modules = [module]
Primiano Tucci6067e732018-01-08 16:19:40 +0000644 elif target['type'] == 'shared_library':
Primiano Tucci8e627442019-08-28 07:58:38 +0200645 module = Module('cc_library_shared', label_to_module_name(target_name))
646 module.export_include_dirs = export_include_dirs
647 modules = [module]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000648 else:
649 raise Error('Unknown target type: %s' % target['type'])
650
651 for module in modules:
652 module.comment = 'GN target: %s' % target_name
Primiano Tucci6067e732018-01-08 16:19:40 +0000653 if target_name in target_initrc:
654 module.init_rc = [target_initrc[target_name]]
Primiano Tucci6aa75572018-03-21 05:33:14 -0700655 if target_name in target_host_supported:
656 module.host_supported = True
Primiano Tucci6067e732018-01-08 16:19:40 +0000657
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000658 # Don't try to inject library/source dependencies into genrules because
659 # they are not compiled in the traditional sense.
Sami Kyostila71625d72017-12-18 10:29:49 +0000660 if module.type != 'genrule':
Sami Kyostila865d1d32017-12-12 18:37:04 +0000661 module.defaults = [defaults_module]
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000662 apply_module_dependency(blueprint, desc, module, target_name)
663 for dep in resolve_dependencies(desc, target_name):
664 apply_module_dependency(blueprint, desc, module, dep)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000665
Lalit Magantic5bcd792018-01-12 18:38:11 +0000666 # If the module is a static library, export all the generated headers.
667 if module.type == 'cc_library_static':
668 module.export_generated_headers = module.generated_headers
669
Ryan Savitskie65beca2019-01-29 18:29:13 +0000670 # Merge in additional hardcoded arguments.
671 for key, add_val in additional_args.get(module.name, []):
672 curr = getattr(module, key)
673 if add_val and isinstance(add_val, list) and isinstance(curr, list):
674 curr.extend(add_val)
675 else:
676 raise Error('Unimplemented type of additional_args')
677
Sami Kyostila865d1d32017-12-12 18:37:04 +0000678 blueprint.add_module(module)
679
680
681def resolve_dependencies(desc, target_name):
682 """Return the transitive set of dependent-on targets for a GN target.
683
684 Args:
685 blueprint: Blueprint instance which is being generated.
686 desc: JSON GN description.
687
688 Returns:
689 A set of transitive dependencies in the form of GN targets.
690 """
691
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100692 if gn_utils.label_without_toolchain(target_name) in builtin_deps:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000693 return set()
694 target = desc[target_name]
695 resolved_deps = set()
696 for dep in target.get('deps', []):
697 resolved_deps.add(dep)
698 # Ignore the transitive dependencies of actions because they are
699 # explicitly converted to genrules.
700 if desc[dep]['type'] == 'action':
701 continue
Primiano Tucci6067e732018-01-08 16:19:40 +0000702 # Dependencies on shared libraries shouldn't propagate any transitive
703 # dependencies but only depend on the shared library target
704 if desc[dep]['type'] == 'shared_library':
705 continue
Sami Kyostila865d1d32017-12-12 18:37:04 +0000706 resolved_deps.update(resolve_dependencies(desc, dep))
707 return resolved_deps
708
709
Ryan Savitski160b5822019-02-28 14:42:50 +0000710def create_blueprint_for_targets(desc, targets):
Sami Kyostila865d1d32017-12-12 18:37:04 +0000711 """Generate a blueprint for a list of GN targets."""
712 blueprint = Blueprint()
713
714 # Default settings used by all modules.
715 defaults = Module('cc_defaults', defaults_module)
Primiano Tucci8e627442019-08-28 07:58:38 +0200716
717 # We have to use include_dirs passing the path relative to the android tree.
718 # This is because: (i) perfetto_cc_defaults is used also by
719 # test/**/Android.bp; (ii) if we use local_include_dirs instead, paths
720 # become relative to the Android.bp that *uses* cc_defaults (not the one
721 # that defines it).s
722 defaults.include_dirs = [tree_path,
723 tree_path + '/include',
724 tree_path + '/' + buildflags_dir]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000725 defaults.cflags = [
726 '-Wno-error=return-type',
727 '-Wno-sign-compare',
728 '-Wno-sign-promo',
729 '-Wno-unused-parameter',
Florian Mayercc424fd2018-01-15 11:19:01 +0000730 '-fvisibility=hidden',
Florian Mayerc2a38ea2018-01-19 11:48:43 +0000731 '-Oz',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000732 ]
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100733 defaults.user_debug_flag = True
Florian Mayer19f734f2019-07-05 12:08:01 +0100734 defaults.lto = True
Sami Kyostila865d1d32017-12-12 18:37:04 +0000735
736 blueprint.add_module(defaults)
737 for target in targets:
738 create_modules_from_target(blueprint, desc, target)
739 return blueprint
740
741
742def main():
743 parser = argparse.ArgumentParser(
744 description='Generate Android.bp from a GN description.')
745 parser.add_argument(
Primiano Tucci9c411652019-08-27 07:13:59 +0200746 '--check-only', help='Don\'t keep the generated files',
747 action='store_true')
748 parser.add_argument(
Sami Kyostilab27619f2017-12-13 19:22:16 +0000749 '--desc',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000750 help=
751 'GN description (e.g., gn desc out --format=json --all-toolchains "//*"'
752 )
753 parser.add_argument(
Lalit Magantic5bcd792018-01-12 18:38:11 +0000754 '--extras',
755 help='Extra targets to include at the end of the Blueprint file',
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100756 default=os.path.join(gn_utils.repo_root(), 'Android.bp.extras'),
Lalit Magantic5bcd792018-01-12 18:38:11 +0000757 )
758 parser.add_argument(
Sami Kyostilab27619f2017-12-13 19:22:16 +0000759 '--output',
760 help='Blueprint file to create',
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100761 default=os.path.join(gn_utils.repo_root(), 'Android.bp'),
Sami Kyostilab27619f2017-12-13 19:22:16 +0000762 )
763 parser.add_argument(
Sami Kyostila865d1d32017-12-12 18:37:04 +0000764 'targets',
765 nargs=argparse.REMAINDER,
766 help='Targets to include in the blueprint (e.g., "//:perfetto_tests")')
767 args = parser.parse_args()
768
Sami Kyostilab27619f2017-12-13 19:22:16 +0000769 if args.desc:
770 with open(args.desc) as f:
771 desc = json.load(f)
772 else:
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100773 desc = gn_utils.create_build_description(gn_args)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000774
Ryan Savitski160b5822019-02-28 14:42:50 +0000775 blueprint = create_blueprint_for_targets(desc, args.targets or
776 default_targets)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000777 output = [
778 """// Copyright (C) 2017 The Android Open Source Project
779//
780// Licensed under the Apache License, Version 2.0 (the "License");
781// you may not use this file except in compliance with the License.
782// You may obtain a copy of the License at
783//
784// http://www.apache.org/licenses/LICENSE-2.0
785//
786// Unless required by applicable law or agreed to in writing, software
787// distributed under the License is distributed on an "AS IS" BASIS,
788// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
789// See the License for the specific language governing permissions and
790// limitations under the License.
791//
792// This file is automatically generated by %s. Do not edit.
793""" % (__file__)
794 ]
795 blueprint.to_string(output)
Lalit Magantic5bcd792018-01-12 18:38:11 +0000796 with open(args.extras, 'r') as r:
797 for line in r:
798 output.append(line.rstrip("\n\r"))
Primiano Tucci9c411652019-08-27 07:13:59 +0200799
800 out_files = []
801
802 # Generate the Android.bp file.
803 out_files.append(args.output + '.swp')
804 with open(out_files[-1], 'w') as f:
Sami Kyostilab27619f2017-12-13 19:22:16 +0000805 f.write('\n'.join(output))
Sami Kyostila865d1d32017-12-12 18:37:04 +0000806
Primiano Tucci8e627442019-08-28 07:58:38 +0200807 # Generate the perfetto_build_flags.h file.
808 out_files.append(os.path.join(buildflags_dir, 'perfetto_build_flags.h.swp'))
809 gn_utils.gen_buildflags(gn_args, out_files[-1])
Sami Kyostila865d1d32017-12-12 18:37:04 +0000810
Primiano Tucci9c411652019-08-27 07:13:59 +0200811 # Either check the contents or move the files to their final destination.
812 return gn_utils.check_or_commit_generated_files (out_files, args.check_only)
813
814
Sami Kyostila865d1d32017-12-12 18:37:04 +0000815if __name__ == '__main__':
816 sys.exit(main())