blob: aa13178f7bb8b1dc810be879c10787959b5900aa [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 Tucci4c5efa42018-10-23 13:15:13 +010075gn_args = 'target_os="android" target_cpu="arm" is_debug=false perfetto_build_with_android=true'
Sami Kyostilab27619f2017-12-13 19:22:16 +000076
Sami Kyostila865d1d32017-12-12 18:37:04 +000077# All module names are prefixed with this string to avoid collisions.
78module_prefix = 'perfetto_'
79
80# Shared libraries which are directly translated to Android system equivalents.
81library_whitelist = [
Lalit Maganti8eba3092019-03-27 13:25:29 +000082 "android.hardware.atrace@1.0",
Primiano Tucci676f0cc2018-12-03 20:03:26 +010083 'android.hardware.health@2.0',
Esteban Talavera1fecac92019-01-09 16:06:29 +000084 "android.hardware.power.stats@1.0",
Sami Kyostila865d1d32017-12-12 18:37:04 +000085 'android',
Primiano Tucci676f0cc2018-12-03 20:03:26 +010086 'base',
Sami Kyostilab5b71692018-01-12 12:16:44 +000087 'binder',
Primiano Tucci676f0cc2018-12-03 20:03:26 +010088 'hidlbase',
89 'hidltransport',
90 'hwbinder',
Ryan Savitski53ca60b2019-06-03 13:04:40 +010091 'incident',
Sami Kyostila865d1d32017-12-12 18:37:04 +000092 'log',
Sami Kyostilab5b71692018-01-12 12:16:44 +000093 'services',
Primiano Tucciedf099c2018-01-08 18:27:56 +000094 'utils',
Sami Kyostila865d1d32017-12-12 18:37:04 +000095]
96
97# Name of the module which settings such as compiler flags for all other
98# modules.
99defaults_module = module_prefix + 'defaults'
100
101# Location of the project in the Android source tree.
102tree_path = 'external/perfetto'
103
Primiano Tucciedf099c2018-01-08 18:27:56 +0000104# Compiler flags which are passed through to the blueprint.
105cflag_whitelist = r'^-DPERFETTO.*$'
106
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000107# Compiler defines which are passed through to the blueprint.
Hector Dearman554627f2019-06-04 17:58:22 +0100108define_whitelist = r'^(GOOGLE_PROTO.*)|(PERFETTO_BUILD_WITH_ANDROID)|(ZLIB_.*)|(USE_MMAP)|(HAVE_HIDDEN)$'
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000109
Logan Chien9bfaaf92018-02-13 18:49:24 +0800110# Shared libraries which are not in PDK.
111library_not_in_pdk = {
112 'libandroid',
113 'libservices',
114}
115
Florian Mayerb6a921f2018-10-18 18:55:23 +0100116# Additional arguments to apply to Android.bp rules.
117additional_args = {
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100118 'heapprofd_client': [
119 ('include_dirs', ['bionic/libc']),
Ryan Savitskie65beca2019-01-29 18:29:13 +0000120 ('static_libs', ['libasync_safe']),
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100121 ],
122 'traced_probes': [
Hector Dearman696ff772019-04-23 18:38:53 +0100123 ('required', ['libperfetto_android_internal', 'trigger_perfetto']),
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100124 ],
125 'libperfetto_android_internal': [
126 ('static_libs', ['libhealthhalutils']),
Florian Mayerb6a921f2018-10-18 18:55:23 +0100127 ],
128}
129
Sami Kyostila865d1d32017-12-12 18:37:04 +0000130
131def enable_gmock(module):
132 module.static_libs.append('libgmock')
133
134
Hector Dearman3e712a02017-12-19 16:39:59 +0000135def enable_gtest_prod(module):
136 module.static_libs.append('libgtest_prod')
137
138
Sami Kyostila865d1d32017-12-12 18:37:04 +0000139def enable_gtest(module):
140 assert module.type == 'cc_test'
141
142
143def enable_protobuf_full(module):
144 module.shared_libs.append('libprotobuf-cpp-full')
145
146
147def enable_protobuf_lite(module):
148 module.shared_libs.append('libprotobuf-cpp-lite')
149
150
151def enable_protoc_lib(module):
152 module.shared_libs.append('libprotoc')
153
Florian Mayera2fae262018-08-31 12:10:01 -0700154def enable_libunwindstack(module):
155 module.shared_libs.append('libunwindstack')
156 module.shared_libs.append('libprocinfo')
157 module.shared_libs.append('libbase')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000158
159def enable_libunwind(module):
Sami Kyostilafc074d42017-12-15 10:33:42 +0000160 # libunwind is disabled on Darwin so we cannot depend on it.
161 pass
Sami Kyostila865d1d32017-12-12 18:37:04 +0000162
Lalit Maganti17aa2732019-02-08 15:47:26 +0000163def enable_sqlite(module):
Lalit Magantiedace412019-06-18 13:28:28 +0100164 if module.type == 'cc_binary_host':
165 module.static_libs.append('libsqlite')
166 else:
167 # Copy what the sqlite3 command line tool does.
168 module.android.shared_libs.append('libsqlite')
169 module.android.shared_libs.append('libandroidicu')
170 module.android.shared_libs.append('liblog')
171 module.android.shared_libs.append('libutils')
172 module.host.static_libs.append('libsqlite')
Lalit Maganti17aa2732019-02-08 15:47:26 +0000173
Hector Dearmane0b993f2019-05-24 18:48:16 +0100174def enable_zlib(module):
175 module.shared_libs.append('libz')
176
Sami Kyostila865d1d32017-12-12 18:37:04 +0000177# Android equivalents for third-party libraries that the upstream project
178# depends on.
179builtin_deps = {
180 '//buildtools:gmock': enable_gmock,
181 '//buildtools:gtest': enable_gtest,
182 '//buildtools:gtest_main': enable_gtest,
183 '//buildtools:libunwind': enable_libunwind,
184 '//buildtools:protobuf_full': enable_protobuf_full,
185 '//buildtools:protobuf_lite': enable_protobuf_lite,
186 '//buildtools:protoc_lib': enable_protoc_lib,
Florian Mayera2fae262018-08-31 12:10:01 -0700187 '//buildtools:libunwindstack': enable_libunwindstack,
Lalit Maganti17aa2732019-02-08 15:47:26 +0000188 '//buildtools:sqlite': enable_sqlite,
Hector Dearmane0b993f2019-05-24 18:48:16 +0100189 '//buildtools:zlib': enable_zlib,
Sami Kyostila865d1d32017-12-12 18:37:04 +0000190}
191
192# ----------------------------------------------------------------------------
193# End of configuration.
194# ----------------------------------------------------------------------------
195
196
197class Error(Exception):
198 pass
199
200
201class ThrowingArgumentParser(argparse.ArgumentParser):
202 def __init__(self, context):
203 super(ThrowingArgumentParser, self).__init__()
204 self.context = context
205
206 def error(self, message):
207 raise Error('%s: %s' % (self.context, message))
208
209
Lalit Magantiedace412019-06-18 13:28:28 +0100210def write_blueprint_key_value(output, name, value, sort=True):
211 """Writes an Blueprint key-value pair to the output"""
212
213 if not value:
214 return
215 if isinstance(value, set):
216 value = sorted(value)
217 if isinstance(value, list):
218 output.append(' %s: [' % name)
219 for item in sorted(value) if sort else value:
220 output.append(' "%s",' % item)
221 output.append(' ],')
222 return
223 if isinstance(value, bool):
224 output.append(' %s: true,' % name)
225 return
226 if isinstance(value, Target):
227 value.to_string(output)
228 return
229 output.append(' %s: "%s",' % (name, value))
230
231class Target(object):
232 """A target-scoped part of a module"""
233
234 def __init__(self, name):
235 self.name = name
236 self.shared_libs = []
237 self.static_libs = []
238 self.cflags = set()
239
240 def to_string(self, output):
241 nested_out = []
242 self._output_field(nested_out, 'shared_libs')
243 self._output_field(nested_out, 'static_libs')
244 self._output_field(nested_out, 'cflags')
245
246 if nested_out:
247 output.append(' %s: {' % self.name)
248 for line in nested_out:
249 output.append(' %s' % line)
250 output.append(' },')
251
252 def _output_field(self, output, name, sort=True):
253 value = getattr(self, name)
254 return write_blueprint_key_value(output, name, value, sort)
255
Sami Kyostila865d1d32017-12-12 18:37:04 +0000256class Module(object):
257 """A single module (e.g., cc_binary, cc_test) in a blueprint."""
258
259 def __init__(self, mod_type, name):
260 self.type = mod_type
261 self.name = name
262 self.srcs = []
263 self.comment = None
264 self.shared_libs = []
265 self.static_libs = []
266 self.tools = []
267 self.cmd = None
Primiano Tucci6aa75572018-03-21 05:33:14 -0700268 self.host_supported = False
Primiano Tucci6067e732018-01-08 16:19:40 +0000269 self.init_rc = []
Sami Kyostila865d1d32017-12-12 18:37:04 +0000270 self.out = []
271 self.export_include_dirs = []
272 self.generated_headers = []
Lalit Magantic5bcd792018-01-12 18:38:11 +0000273 self.export_generated_headers = []
Sami Kyostila865d1d32017-12-12 18:37:04 +0000274 self.defaults = []
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000275 self.cflags = set()
Sami Kyostila865d1d32017-12-12 18:37:04 +0000276 self.local_include_dirs = []
Ryan Savitskie65beca2019-01-29 18:29:13 +0000277 self.include_dirs = []
278 self.required = []
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100279 self.user_debug_flag = False
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100280 self.tool_files = None
Lalit Magantiedace412019-06-18 13:28:28 +0100281 self.android = Target('android')
282 self.host = Target('host')
Florian Mayer19f734f2019-07-05 12:08:01 +0100283 self.lto = None
Sami Kyostila865d1d32017-12-12 18:37:04 +0000284
285 def to_string(self, output):
286 if self.comment:
287 output.append('// %s' % self.comment)
288 output.append('%s {' % self.type)
289 self._output_field(output, 'name')
290 self._output_field(output, 'srcs')
291 self._output_field(output, 'shared_libs')
292 self._output_field(output, 'static_libs')
293 self._output_field(output, 'tools')
294 self._output_field(output, 'cmd', sort=False)
Primiano Tucci6aa75572018-03-21 05:33:14 -0700295 self._output_field(output, 'host_supported')
Primiano Tucci6067e732018-01-08 16:19:40 +0000296 self._output_field(output, 'init_rc')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000297 self._output_field(output, 'out')
298 self._output_field(output, 'export_include_dirs')
299 self._output_field(output, 'generated_headers')
Lalit Magantic5bcd792018-01-12 18:38:11 +0000300 self._output_field(output, 'export_generated_headers')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000301 self._output_field(output, 'defaults')
302 self._output_field(output, 'cflags')
303 self._output_field(output, 'local_include_dirs')
Ryan Savitskie65beca2019-01-29 18:29:13 +0000304 self._output_field(output, 'include_dirs')
305 self._output_field(output, 'required')
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100306 self._output_field(output, 'tool_files')
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100307
Lalit Magantiedace412019-06-18 13:28:28 +0100308 target_out = []
309 self._output_field(target_out, 'android')
310 self._output_field(target_out, 'host')
311 if target_out:
312 output.append(' target: {')
313 for line in target_out:
314 output.append(' %s' % line)
315 output.append(' },')
316
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100317 disable_pdk = any(name in library_not_in_pdk for name in self.shared_libs)
318 if self.user_debug_flag or disable_pdk:
Logan Chien9bfaaf92018-02-13 18:49:24 +0800319 output.append(' product_variables: {')
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100320 if disable_pdk:
321 output.append(' pdk: {')
322 output.append(' enabled: false,')
323 output.append(' },')
324 if self.user_debug_flag:
325 output.append(' debuggable: {')
326 output.append(' cflags: ["-DPERFETTO_BUILD_WITH_ANDROID_USERDEBUG"],')
327 output.append(' },')
Logan Chien9bfaaf92018-02-13 18:49:24 +0800328 output.append(' },')
Florian Mayer19f734f2019-07-05 12:08:01 +0100329 if self.lto is not None:
330 output.append(' target: {')
331 output.append(' android: {')
332 output.append(' lto: {')
333 output.append(' thin: %s,' % 'true' if self.lto else 'false')
334 output.append(' },')
335 output.append(' },')
336 output.append(' },')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000337 output.append('}')
338 output.append('')
339
340 def _output_field(self, output, name, sort=True):
341 value = getattr(self, name)
Lalit Magantiedace412019-06-18 13:28:28 +0100342 return write_blueprint_key_value(output, name, value, sort)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000343
344
345class Blueprint(object):
346 """In-memory representation of an Android.bp file."""
347
348 def __init__(self):
349 self.modules = {}
350
351 def add_module(self, module):
352 """Adds a new module to the blueprint, replacing any existing module
353 with the same name.
354
355 Args:
356 module: Module instance.
357 """
358 self.modules[module.name] = module
359
360 def to_string(self, output):
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000361 for m in sorted(self.modules.itervalues(), key=lambda m: m.name):
Sami Kyostila865d1d32017-12-12 18:37:04 +0000362 m.to_string(output)
363
364
Sami Kyostila865d1d32017-12-12 18:37:04 +0000365def label_to_module_name(label):
366 """Turn a GN label (e.g., //:perfetto_tests) into a module name."""
Primiano Tucci4e49c022017-12-21 18:22:44 +0100367 module = re.sub(r'^//:?', '', label)
368 module = re.sub(r'[^a-zA-Z0-9_]', '_', module)
369 if not module.startswith(module_prefix) and label not in default_targets:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000370 return module_prefix + module
371 return module
372
373
Sami Kyostila865d1d32017-12-12 18:37:04 +0000374def is_supported_source_file(name):
375 """Returns True if |name| can appear in a 'srcs' list."""
376 return os.path.splitext(name)[1] in ['.c', '.cc', '.proto']
377
378
379def is_generated_by_action(desc, label):
380 """Checks if a label is generated by an action.
381
382 Returns True if a GN output label |label| is an output for any action,
383 i.e., the file is generated dynamically.
384 """
385 for target in desc.itervalues():
386 if target['type'] == 'action' and label in target['outputs']:
387 return True
388 return False
389
390
391def apply_module_dependency(blueprint, desc, module, dep_name):
392 """Recursively collect dependencies for a given module.
393
394 Walk the transitive dependencies for a GN target and apply them to a given
395 module. This effectively flattens the dependency tree so that |module|
396 directly contains all the sources, libraries, etc. in the corresponding GN
397 dependency tree.
398
399 Args:
400 blueprint: Blueprint instance which is being generated.
401 desc: JSON GN description.
402 module: Module to which dependencies should be added.
403 dep_name: GN target of the dependency.
404 """
Sami Kyostila865d1d32017-12-12 18:37:04 +0000405 # If the dependency refers to a library which we can replace with an Android
406 # equivalent, stop recursing and patch the dependency in.
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100407 if gn_utils.label_without_toolchain(dep_name) in builtin_deps:
408 builtin_deps[gn_utils.label_without_toolchain(dep_name)](module)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000409 return
410
411 # Similarly some shared libraries are directly mapped to Android
412 # equivalents.
413 target = desc[dep_name]
414 for lib in target.get('libs', []):
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100415 # Generally library names sould be mangled as 'libXXX', unless they are
416 # HAL libraries (e.g., android.hardware.health@2.0).
417 android_lib = lib if '@' in lib else 'lib' + lib
Sami Kyostila865d1d32017-12-12 18:37:04 +0000418 if lib in library_whitelist and not android_lib in module.shared_libs:
419 module.shared_libs.append(android_lib)
420
421 type = target['type']
422 if type == 'action':
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100423 if "gen_merged_sql_metrics" in dep_name:
424 dep_mod = create_merged_sql_metrics_target(blueprint, desc, dep_name)
425 module.generated_headers.append(dep_mod.name)
426 else:
427 create_modules_from_target(blueprint, desc, dep_name)
428 # Depend both on the generated sources and headers -- see
429 # make_genrules_for_action.
430 module.srcs.append(':' + label_to_module_name(dep_name))
431 module.generated_headers.append(
432 label_to_module_name(dep_name) + '_headers')
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000433 elif type == 'static_library' and label_to_module_name(
434 dep_name) != module.name:
435 create_modules_from_target(blueprint, desc, dep_name)
436 module.static_libs.append(label_to_module_name(dep_name))
Primiano Tucci6067e732018-01-08 16:19:40 +0000437 elif type == 'shared_library' and label_to_module_name(
438 dep_name) != module.name:
439 module.shared_libs.append(label_to_module_name(dep_name))
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000440 elif type in ['group', 'source_set', 'executable', 'static_library'
441 ] and 'sources' in target:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000442 # Ignore source files that are generated by actions since they will be
443 # implicitly added by the genrule dependencies.
444 module.srcs.extend(
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100445 gn_utils.label_to_path(src) for src in target['sources']
Sami Kyostila865d1d32017-12-12 18:37:04 +0000446 if is_supported_source_file(src)
447 and not is_generated_by_action(desc, src))
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000448 module.cflags |= _get_cflags(target)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000449
450
451def make_genrules_for_action(blueprint, desc, target_name):
452 """Generate genrules for a GN action.
453
454 GN actions are used to dynamically generate files during the build. The
455 Soong equivalent is a genrule. This function turns a specific kind of
456 genrule which turns .proto files into source and header files into a pair
Sami Kyostila71625d72017-12-18 10:29:49 +0000457 equivalent genrules.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000458
459 Args:
460 blueprint: Blueprint instance which is being generated.
461 desc: JSON GN description.
462 target_name: GN target for genrule generation.
463
464 Returns:
465 A (source_genrule, header_genrule) module tuple.
466 """
467 target = desc[target_name]
468
469 # We only support genrules which call protoc (with or without a plugin) to
470 # turn .proto files into header and source files.
471 args = target['args']
Lalit Magantie87838f2019-06-25 18:31:49 +0100472 if '/protoc' not in args[0]:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000473 raise Error('Unsupported action in target %s: %s' % (target_name,
474 target['args']))
Primiano Tucci20b760c2018-01-19 12:36:12 +0000475 parser = ThrowingArgumentParser('Action in target %s (%s)' %
476 (target_name, ' '.join(target['args'])))
477 parser.add_argument('--proto_path')
478 parser.add_argument('--cpp_out')
479 parser.add_argument('--plugin')
480 parser.add_argument('--plugin_out')
Florian Mayerab55e3d2018-04-19 11:56:01 +0100481 parser.add_argument('--descriptor_set_out')
Lalit Magantidfe69ca2018-10-30 12:18:23 +0000482 parser.add_argument('--include_imports', action='store_true')
Primiano Tucci20b760c2018-01-19 12:36:12 +0000483 parser.add_argument('protos', nargs=argparse.REMAINDER)
Lalit Magantie87838f2019-06-25 18:31:49 +0100484 args = parser.parse_args(args[2:])
Primiano Tucci20b760c2018-01-19 12:36:12 +0000485
486 # Depending on whether we are using the default protoc C++ generator or the
487 # protozero plugin, the output dir is passed as:
488 # --cpp_out=gen/xxx or
489 # --plugin_out=:gen/xxx or
490 # --plugin_out=wrapper_namespace=pbzero:gen/xxx
491 gen_dir = args.cpp_out if args.cpp_out else args.plugin_out.split(':')[1]
492 assert gen_dir.startswith('gen/')
493 gen_dir = gen_dir[4:]
494 cpp_out_dir = ('$(genDir)/%s/%s' % (tree_path, gen_dir)).rstrip('/')
495
496 # TODO(skyostil): Is there a way to avoid hardcoding the tree path here?
497 # TODO(skyostil): Find a way to avoid creating the directory.
498 cmd = [
499 'mkdir -p %s &&' % cpp_out_dir,
500 '$(location aprotoc)',
501 '--cpp_out=%s' % cpp_out_dir
502 ]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000503
504 # We create two genrules for each action: one for the protobuf headers and
505 # another for the sources. This is because the module that depends on the
506 # generated files needs to declare two different types of dependencies --
507 # source files in 'srcs' and headers in 'generated_headers' -- and it's not
508 # valid to generate .h files from a source dependency and vice versa.
Sami Kyostila71625d72017-12-18 10:29:49 +0000509 source_module = Module('genrule', label_to_module_name(target_name))
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100510 source_module.srcs.extend(
511 gn_utils.label_to_path(src) for src in target['sources'])
Sami Kyostila865d1d32017-12-12 18:37:04 +0000512 source_module.tools = ['aprotoc']
513
Sami Kyostila71625d72017-12-18 10:29:49 +0000514 header_module = Module('genrule',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000515 label_to_module_name(target_name) + '_headers')
516 header_module.srcs = source_module.srcs[:]
517 header_module.tools = source_module.tools[:]
Primiano Tucci20b760c2018-01-19 12:36:12 +0000518 header_module.export_include_dirs = [gen_dir or '.']
Sami Kyostila865d1d32017-12-12 18:37:04 +0000519
Primiano Tucci20b760c2018-01-19 12:36:12 +0000520 # In GN builds the proto path is always relative to the output directory
521 # (out/tmp.xxx).
522 assert args.proto_path.startswith('../../')
523 cmd += [ '--proto_path=%s/%s' % (tree_path, args.proto_path[6:])]
524
Sami Kyostila865d1d32017-12-12 18:37:04 +0000525 namespaces = ['pb']
Sami Kyostila865d1d32017-12-12 18:37:04 +0000526 if args.plugin:
527 _, plugin = os.path.split(args.plugin)
528 # TODO(skyostil): Can we detect this some other way?
529 if plugin == 'ipc_plugin':
530 namespaces.append('ipc')
Primiano Tucci764c5042019-06-22 18:28:45 +0100531 elif plugin == 'protozero_plugin':
Sami Kyostila865d1d32017-12-12 18:37:04 +0000532 namespaces = ['pbzero']
533 for dep in target['deps']:
534 if desc[dep]['type'] != 'executable':
535 continue
536 _, executable = os.path.split(desc[dep]['outputs'][0])
537 if executable == plugin:
538 cmd += [
539 '--plugin=protoc-gen-plugin=$(location %s)' %
540 label_to_module_name(dep)
541 ]
542 source_module.tools.append(label_to_module_name(dep))
543 # Also make sure the module for the tool is generated.
544 create_modules_from_target(blueprint, desc, dep)
545 break
546 else:
547 raise Error('Unrecognized protoc plugin in target %s: %s' %
548 (target_name, args[i]))
549 if args.plugin_out:
550 plugin_args = args.plugin_out.split(':')[0]
Primiano Tucci20b760c2018-01-19 12:36:12 +0000551 cmd += ['--plugin_out=%s:%s' % (plugin_args, cpp_out_dir)]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000552
553 cmd += ['$(in)']
554 source_module.cmd = ' '.join(cmd)
555 header_module.cmd = source_module.cmd
556 header_module.tools = source_module.tools[:]
557
558 for ns in namespaces:
559 source_module.out += [
560 '%s/%s' % (tree_path, src.replace('.proto', '.%s.cc' % ns))
561 for src in source_module.srcs
562 ]
563 header_module.out += [
564 '%s/%s' % (tree_path, src.replace('.proto', '.%s.h' % ns))
565 for src in header_module.srcs
566 ]
567 return source_module, header_module
568
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100569
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100570def create_merged_sql_metrics_target(blueprint, desc, gn_target_name):
571 target_desc = desc[gn_target_name]
572 module = Module(
573 'genrule',
574 'gen_merged_sql_metrics',
575 )
576 module.tool_files = [
Lalit Maganti697cc482019-05-01 14:39:11 +0100577 'tools/gen_merged_sql_metrics.py',
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100578 ]
Lalit Maganti697cc482019-05-01 14:39:11 +0100579 module.cmd = ' '.join([
580 '$(location tools/gen_merged_sql_metrics.py)',
581 '--cpp_out=$(out)',
582 '$(in)',
583 ])
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100584 module.out = set(
585 src[src.index('gen/') + len('gen/'):]
586 for src in target_desc.get('outputs', [])
587 )
588 module.srcs.extend(
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100589 gn_utils.label_to_path(src)
Lalit Maganti26f69bd2019-04-29 18:23:47 +0100590 for src in target_desc.get('inputs', [])
591 )
592 blueprint.add_module(module)
593 return module
Sami Kyostila865d1d32017-12-12 18:37:04 +0000594
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100595
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000596def _get_cflags(target):
597 cflags = set(flag for flag in target.get('cflags', [])
598 if re.match(cflag_whitelist, flag))
599 cflags |= set("-D%s" % define for define in target.get('defines', [])
600 if re.match(define_whitelist, define))
601 return cflags
602
603
Sami Kyostila865d1d32017-12-12 18:37:04 +0000604def create_modules_from_target(blueprint, desc, target_name):
605 """Generate module(s) for a given GN target.
606
607 Given a GN target name, generate one or more corresponding modules into a
608 blueprint.
609
610 Args:
611 blueprint: Blueprint instance which is being generated.
612 desc: JSON GN description.
613 target_name: GN target for module generation.
614 """
615 target = desc[target_name]
616 if target['type'] == 'executable':
Primiano Tucci21c19d82018-03-29 12:35:08 +0100617 if 'host' in target['toolchain'] or target_name in target_host_only:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000618 module_type = 'cc_binary_host'
Lalit Magantiedace412019-06-18 13:28:28 +0100619 elif target.get('testonly') and target_name not in non_test_binaries:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000620 module_type = 'cc_test'
621 else:
622 module_type = 'cc_binary'
623 modules = [Module(module_type, label_to_module_name(target_name))]
624 elif target['type'] == 'action':
625 modules = make_genrules_for_action(blueprint, desc, target_name)
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000626 elif target['type'] == 'static_library':
Lalit Magantic5bcd792018-01-12 18:38:11 +0000627 module = Module('cc_library_static', label_to_module_name(target_name))
628 module.export_include_dirs = ['include']
629 modules = [module]
Primiano Tucci6067e732018-01-08 16:19:40 +0000630 elif target['type'] == 'shared_library':
631 modules = [
632 Module('cc_library_shared', label_to_module_name(target_name))
633 ]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000634 else:
635 raise Error('Unknown target type: %s' % target['type'])
636
637 for module in modules:
638 module.comment = 'GN target: %s' % target_name
Primiano Tucci6067e732018-01-08 16:19:40 +0000639 if target_name in target_initrc:
640 module.init_rc = [target_initrc[target_name]]
Primiano Tucci6aa75572018-03-21 05:33:14 -0700641 if target_name in target_host_supported:
642 module.host_supported = True
Primiano Tucci6067e732018-01-08 16:19:40 +0000643
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000644 # Don't try to inject library/source dependencies into genrules because
645 # they are not compiled in the traditional sense.
Sami Kyostila71625d72017-12-18 10:29:49 +0000646 if module.type != 'genrule':
Sami Kyostila865d1d32017-12-12 18:37:04 +0000647 module.defaults = [defaults_module]
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000648 apply_module_dependency(blueprint, desc, module, target_name)
649 for dep in resolve_dependencies(desc, target_name):
650 apply_module_dependency(blueprint, desc, module, dep)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000651
Lalit Magantic5bcd792018-01-12 18:38:11 +0000652 # If the module is a static library, export all the generated headers.
653 if module.type == 'cc_library_static':
654 module.export_generated_headers = module.generated_headers
655
Ryan Savitskie65beca2019-01-29 18:29:13 +0000656 # Merge in additional hardcoded arguments.
657 for key, add_val in additional_args.get(module.name, []):
658 curr = getattr(module, key)
659 if add_val and isinstance(add_val, list) and isinstance(curr, list):
660 curr.extend(add_val)
661 else:
662 raise Error('Unimplemented type of additional_args')
663
Sami Kyostila865d1d32017-12-12 18:37:04 +0000664 blueprint.add_module(module)
665
666
667def resolve_dependencies(desc, target_name):
668 """Return the transitive set of dependent-on targets for a GN target.
669
670 Args:
671 blueprint: Blueprint instance which is being generated.
672 desc: JSON GN description.
673
674 Returns:
675 A set of transitive dependencies in the form of GN targets.
676 """
677
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100678 if gn_utils.label_without_toolchain(target_name) in builtin_deps:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000679 return set()
680 target = desc[target_name]
681 resolved_deps = set()
682 for dep in target.get('deps', []):
683 resolved_deps.add(dep)
684 # Ignore the transitive dependencies of actions because they are
685 # explicitly converted to genrules.
686 if desc[dep]['type'] == 'action':
687 continue
Primiano Tucci6067e732018-01-08 16:19:40 +0000688 # Dependencies on shared libraries shouldn't propagate any transitive
689 # dependencies but only depend on the shared library target
690 if desc[dep]['type'] == 'shared_library':
691 continue
Sami Kyostila865d1d32017-12-12 18:37:04 +0000692 resolved_deps.update(resolve_dependencies(desc, dep))
693 return resolved_deps
694
695
Ryan Savitski160b5822019-02-28 14:42:50 +0000696def create_blueprint_for_targets(desc, targets):
Sami Kyostila865d1d32017-12-12 18:37:04 +0000697 """Generate a blueprint for a list of GN targets."""
698 blueprint = Blueprint()
699
700 # Default settings used by all modules.
701 defaults = Module('cc_defaults', defaults_module)
702 defaults.local_include_dirs = ['include']
703 defaults.cflags = [
704 '-Wno-error=return-type',
705 '-Wno-sign-compare',
706 '-Wno-sign-promo',
707 '-Wno-unused-parameter',
Florian Mayercc424fd2018-01-15 11:19:01 +0000708 '-fvisibility=hidden',
Florian Mayerc2a38ea2018-01-19 11:48:43 +0000709 '-Oz',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000710 ]
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100711 defaults.user_debug_flag = True
Florian Mayer19f734f2019-07-05 12:08:01 +0100712 defaults.lto = True
Sami Kyostila865d1d32017-12-12 18:37:04 +0000713
714 blueprint.add_module(defaults)
715 for target in targets:
716 create_modules_from_target(blueprint, desc, target)
717 return blueprint
718
719
720def main():
721 parser = argparse.ArgumentParser(
722 description='Generate Android.bp from a GN description.')
723 parser.add_argument(
Sami Kyostilab27619f2017-12-13 19:22:16 +0000724 '--desc',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000725 help=
726 'GN description (e.g., gn desc out --format=json --all-toolchains "//*"'
727 )
728 parser.add_argument(
Lalit Magantic5bcd792018-01-12 18:38:11 +0000729 '--extras',
730 help='Extra targets to include at the end of the Blueprint file',
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100731 default=os.path.join(gn_utils.repo_root(), 'Android.bp.extras'),
Lalit Magantic5bcd792018-01-12 18:38:11 +0000732 )
733 parser.add_argument(
Sami Kyostilab27619f2017-12-13 19:22:16 +0000734 '--output',
735 help='Blueprint file to create',
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100736 default=os.path.join(gn_utils.repo_root(), 'Android.bp'),
Sami Kyostilab27619f2017-12-13 19:22:16 +0000737 )
738 parser.add_argument(
Sami Kyostila865d1d32017-12-12 18:37:04 +0000739 'targets',
740 nargs=argparse.REMAINDER,
741 help='Targets to include in the blueprint (e.g., "//:perfetto_tests")')
742 args = parser.parse_args()
743
Sami Kyostilab27619f2017-12-13 19:22:16 +0000744 if args.desc:
745 with open(args.desc) as f:
746 desc = json.load(f)
747 else:
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100748 desc = gn_utils.create_build_description(gn_args)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000749
Ryan Savitski160b5822019-02-28 14:42:50 +0000750 blueprint = create_blueprint_for_targets(desc, args.targets or
751 default_targets)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000752 output = [
753 """// Copyright (C) 2017 The Android Open Source Project
754//
755// Licensed under the Apache License, Version 2.0 (the "License");
756// you may not use this file except in compliance with the License.
757// You may obtain a copy of the License at
758//
759// http://www.apache.org/licenses/LICENSE-2.0
760//
761// Unless required by applicable law or agreed to in writing, software
762// distributed under the License is distributed on an "AS IS" BASIS,
763// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
764// See the License for the specific language governing permissions and
765// limitations under the License.
766//
767// This file is automatically generated by %s. Do not edit.
768""" % (__file__)
769 ]
770 blueprint.to_string(output)
Lalit Magantic5bcd792018-01-12 18:38:11 +0000771 with open(args.extras, 'r') as r:
772 for line in r:
773 output.append(line.rstrip("\n\r"))
Sami Kyostilab27619f2017-12-13 19:22:16 +0000774 with open(args.output, 'w') as f:
775 f.write('\n'.join(output))
Sami Kyostila865d1d32017-12-12 18:37:04 +0000776
777
778if __name__ == '__main__':
779 sys.exit(main())