blob: 2d1203b1af711501569755741695d79b42b7586c [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
Matthew Clarkson9a5dfa52019-10-03 09:54:04 +010036from compat import itervalues
37
Florian Mayer246c1422019-09-18 15:40:38 +010038ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
39
Sami Kyostilab27619f2017-12-13 19:22:16 +000040# Arguments for the GN output directory.
Primiano Tucci9c411652019-08-27 07:13:59 +020041gn_args = ' '.join([
42 'is_debug=false',
Primiano Tucci7e05fc12019-08-27 17:29:47 +020043 'is_perfetto_build_generator=true',
Primiano Tucci9c411652019-08-27 07:13:59 +020044 'perfetto_build_with_android=true',
45 'target_cpu="arm"',
46 'target_os="android"',
47])
Sami Kyostilab27619f2017-12-13 19:22:16 +000048
Primiano Tucci02c11762019-08-30 00:57:59 +020049# Default targets to translate to the blueprint file.
50default_targets = [
51 '//:libperfetto_client_experimental',
52 '//:libperfetto',
53 '//:perfetto_integrationtests',
54 '//:perfetto_unittests',
55 '//protos/perfetto/trace:perfetto_trace_protos',
56 '//src/android_internal:libperfetto_android_internal',
57 '//src/perfetto_cmd:perfetto',
58 '//src/perfetto_cmd:trigger_perfetto',
59 '//src/profiling/memory:heapprofd_client',
60 '//src/profiling/memory:heapprofd',
Primiano Tucci02c11762019-08-30 00:57:59 +020061 '//src/traced/probes:traced_probes',
62 '//src/traced/service:traced',
Primiano Tuccif0d7ef82019-10-04 15:35:24 +010063]
64
65# Host targets
66ipc_plugin = '//src/ipc/protoc_plugin:ipc_plugin(%s)' % gn_utils.HOST_TOOLCHAIN
67protozero_plugin = '//src/protozero/protoc_plugin:protozero_plugin(%s)' % (
68 gn_utils.HOST_TOOLCHAIN)
Primiano Tucci57dd66b2019-10-15 23:09:04 +010069cppgen_plugin = '//src/protozero/protoc_plugin:cppgen_plugin(%s)' % (
70 gn_utils.HOST_TOOLCHAIN)
71
Primiano Tuccif0d7ef82019-10-04 15:35:24 +010072default_targets += [
73 '//src/trace_processor:trace_processor_shell(%s)' % gn_utils.HOST_TOOLCHAIN,
74 '//tools/trace_to_text:trace_to_text(%s)' % gn_utils.HOST_TOOLCHAIN,
75 protozero_plugin,
76 ipc_plugin,
Primiano Tucci02c11762019-08-30 00:57:59 +020077]
78
Primiano Tucci02c11762019-08-30 00:57:59 +020079# Defines a custom init_rc argument to be applied to the corresponding output
80# blueprint target.
81target_initrc = {
Primiano Tuccif0d7ef82019-10-04 15:35:24 +010082 '//src/traced/service:traced': {'perfetto.rc'},
83 '//src/profiling/memory:heapprofd': {'heapprofd.rc'},
Primiano Tucci02c11762019-08-30 00:57:59 +020084}
85
86target_host_supported = [
87 '//protos/perfetto/trace:perfetto_trace_protos',
Hector Dearman04cfac72019-09-24 22:05:55 +010088 '//:libperfetto',
Primiano Tucci02c11762019-08-30 00:57:59 +020089]
90
Sami Kyostila865d1d32017-12-12 18:37:04 +000091# All module names are prefixed with this string to avoid collisions.
92module_prefix = 'perfetto_'
93
94# Shared libraries which are directly translated to Android system equivalents.
95library_whitelist = [
Lalit Maganti8eba3092019-03-27 13:25:29 +000096 "android.hardware.atrace@1.0",
Primiano Tucci676f0cc2018-12-03 20:03:26 +010097 'android.hardware.health@2.0',
Esteban Talavera1fecac92019-01-09 16:06:29 +000098 "android.hardware.power.stats@1.0",
Sami Kyostila865d1d32017-12-12 18:37:04 +000099 'android',
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100100 'base',
Sami Kyostilab5b71692018-01-12 12:16:44 +0000101 'binder',
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100102 'hidlbase',
103 'hidltransport',
104 'hwbinder',
Ryan Savitski53ca60b2019-06-03 13:04:40 +0100105 'incident',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000106 'log',
Sami Kyostilab5b71692018-01-12 12:16:44 +0000107 'services',
Primiano Tucciedf099c2018-01-08 18:27:56 +0000108 'utils',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000109]
110
111# Name of the module which settings such as compiler flags for all other
112# modules.
113defaults_module = module_prefix + 'defaults'
114
115# Location of the project in the Android source tree.
116tree_path = 'external/perfetto'
117
Primiano Tucciedf099c2018-01-08 18:27:56 +0000118# Compiler flags which are passed through to the blueprint.
119cflag_whitelist = r'^-DPERFETTO.*$'
120
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000121# Compiler defines which are passed through to the blueprint.
Primiano Tucci8e627442019-08-28 07:58:38 +0200122define_whitelist = r'^(GOOGLE_PROTO.*)|(ZLIB_.*)|(USE_MMAP)|(HAVE_HIDDEN)$'
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000123
Logan Chien9bfaaf92018-02-13 18:49:24 +0800124# Shared libraries which are not in PDK.
125library_not_in_pdk = {
126 'libandroid',
127 'libservices',
128}
129
Primiano Tucci8e627442019-08-28 07:58:38 +0200130# The directory where the generated perfetto_build_flags.h will be copied into.
131buildflags_dir = 'include/perfetto/base/build_configs/android_tree'
132
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100133
134def enumerate_data_deps():
135 with open(os.path.join(ROOT_DIR, 'tools', 'test_data.txt')) as f:
136 lines = f.readlines()
137 for line in (line.strip() for line in lines if not line.startswith('#')):
138 assert os.path.exists(line), line
139 if line.endswith('/'):
140 yield line + '**/*'
141 else:
142 yield line
143
144
Florian Mayerb6a921f2018-10-18 18:55:23 +0100145# Additional arguments to apply to Android.bp rules.
146additional_args = {
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100147 'heapprofd_client': [
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100148 ('include_dirs', {'bionic/libc'}),
149 ('static_libs', {'libasync_safe'}),
150 ('header_libs', {'bionic_libc_platform_headers'}),
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100151 ],
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100152 'perfetto_unittests': [('data', set(enumerate_data_deps())),],
Matthew Clarkson63028d62019-10-10 15:48:23 +0100153 'traced_probes': [
154 ('required', {'libperfetto_android_internal', 'trigger_perfetto'}),
155 ],
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100156 'libperfetto_android_internal': [('static_libs', {'libhealthhalutils'}),],
Lalit Maganti3d415ec2019-10-23 17:53:17 +0100157 'trace_processor_shell': [('static_executable', True),],
Florian Mayerb6a921f2018-10-18 18:55:23 +0100158}
159
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100160
Sami Kyostila865d1d32017-12-12 18:37:04 +0000161def enable_gmock(module):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100162 module.static_libs.add('libgmock')
163
Sami Kyostila865d1d32017-12-12 18:37:04 +0000164
Sami Kyostila865d1d32017-12-12 18:37:04 +0000165def enable_protobuf_full(module):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100166 module.shared_libs.add('libprotobuf-cpp-full')
167
Sami Kyostila865d1d32017-12-12 18:37:04 +0000168
Sami Kyostila865d1d32017-12-12 18:37:04 +0000169def enable_protobuf_lite(module):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100170 module.shared_libs.add('libprotobuf-cpp-lite')
171
Sami Kyostila865d1d32017-12-12 18:37:04 +0000172
Sami Kyostila865d1d32017-12-12 18:37:04 +0000173def enable_protoc_lib(module):
Lalit Maganti3d415ec2019-10-23 17:53:17 +0100174 if module.type == 'cc_binary_host':
175 module.static_libs.add('libprotoc')
176 else:
177 module.shared_libs.add('libprotoc')
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100178
Sami Kyostila865d1d32017-12-12 18:37:04 +0000179
Florian Mayera2fae262018-08-31 12:10:01 -0700180def enable_libunwindstack(module):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100181 module.shared_libs.add('libunwindstack')
182 module.shared_libs.add('libprocinfo')
183 module.shared_libs.add('libbase')
184
Sami Kyostila865d1d32017-12-12 18:37:04 +0000185
186def enable_libunwind(module):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100187 # libunwind is disabled on Darwin so we cannot depend on it.
188 pass
189
Sami Kyostila865d1d32017-12-12 18:37:04 +0000190
Lalit Maganti17aa2732019-02-08 15:47:26 +0000191def enable_sqlite(module):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100192 if module.type == 'cc_binary_host':
193 module.static_libs.add('libsqlite')
194 else:
195 # Copy what the sqlite3 command line tool does.
196 module.android.shared_libs.add('libsqlite')
197 module.android.shared_libs.add('libandroidicu')
198 module.android.shared_libs.add('liblog')
199 module.android.shared_libs.add('libutils')
200 module.host.static_libs.add('libsqlite')
201
Lalit Maganti17aa2732019-02-08 15:47:26 +0000202
Hector Dearmane0b993f2019-05-24 18:48:16 +0100203def enable_zlib(module):
Lalit Maganti3d415ec2019-10-23 17:53:17 +0100204 if module.type == 'cc_binary_host':
205 module.static_libs.add('libz')
206 else:
207 module.shared_libs.add('libz')
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100208
Hector Dearmane0b993f2019-05-24 18:48:16 +0100209
Sami Kyostila865d1d32017-12-12 18:37:04 +0000210# Android equivalents for third-party libraries that the upstream project
211# depends on.
212builtin_deps = {
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100213 '//gn:default_deps': lambda x: None,
214 '//gn:gtest_main': lambda x: None,
215 '//gn:protoc': lambda x: None,
Primiano Tuccib7ebffd2019-09-09 07:42:35 -0700216 '//gn:gtest_and_gmock': enable_gmock,
217 '//gn:libunwind': enable_libunwind,
218 '//gn:protobuf_full': enable_protobuf_full,
219 '//gn:protobuf_lite': enable_protobuf_lite,
220 '//gn:protoc_lib': enable_protoc_lib,
221 '//gn:libunwindstack': enable_libunwindstack,
222 '//gn:sqlite': enable_sqlite,
223 '//gn:zlib': enable_zlib,
Sami Kyostila865d1d32017-12-12 18:37:04 +0000224}
225
226# ----------------------------------------------------------------------------
227# End of configuration.
228# ----------------------------------------------------------------------------
229
230
231class Error(Exception):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100232 pass
Sami Kyostila865d1d32017-12-12 18:37:04 +0000233
234
235class ThrowingArgumentParser(argparse.ArgumentParser):
Sami Kyostila865d1d32017-12-12 18:37:04 +0000236
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100237 def __init__(self, context):
238 super(ThrowingArgumentParser, self).__init__()
239 self.context = context
240
241 def error(self, message):
242 raise Error('%s: %s' % (self.context, message))
Sami Kyostila865d1d32017-12-12 18:37:04 +0000243
244
Lalit Magantiedace412019-06-18 13:28:28 +0100245def write_blueprint_key_value(output, name, value, sort=True):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100246 """Writes a Blueprint key-value pair to the output"""
Lalit Magantiedace412019-06-18 13:28:28 +0100247
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100248 if not value:
249 return
250 if isinstance(value, set):
251 value = sorted(value)
252 if isinstance(value, list):
253 output.append(' %s: [' % name)
254 for item in sorted(value) if sort else value:
255 output.append(' "%s",' % item)
256 output.append(' ],')
257 return
258 if isinstance(value, bool):
259 output.append(' %s: true,' % name)
260 return
261 if isinstance(value, Target):
262 value.to_string(output)
263 return
264 output.append(' %s: "%s",' % (name, value))
265
Lalit Magantiedace412019-06-18 13:28:28 +0100266
267class Target(object):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100268 """A target-scoped part of a module"""
Lalit Magantiedace412019-06-18 13:28:28 +0100269
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100270 def __init__(self, name):
271 self.name = name
272 self.shared_libs = set()
273 self.static_libs = set()
274 self.cflags = set()
Lalit Magantiedace412019-06-18 13:28:28 +0100275
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100276 def to_string(self, output):
277 nested_out = []
278 self._output_field(nested_out, 'shared_libs')
279 self._output_field(nested_out, 'static_libs')
280 self._output_field(nested_out, 'cflags')
Lalit Magantiedace412019-06-18 13:28:28 +0100281
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100282 if nested_out:
283 output.append(' %s: {' % self.name)
284 for line in nested_out:
285 output.append(' %s' % line)
286 output.append(' },')
Lalit Magantiedace412019-06-18 13:28:28 +0100287
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100288 def _output_field(self, output, name, sort=True):
289 value = getattr(self, name)
290 return write_blueprint_key_value(output, name, value, sort)
291
Lalit Magantiedace412019-06-18 13:28:28 +0100292
Sami Kyostila865d1d32017-12-12 18:37:04 +0000293class Module(object):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100294 """A single module (e.g., cc_binary, cc_test) in a blueprint."""
Sami Kyostila865d1d32017-12-12 18:37:04 +0000295
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100296 def __init__(self, mod_type, name, gn_target):
297 self.type = mod_type
298 self.gn_target = gn_target
299 self.name = name
300 self.srcs = set()
301 self.comment = 'GN: ' + gn_utils.label_without_toolchain(gn_target)
302 self.shared_libs = set()
303 self.static_libs = set()
304 self.tools = set()
305 self.cmd = None
306 self.host_supported = False
307 self.init_rc = set()
308 self.out = set()
309 self.export_include_dirs = set()
310 self.generated_headers = set()
311 self.export_generated_headers = set()
312 self.defaults = set()
313 self.cflags = set()
314 self.include_dirs = set()
315 self.header_libs = set()
316 self.required = set()
317 self.user_debug_flag = False
318 self.tool_files = None
319 self.android = Target('android')
320 self.host = Target('host')
321 self.lto = None
Lalit Maganti3d415ec2019-10-23 17:53:17 +0100322 self.static_executable = False
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100323 self.data = set()
324 # The genrule_XXX below are properties that must to be propagated back
325 # on the module(s) that depend on the genrule.
326 self.genrule_headers = set()
327 self.genrule_srcs = set()
328 self.genrule_shared_libs = set()
Sami Kyostila865d1d32017-12-12 18:37:04 +0000329
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100330 def to_string(self, output):
331 if self.comment:
332 output.append('// %s' % self.comment)
333 output.append('%s {' % self.type)
334 self._output_field(output, 'name')
335 self._output_field(output, 'srcs')
336 self._output_field(output, 'shared_libs')
337 self._output_field(output, 'static_libs')
338 self._output_field(output, 'tools')
339 self._output_field(output, 'cmd', sort=False)
340 self._output_field(output, 'host_supported')
341 self._output_field(output, 'init_rc')
342 self._output_field(output, 'out')
343 self._output_field(output, 'export_include_dirs')
344 self._output_field(output, 'generated_headers')
345 self._output_field(output, 'export_generated_headers')
346 self._output_field(output, 'defaults')
347 self._output_field(output, 'cflags')
348 self._output_field(output, 'include_dirs')
349 self._output_field(output, 'header_libs')
350 self._output_field(output, 'required')
Lalit Maganti3d415ec2019-10-23 17:53:17 +0100351 self._output_field(output, 'static_executable')
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100352 self._output_field(output, 'tool_files')
353 self._output_field(output, 'data')
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100354
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100355 target_out = []
356 self._output_field(target_out, 'android')
357 self._output_field(target_out, 'host')
358 if target_out:
359 output.append(' target: {')
360 for line in target_out:
361 output.append(' %s' % line)
362 output.append(' },')
Lalit Magantiedace412019-06-18 13:28:28 +0100363
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100364 disable_pdk = any(name in library_not_in_pdk for name in self.shared_libs)
365 if self.user_debug_flag or disable_pdk:
366 output.append(' product_variables: {')
367 if disable_pdk:
368 output.append(' pdk: {')
369 output.append(' enabled: false,')
370 output.append(' },')
371 if self.user_debug_flag:
372 output.append(' debuggable: {')
373 output.append(
374 ' cflags: ["-DPERFETTO_BUILD_WITH_ANDROID_USERDEBUG"],')
375 output.append(' },')
376 output.append(' },')
377 if self.lto is not None:
378 output.append(' target: {')
379 output.append(' android: {')
380 output.append(' lto: {')
381 output.append(' thin: %s,' % 'true' if self.lto else 'false')
382 output.append(' },')
383 output.append(' },')
384 output.append(' },')
385 output.append('}')
386 output.append('')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000387
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100388 def _output_field(self, output, name, sort=True):
389 value = getattr(self, name)
390 return write_blueprint_key_value(output, name, value, sort)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000391
392
393class Blueprint(object):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100394 """In-memory representation of an Android.bp file."""
Sami Kyostila865d1d32017-12-12 18:37:04 +0000395
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100396 def __init__(self):
397 self.modules = {}
Sami Kyostila865d1d32017-12-12 18:37:04 +0000398
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100399 def add_module(self, module):
400 """Adds a new module to the blueprint, replacing any existing module
Sami Kyostila865d1d32017-12-12 18:37:04 +0000401 with the same name.
402
403 Args:
404 module: Module instance.
405 """
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100406 self.modules[module.name] = module
Sami Kyostila865d1d32017-12-12 18:37:04 +0000407
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100408 def to_string(self, output):
409 for m in sorted(itervalues(self.modules), key=lambda m: m.name):
410 m.to_string(output)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000411
412
Sami Kyostila865d1d32017-12-12 18:37:04 +0000413def label_to_module_name(label):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100414 """Turn a GN label (e.g., //:perfetto_tests) into a module name."""
415 # If the label is explicibly listed in the default target list, don't prefix
416 # its name and return just the target name. This is so tools like
417 # "trace_to_text" stay as such in the Android tree.
418 label_without_toolchain = gn_utils.label_without_toolchain(label)
419 if label in default_targets or label_without_toolchain in default_targets:
420 return label_without_toolchain.split(':')[-1]
Primiano Tucci02c11762019-08-30 00:57:59 +0200421
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100422 module = re.sub(r'^//:?', '', label_without_toolchain)
423 module = re.sub(r'[^a-zA-Z0-9_]', '_', module)
424 if not module.startswith(module_prefix):
425 return module_prefix + module
426 return module
Sami Kyostila865d1d32017-12-12 18:37:04 +0000427
428
Sami Kyostila865d1d32017-12-12 18:37:04 +0000429def is_supported_source_file(name):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100430 """Returns True if |name| can appear in a 'srcs' list."""
431 return os.path.splitext(name)[1] in ['.c', '.cc', '.proto']
Sami Kyostila865d1d32017-12-12 18:37:04 +0000432
433
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100434def create_proto_modules(blueprint, gn, target):
435 """Generate genrules for a proto GN target.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000436
437 GN actions are used to dynamically generate files during the build. The
438 Soong equivalent is a genrule. This function turns a specific kind of
439 genrule which turns .proto files into source and header files into a pair
Sami Kyostila71625d72017-12-18 10:29:49 +0000440 equivalent genrules.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000441
442 Args:
443 blueprint: Blueprint instance which is being generated.
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100444 target: gn_utils.Target object.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000445
446 Returns:
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100447 The source_genrule module.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000448 """
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100449 assert (target.type == 'proto_library')
450 cpp_out_dir = '$(genDir)/%s/' % tree_path
451 cmd = [
452 'mkdir -p %s &&' % cpp_out_dir, '$(location aprotoc)',
453 '--cpp_out=%s' % cpp_out_dir
454 ]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000455
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100456 # We create two genrules for each proto target: one for the headers and
457 # another for the sources. This is because the module that depends on the
458 # generated files needs to declare two different types of dependencies --
459 # source files in 'srcs' and headers in 'generated_headers' -- and it's not
460 # valid to generate .h files from a source dependency and vice versa.
461 source_module_name = label_to_module_name(target.name) + '_gen'
462 source_module = Module('genrule', source_module_name, target.name)
463 blueprint.add_module(source_module)
464 source_module.srcs.update(
465 gn_utils.label_to_path(src) for src in target.sources)
466 tools = {'aprotoc'}
Primiano Tucci20b760c2018-01-19 12:36:12 +0000467
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100468 header_module = Module('genrule', source_module_name + '_headers',
469 target.name)
470 blueprint.add_module(header_module)
471 header_module.srcs = set(source_module.srcs)
Primiano Tucci20b760c2018-01-19 12:36:12 +0000472
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100473 # TODO(primiano): at some point we should remove this. This was introduced
474 # by aosp/1108421 when adding "protos/" to .proto include paths, in order to
475 # avoid doing multi-repo changes and allow old clients in the android tree
476 # to still do the old #include "perfetto/..." rather than
477 # #include "protos/perfetto/...".
478 header_module.export_include_dirs = {'.', 'protos'}
Sami Kyostila865d1d32017-12-12 18:37:04 +0000479
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100480 source_module.genrule_srcs.add(':' + source_module.name)
481 source_module.genrule_headers.add(header_module.name)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000482
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100483 # In GN builds the proto path is always relative to the output directory
484 # (out/tmp.xxx).
485 cmd += ['--proto_path=%s' % tree_path]
Primiano Tucci355b8c82019-08-29 08:37:51 +0200486
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100487 suffixes = ['pb']
488 if target.proto_plugin == 'proto':
489 source_module.genrule_shared_libs.add('libprotobuf-cpp-lite')
490 elif target.proto_plugin == 'protozero':
491 suffixes = ['pbzero']
492 plugin = create_modules_from_target(blueprint, gn, protozero_plugin)
493 tools.add(plugin.name)
494 cmd += ['--plugin=protoc-gen-plugin=$(location %s)' % plugin.name]
495 cmd += ['--plugin_out=wrapper_namespace=pbzero:' + cpp_out_dir]
Primiano Tucci57dd66b2019-10-15 23:09:04 +0100496 elif target.proto_plugin == 'cppgen':
497 suffixes = ['gen']
498 plugin = create_modules_from_target(blueprint, gn, cppgen_plugin)
499 tools.add(plugin.name)
500 cmd += ['--plugin=protoc-gen-plugin=$(location %s)' % plugin.name]
501 cmd += ['--plugin_out=:' + cpp_out_dir]
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100502 elif target.proto_plugin == 'ipc':
503 suffixes.append('ipc')
504 plugin = create_modules_from_target(blueprint, gn, ipc_plugin)
505 tools.add(plugin.name)
506 cmd += ['--plugin=protoc-gen-plugin=$(location %s)' % plugin.name]
507 cmd += ['--plugin_out=:' + cpp_out_dir]
508 else:
509 raise Error('Unsupported proto plugin: %s' % target.proto_plugin)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000510
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100511 cmd += ['$(in)']
512 source_module.cmd = ' '.join(cmd)
513 header_module.cmd = source_module.cmd
514 source_module.tools = tools
515 header_module.tools = tools
Primiano Tucci20b760c2018-01-19 12:36:12 +0000516
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100517 for sfx in suffixes:
Matthew Clarkson63028d62019-10-10 15:48:23 +0100518 source_module.out.update('%s/%s' %
519 (tree_path, src.replace('.proto', '.%s.cc' % sfx))
520 for src in source_module.srcs)
521 header_module.out.update('%s/%s' %
522 (tree_path, src.replace('.proto', '.%s.h' % sfx))
523 for src in header_module.srcs)
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100524 return source_module
Sami Kyostila865d1d32017-12-12 18:37:04 +0000525
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100526
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100527def create_merged_sql_metrics_module(blueprint, target):
528 module = Module('genrule', 'gen_merged_sql_metrics',
529 '//src/trace_processor/metrics:gen_merged_sql_metrics')
530 module.genrule_headers.add('gen_merged_sql_metrics')
531 module.tool_files = [
532 'tools/gen_merged_sql_metrics.py',
533 ]
534 module.cmd = ' '.join([
535 '$(location tools/gen_merged_sql_metrics.py)',
536 '--cpp_out=$(out)',
537 '$(in)',
538 ])
539 module.out.update(target.outputs)
540 module.srcs.update(gn_utils.label_to_path(src) for src in target.inputs)
541 blueprint.add_module(module)
542 return module
Sami Kyostila865d1d32017-12-12 18:37:04 +0000543
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100544
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000545def _get_cflags(target):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100546 cflags = {flag for flag in target.cflags if re.match(cflag_whitelist, flag)}
547 cflags |= set("-D%s" % define
548 for define in target.defines
549 if re.match(define_whitelist, define))
550 return cflags
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000551
552
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100553def create_modules_from_target(blueprint, gn, gn_target_name):
554 """Generate module(s) for a given GN target.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000555
556 Given a GN target name, generate one or more corresponding modules into a
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100557 blueprint. The only case when this generates >1 module is proto libraries.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000558
559 Args:
560 blueprint: Blueprint instance which is being generated.
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100561 gn: gn_utils.GnParser object.
562 gn_target_name: GN target for module generation.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000563 """
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100564 bp_module_name = label_to_module_name(gn_target_name)
565 if bp_module_name in blueprint.modules:
566 return blueprint.modules[bp_module_name]
567 target = gn.get_target(gn_target_name)
568 export_include_dirs = {'include', buildflags_dir}
569
570 if target.type == 'executable':
571 if target.toolchain == gn_utils.HOST_TOOLCHAIN:
572 module_type = 'cc_binary_host'
573 elif target.testonly:
574 module_type = 'cc_test'
Sami Kyostila865d1d32017-12-12 18:37:04 +0000575 else:
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100576 module_type = 'cc_binary'
577 module = Module(module_type, bp_module_name, gn_target_name)
578 elif target.type == 'static_library':
579 module = Module('cc_library_static', bp_module_name, gn_target_name)
580 module.export_include_dirs = export_include_dirs
581 elif target.type == 'shared_library':
582 module = Module('cc_library_shared', bp_module_name, gn_target_name)
583 module.export_include_dirs = export_include_dirs
584 elif target.type == 'source_set':
585 module = Module('filegroup', bp_module_name, gn_target_name)
586 elif target.type == 'group':
587 # "group" targets are resolved recursively by gn_utils.get_target().
588 # There's nothing we need to do at this level for them.
589 return None
590 elif target.type == 'proto_library':
591 module = create_proto_modules(blueprint, gn, target)
592 elif target.type == 'action' and 'gen_merged_sql_metrics' in target.name:
593 module = create_merged_sql_metrics_module(blueprint, target)
594 else:
595 raise Error('Unknown target %s (%s)' % (target.name, target.type))
Sami Kyostila865d1d32017-12-12 18:37:04 +0000596
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100597 blueprint.add_module(module)
598 module.host_supported = target.name in target_host_supported
599 module.init_rc = target_initrc.get(target.name, [])
600 module.srcs.update(
601 gn_utils.label_to_path(src)
602 for src in target.sources
603 if is_supported_source_file(src))
Primiano Tucci6067e732018-01-08 16:19:40 +0000604
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100605 if target.type in gn_utils.LINKER_UNIT_TYPES:
606 module.cflags.update(_get_cflags(target))
Sami Kyostila865d1d32017-12-12 18:37:04 +0000607
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100608 module_is_compiled = module.type not in ('genrule', 'filegroup')
609 if module_is_compiled:
610 # Don't try to inject library/source dependencies into genrules or
611 # filegroups because they are not compiled in the traditional sense.
612 module.defaults = [defaults_module]
613 for lib in target.libs:
614 # Generally library names should be mangled as 'libXXX', unless they
615 # are HAL libraries (e.g., android.hardware.health@2.0).
616 android_lib = lib if '@' in lib else 'lib' + lib
617 if lib in library_whitelist:
618 module.shared_libs.add(android_lib)
Lalit Magantic5bcd792018-01-12 18:38:11 +0000619
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100620 # If the module is a static library, export all the generated headers.
621 if module.type == 'cc_library_static':
622 module.export_generated_headers = module.generated_headers
Ryan Savitskie65beca2019-01-29 18:29:13 +0000623
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100624 # Merge in additional hardcoded arguments.
625 for key, add_val in additional_args.get(module.name, []):
626 curr = getattr(module, key)
627 if add_val and isinstance(add_val, set) and isinstance(curr, set):
628 curr.update(add_val)
Lalit Maganti3d415ec2019-10-23 17:53:17 +0100629 elif isinstance(add_val, bool) and isinstance(curr, bool):
630 setattr(module, key, add_val)
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100631 else:
632 raise Error('Unimplemented type of additional_args: %r' % key)
633
634 # dep_name is an unmangled GN target name (e.g. //foo:bar(toolchain)).
635 for dep_name in target.deps | target.source_set_deps | target.proto_deps:
636 # If the dependency refers to a library which we can replace with an
637 # Android equivalent, stop recursing and patch the dependency in.
638 # Don't recurse into //buildtools, builtin_deps are intercepted at
639 # the //gn:xxx level.
640 if dep_name.startswith('//buildtools'):
641 continue
642
643 # Ignore the dependency on the gen_buildflags genrule. That is run
644 # separately in this generator and the generated file is copied over
645 # into the repo (see usage of |buildflags_dir| in this script).
646 if dep_name.startswith(gn_utils.BUILDFLAGS_TARGET):
647 continue
648
649 dep_module = create_modules_from_target(blueprint, gn, dep_name)
650
651 # For filegroups and genrule, recurse but don't apply the deps.
652 if not module_is_compiled:
653 continue
654
655 # |builtin_deps| override GN deps with Android-specific ones. See the
656 # config in the top of this file.
657 if gn_utils.label_without_toolchain(dep_name) in builtin_deps:
658 builtin_deps[gn_utils.label_without_toolchain(dep_name)](module)
659 continue
660
661 # Don't recurse in any other //gn dep if not handled by builtin_deps.
662 if dep_name.startswith('//gn:'):
663 continue
664
665 if dep_module is None:
666 continue
667 if dep_module.type == 'cc_library_shared':
668 module.shared_libs.add(dep_module.name)
669 elif dep_module.type == 'cc_library_static':
670 module.static_libs.add(dep_module.name)
671 elif dep_module.type == 'filegroup':
672 module.srcs.add(':' + dep_module.name)
673 elif dep_module.type == 'genrule':
674 module.generated_headers.update(dep_module.genrule_headers)
675 module.srcs.update(dep_module.genrule_srcs)
676 module.shared_libs.update(dep_module.genrule_shared_libs)
677 else:
678 raise Error('Unknown dep %s (%s) for target %s' %
679 (dep_module.name, dep_module.type, module.name))
680
681 return module
Sami Kyostila865d1d32017-12-12 18:37:04 +0000682
683
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100684def create_blueprint_for_targets(gn, desc, targets):
685 """Generate a blueprint for a list of GN targets."""
686 blueprint = Blueprint()
Sami Kyostila865d1d32017-12-12 18:37:04 +0000687
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100688 # Default settings used by all modules.
689 defaults = Module('cc_defaults', defaults_module, '//gn:default_deps')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000690
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100691 # We have to use include_dirs passing the path relative to the android tree.
692 # This is because: (i) perfetto_cc_defaults is used also by
693 # test/**/Android.bp; (ii) if we use local_include_dirs instead, paths
694 # become relative to the Android.bp that *uses* cc_defaults (not the one
695 # that defines it).s
696 defaults.include_dirs = {
697 tree_path, tree_path + '/include', tree_path + '/' + buildflags_dir
698 }
699 defaults.cflags = [
700 '-Wno-error=return-type',
701 '-Wno-sign-compare',
702 '-Wno-sign-promo',
703 '-Wno-unused-parameter',
704 '-fvisibility=hidden',
705 '-O2',
706 ]
707 defaults.user_debug_flag = True
708 defaults.lto = True
Sami Kyostila865d1d32017-12-12 18:37:04 +0000709
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100710 blueprint.add_module(defaults)
711 gn = gn_utils.GnParser(desc)
712 for target in targets:
713 create_modules_from_target(blueprint, gn, target)
714 return blueprint
Sami Kyostila865d1d32017-12-12 18:37:04 +0000715
716
717def main():
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100718 parser = argparse.ArgumentParser(
719 description='Generate Android.bp from a GN description.')
720 parser.add_argument(
721 '--check-only',
722 help='Don\'t keep the generated files',
723 action='store_true')
724 parser.add_argument(
725 '--desc',
Matthew Clarkson63028d62019-10-10 15:48:23 +0100726 help='GN description (e.g., gn desc out --format=json --all-toolchains "//*"'
727 )
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100728 parser.add_argument(
729 '--extras',
730 help='Extra targets to include at the end of the Blueprint file',
731 default=os.path.join(gn_utils.repo_root(), 'Android.bp.extras'),
732 )
733 parser.add_argument(
734 '--output',
735 help='Blueprint file to create',
736 default=os.path.join(gn_utils.repo_root(), 'Android.bp'),
737 )
738 parser.add_argument(
739 'targets',
740 nargs=argparse.REMAINDER,
741 help='Targets to include in the blueprint (e.g., "//:perfetto_tests")')
742 args = parser.parse_args()
Sami Kyostila865d1d32017-12-12 18:37:04 +0000743
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100744 if args.desc:
745 with open(args.desc) as f:
746 desc = json.load(f)
747 else:
748 desc = gn_utils.create_build_description(gn_args)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000749
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100750 gn = gn_utils.GnParser(desc)
751 blueprint = create_blueprint_for_targets(gn, desc, args.targets or
752 default_targets)
753 output = [
754 """// Copyright (C) 2017 The Android Open Source Project
Sami Kyostila865d1d32017-12-12 18:37:04 +0000755//
756// Licensed under the Apache License, Version 2.0 (the "License");
757// you may not use this file except in compliance with the License.
758// You may obtain a copy of the License at
759//
760// http://www.apache.org/licenses/LICENSE-2.0
761//
762// Unless required by applicable law or agreed to in writing, software
763// distributed under the License is distributed on an "AS IS" BASIS,
764// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
765// See the License for the specific language governing permissions and
766// limitations under the License.
767//
768// This file is automatically generated by %s. Do not edit.
769""" % (__file__)
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100770 ]
771 blueprint.to_string(output)
772 with open(args.extras, 'r') as r:
773 for line in r:
774 output.append(line.rstrip("\n\r"))
Primiano Tucci9c411652019-08-27 07:13:59 +0200775
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100776 out_files = []
Primiano Tucci9c411652019-08-27 07:13:59 +0200777
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100778 # Generate the Android.bp file.
779 out_files.append(args.output + '.swp')
780 with open(out_files[-1], 'w') as f:
781 f.write('\n'.join(output))
Sami Kyostila865d1d32017-12-12 18:37:04 +0000782
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100783 # Generate the perfetto_build_flags.h file.
784 out_files.append(os.path.join(buildflags_dir, 'perfetto_build_flags.h.swp'))
785 gn_utils.gen_buildflags(gn_args, out_files[-1])
Sami Kyostila865d1d32017-12-12 18:37:04 +0000786
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100787 # Either check the contents or move the files to their final destination.
788 return gn_utils.check_or_commit_generated_files(out_files, args.check_only)
Primiano Tucci9c411652019-08-27 07:13:59 +0200789
790
Sami Kyostila865d1d32017-12-12 18:37:04 +0000791if __name__ == '__main__':
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100792 sys.exit(main())