blob: fa38f37cf1401734acca325a550b4c1a5603b07e [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',
Ryan Savitski462b5db2019-11-20 19:06:46 +000061 '//src/profiling/perf:traced_perf',
Primiano Tucci02c11762019-08-30 00:57:59 +020062 '//src/traced/probes:traced_probes',
63 '//src/traced/service:traced',
Primiano Tuccif0d7ef82019-10-04 15:35:24 +010064]
65
66# Host targets
67ipc_plugin = '//src/ipc/protoc_plugin:ipc_plugin(%s)' % gn_utils.HOST_TOOLCHAIN
68protozero_plugin = '//src/protozero/protoc_plugin:protozero_plugin(%s)' % (
69 gn_utils.HOST_TOOLCHAIN)
Primiano Tucci57dd66b2019-10-15 23:09:04 +010070cppgen_plugin = '//src/protozero/protoc_plugin:cppgen_plugin(%s)' % (
71 gn_utils.HOST_TOOLCHAIN)
72
Primiano Tuccif0d7ef82019-10-04 15:35:24 +010073default_targets += [
74 '//src/trace_processor:trace_processor_shell(%s)' % gn_utils.HOST_TOOLCHAIN,
75 '//tools/trace_to_text:trace_to_text(%s)' % gn_utils.HOST_TOOLCHAIN,
76 protozero_plugin,
77 ipc_plugin,
Primiano Tucci02c11762019-08-30 00:57:59 +020078]
79
Primiano Tucci02c11762019-08-30 00:57:59 +020080# Defines a custom init_rc argument to be applied to the corresponding output
81# blueprint target.
82target_initrc = {
Primiano Tuccif0d7ef82019-10-04 15:35:24 +010083 '//src/traced/service:traced': {'perfetto.rc'},
84 '//src/profiling/memory:heapprofd': {'heapprofd.rc'},
Primiano Tucci02c11762019-08-30 00:57:59 +020085}
86
87target_host_supported = [
88 '//protos/perfetto/trace:perfetto_trace_protos',
Hector Dearman04cfac72019-09-24 22:05:55 +010089 '//:libperfetto',
Primiano Tucci02c11762019-08-30 00:57:59 +020090]
91
Sami Kyostila865d1d32017-12-12 18:37:04 +000092# All module names are prefixed with this string to avoid collisions.
93module_prefix = 'perfetto_'
94
95# Shared libraries which are directly translated to Android system equivalents.
Hector Dearman6d2a11d2019-11-19 16:02:47 +000096library_whitelist = [
97 "android.hardware.atrace@1.0",
Hector Dearmana5b90822019-11-18 13:00:16 +000098 'android.hardware.health@2.0',
Hector Dearman6d2a11d2019-11-19 16:02:47 +000099 "android.hardware.power.stats@1.0",
100 'android',
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100101 'base',
Sami Kyostilab5b71692018-01-12 12:16:44 +0000102 'binder',
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100103 'hidlbase',
104 'hidltransport',
105 'hwbinder',
Ryan Savitski53ca60b2019-06-03 13:04:40 +0100106 'incident',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000107 'log',
Sami Kyostilab5b71692018-01-12 12:16:44 +0000108 'services',
Primiano Tucciedf099c2018-01-08 18:27:56 +0000109 'utils',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000110]
111
112# Name of the module which settings such as compiler flags for all other
113# modules.
114defaults_module = module_prefix + 'defaults'
115
116# Location of the project in the Android source tree.
117tree_path = 'external/perfetto'
118
Primiano Tucciedf099c2018-01-08 18:27:56 +0000119# Compiler flags which are passed through to the blueprint.
120cflag_whitelist = r'^-DPERFETTO.*$'
121
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000122# Compiler defines which are passed through to the blueprint.
Primiano Tucci8e627442019-08-28 07:58:38 +0200123define_whitelist = r'^(GOOGLE_PROTO.*)|(ZLIB_.*)|(USE_MMAP)|(HAVE_HIDDEN)$'
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000124
Logan Chien9bfaaf92018-02-13 18:49:24 +0800125# Shared libraries which are not in PDK.
126library_not_in_pdk = {
127 'libandroid',
128 'libservices',
129}
130
Primiano Tucci8e627442019-08-28 07:58:38 +0200131# The directory where the generated perfetto_build_flags.h will be copied into.
132buildflags_dir = 'include/perfetto/base/build_configs/android_tree'
133
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100134
135def enumerate_data_deps():
136 with open(os.path.join(ROOT_DIR, 'tools', 'test_data.txt')) as f:
137 lines = f.readlines()
138 for line in (line.strip() for line in lines if not line.startswith('#')):
139 assert os.path.exists(line), line
140 if line.endswith('/'):
141 yield line + '**/*'
142 else:
143 yield line
144
145
Florian Mayerb6a921f2018-10-18 18:55:23 +0100146# Additional arguments to apply to Android.bp rules.
147additional_args = {
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100148 'heapprofd_client': [
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100149 ('include_dirs', {'bionic/libc'}),
150 ('static_libs', {'libasync_safe'}),
151 ('header_libs', {'bionic_libc_platform_headers'}),
Primiano Tucci676f0cc2018-12-03 20:03:26 +0100152 ],
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100153 'perfetto_unittests': [('data', set(enumerate_data_deps())),],
Matthew Clarkson63028d62019-10-10 15:48:23 +0100154 'traced_probes': [
155 ('required', {'libperfetto_android_internal', 'trigger_perfetto'}),
156 ],
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100157 'libperfetto_android_internal': [('static_libs', {'libhealthhalutils'}),],
Lalit Maganticdda9112019-11-27 14:19:49 +0000158 'trace_processor_shell': [
159 ('dist', {'targets': ['sdk_repo']}),
160 ('stl', 'libc++_static'),
161 ],
Florian Mayerb6a921f2018-10-18 18:55:23 +0100162}
163
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100164
Sami Kyostila865d1d32017-12-12 18:37:04 +0000165def enable_gmock(module):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100166 module.static_libs.add('libgmock')
167
Sami Kyostila865d1d32017-12-12 18:37:04 +0000168
Sami Kyostila865d1d32017-12-12 18:37:04 +0000169def enable_protobuf_full(module):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100170 module.shared_libs.add('libprotobuf-cpp-full')
171
Sami Kyostila865d1d32017-12-12 18:37:04 +0000172
Sami Kyostila865d1d32017-12-12 18:37:04 +0000173def enable_protobuf_lite(module):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100174 module.shared_libs.add('libprotobuf-cpp-lite')
175
Sami Kyostila865d1d32017-12-12 18:37:04 +0000176
Sami Kyostila865d1d32017-12-12 18:37:04 +0000177def enable_protoc_lib(module):
Lalit Maganti3d415ec2019-10-23 17:53:17 +0100178 if module.type == 'cc_binary_host':
179 module.static_libs.add('libprotoc')
180 else:
181 module.shared_libs.add('libprotoc')
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100182
Sami Kyostila865d1d32017-12-12 18:37:04 +0000183
Florian Mayera2fae262018-08-31 12:10:01 -0700184def enable_libunwindstack(module):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100185 module.shared_libs.add('libunwindstack')
186 module.shared_libs.add('libprocinfo')
187 module.shared_libs.add('libbase')
188
Sami Kyostila865d1d32017-12-12 18:37:04 +0000189
190def enable_libunwind(module):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100191 # libunwind is disabled on Darwin so we cannot depend on it.
192 pass
193
Sami Kyostila865d1d32017-12-12 18:37:04 +0000194
Lalit Maganti17aa2732019-02-08 15:47:26 +0000195def enable_sqlite(module):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100196 if module.type == 'cc_binary_host':
197 module.static_libs.add('libsqlite')
198 else:
199 # Copy what the sqlite3 command line tool does.
200 module.android.shared_libs.add('libsqlite')
201 module.android.shared_libs.add('libandroidicu')
202 module.android.shared_libs.add('liblog')
203 module.android.shared_libs.add('libutils')
204 module.host.static_libs.add('libsqlite')
205
Lalit Maganti17aa2732019-02-08 15:47:26 +0000206
Hector Dearmane0b993f2019-05-24 18:48:16 +0100207def enable_zlib(module):
Lalit Maganti3d415ec2019-10-23 17:53:17 +0100208 if module.type == 'cc_binary_host':
209 module.static_libs.add('libz')
210 else:
211 module.shared_libs.add('libz')
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100212
Hector Dearmane0b993f2019-05-24 18:48:16 +0100213
Sami Kyostila865d1d32017-12-12 18:37:04 +0000214# Android equivalents for third-party libraries that the upstream project
215# depends on.
216builtin_deps = {
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100217 '//gn:default_deps': lambda x: None,
218 '//gn:gtest_main': lambda x: None,
219 '//gn:protoc': lambda x: None,
Primiano Tuccib7ebffd2019-09-09 07:42:35 -0700220 '//gn:gtest_and_gmock': enable_gmock,
221 '//gn:libunwind': enable_libunwind,
222 '//gn:protobuf_full': enable_protobuf_full,
223 '//gn:protobuf_lite': enable_protobuf_lite,
224 '//gn:protoc_lib': enable_protoc_lib,
225 '//gn:libunwindstack': enable_libunwindstack,
226 '//gn:sqlite': enable_sqlite,
227 '//gn:zlib': enable_zlib,
Sami Kyostila865d1d32017-12-12 18:37:04 +0000228}
229
230# ----------------------------------------------------------------------------
231# End of configuration.
232# ----------------------------------------------------------------------------
233
234
235class Error(Exception):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100236 pass
Sami Kyostila865d1d32017-12-12 18:37:04 +0000237
238
239class ThrowingArgumentParser(argparse.ArgumentParser):
Sami Kyostila865d1d32017-12-12 18:37:04 +0000240
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100241 def __init__(self, context):
242 super(ThrowingArgumentParser, self).__init__()
243 self.context = context
244
245 def error(self, message):
246 raise Error('%s: %s' % (self.context, message))
Sami Kyostila865d1d32017-12-12 18:37:04 +0000247
248
Lalit Magantiedace412019-06-18 13:28:28 +0100249def write_blueprint_key_value(output, name, value, sort=True):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100250 """Writes a Blueprint key-value pair to the output"""
Lalit Magantiedace412019-06-18 13:28:28 +0100251
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100252 if not value:
253 return
254 if isinstance(value, set):
255 value = sorted(value)
256 if isinstance(value, list):
257 output.append(' %s: [' % name)
258 for item in sorted(value) if sort else value:
259 output.append(' "%s",' % item)
260 output.append(' ],')
261 return
262 if isinstance(value, bool):
263 output.append(' %s: true,' % name)
264 return
265 if isinstance(value, Target):
266 value.to_string(output)
267 return
Lalit Maganticdda9112019-11-27 14:19:49 +0000268 if isinstance(value, dict):
269 kv_output = []
270 for k, v in value.items():
271 write_blueprint_key_value(kv_output, k, v)
272
273 output.append(' %s: {' % name)
274 for line in kv_output:
275 output.append(' %s' % line)
276 output.append(' },')
277 return
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100278 output.append(' %s: "%s",' % (name, value))
279
Lalit Magantiedace412019-06-18 13:28:28 +0100280
281class Target(object):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100282 """A target-scoped part of a module"""
Lalit Magantiedace412019-06-18 13:28:28 +0100283
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100284 def __init__(self, name):
285 self.name = name
286 self.shared_libs = set()
287 self.static_libs = set()
288 self.cflags = set()
Lalit Magantiedace412019-06-18 13:28:28 +0100289
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100290 def to_string(self, output):
291 nested_out = []
292 self._output_field(nested_out, 'shared_libs')
293 self._output_field(nested_out, 'static_libs')
294 self._output_field(nested_out, 'cflags')
Lalit Magantiedace412019-06-18 13:28:28 +0100295
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100296 if nested_out:
297 output.append(' %s: {' % self.name)
298 for line in nested_out:
299 output.append(' %s' % line)
300 output.append(' },')
Lalit Magantiedace412019-06-18 13:28:28 +0100301
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100302 def _output_field(self, output, name, sort=True):
303 value = getattr(self, name)
304 return write_blueprint_key_value(output, name, value, sort)
305
Lalit Magantiedace412019-06-18 13:28:28 +0100306
Sami Kyostila865d1d32017-12-12 18:37:04 +0000307class Module(object):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100308 """A single module (e.g., cc_binary, cc_test) in a blueprint."""
Sami Kyostila865d1d32017-12-12 18:37:04 +0000309
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100310 def __init__(self, mod_type, name, gn_target):
311 self.type = mod_type
312 self.gn_target = gn_target
313 self.name = name
314 self.srcs = set()
315 self.comment = 'GN: ' + gn_utils.label_without_toolchain(gn_target)
316 self.shared_libs = set()
317 self.static_libs = set()
318 self.tools = set()
319 self.cmd = None
320 self.host_supported = False
321 self.init_rc = set()
322 self.out = set()
323 self.export_include_dirs = set()
324 self.generated_headers = set()
325 self.export_generated_headers = set()
326 self.defaults = set()
327 self.cflags = set()
328 self.include_dirs = set()
329 self.header_libs = set()
330 self.required = set()
331 self.user_debug_flag = False
332 self.tool_files = None
333 self.android = Target('android')
334 self.host = Target('host')
335 self.lto = None
Lalit Maganticdda9112019-11-27 14:19:49 +0000336 self.stl = None
337 self.dist = dict()
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100338 self.data = set()
339 # The genrule_XXX below are properties that must to be propagated back
340 # on the module(s) that depend on the genrule.
341 self.genrule_headers = set()
342 self.genrule_srcs = set()
343 self.genrule_shared_libs = set()
Sami Kyostila865d1d32017-12-12 18:37:04 +0000344
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100345 def to_string(self, output):
346 if self.comment:
347 output.append('// %s' % self.comment)
348 output.append('%s {' % self.type)
349 self._output_field(output, 'name')
350 self._output_field(output, 'srcs')
351 self._output_field(output, 'shared_libs')
352 self._output_field(output, 'static_libs')
353 self._output_field(output, 'tools')
354 self._output_field(output, 'cmd', sort=False)
355 self._output_field(output, 'host_supported')
356 self._output_field(output, 'init_rc')
357 self._output_field(output, 'out')
358 self._output_field(output, 'export_include_dirs')
359 self._output_field(output, 'generated_headers')
360 self._output_field(output, 'export_generated_headers')
361 self._output_field(output, 'defaults')
362 self._output_field(output, 'cflags')
363 self._output_field(output, 'include_dirs')
364 self._output_field(output, 'header_libs')
365 self._output_field(output, 'required')
Lalit Maganticdda9112019-11-27 14:19:49 +0000366 self._output_field(output, 'dist')
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100367 self._output_field(output, 'tool_files')
368 self._output_field(output, 'data')
Lalit Maganticdda9112019-11-27 14:19:49 +0000369 self._output_field(output, 'stl')
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100370
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100371 target_out = []
372 self._output_field(target_out, 'android')
373 self._output_field(target_out, 'host')
374 if target_out:
375 output.append(' target: {')
376 for line in target_out:
377 output.append(' %s' % line)
378 output.append(' },')
Lalit Magantiedace412019-06-18 13:28:28 +0100379
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100380 disable_pdk = any(name in library_not_in_pdk for name in self.shared_libs)
381 if self.user_debug_flag or disable_pdk:
382 output.append(' product_variables: {')
383 if disable_pdk:
384 output.append(' pdk: {')
385 output.append(' enabled: false,')
386 output.append(' },')
387 if self.user_debug_flag:
388 output.append(' debuggable: {')
389 output.append(
390 ' cflags: ["-DPERFETTO_BUILD_WITH_ANDROID_USERDEBUG"],')
391 output.append(' },')
392 output.append(' },')
393 if self.lto is not None:
394 output.append(' target: {')
395 output.append(' android: {')
396 output.append(' lto: {')
397 output.append(' thin: %s,' % 'true' if self.lto else 'false')
398 output.append(' },')
399 output.append(' },')
400 output.append(' },')
401 output.append('}')
402 output.append('')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000403
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100404 def _output_field(self, output, name, sort=True):
405 value = getattr(self, name)
406 return write_blueprint_key_value(output, name, value, sort)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000407
408
409class Blueprint(object):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100410 """In-memory representation of an Android.bp file."""
Sami Kyostila865d1d32017-12-12 18:37:04 +0000411
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100412 def __init__(self):
413 self.modules = {}
Sami Kyostila865d1d32017-12-12 18:37:04 +0000414
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100415 def add_module(self, module):
416 """Adds a new module to the blueprint, replacing any existing module
Sami Kyostila865d1d32017-12-12 18:37:04 +0000417 with the same name.
418
419 Args:
420 module: Module instance.
421 """
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100422 self.modules[module.name] = module
Sami Kyostila865d1d32017-12-12 18:37:04 +0000423
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100424 def to_string(self, output):
425 for m in sorted(itervalues(self.modules), key=lambda m: m.name):
426 m.to_string(output)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000427
428
Sami Kyostila865d1d32017-12-12 18:37:04 +0000429def label_to_module_name(label):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100430 """Turn a GN label (e.g., //:perfetto_tests) into a module name."""
431 # If the label is explicibly listed in the default target list, don't prefix
432 # its name and return just the target name. This is so tools like
433 # "trace_to_text" stay as such in the Android tree.
434 label_without_toolchain = gn_utils.label_without_toolchain(label)
435 if label in default_targets or label_without_toolchain in default_targets:
436 return label_without_toolchain.split(':')[-1]
Primiano Tucci02c11762019-08-30 00:57:59 +0200437
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100438 module = re.sub(r'^//:?', '', label_without_toolchain)
439 module = re.sub(r'[^a-zA-Z0-9_]', '_', module)
440 if not module.startswith(module_prefix):
441 return module_prefix + module
442 return module
Sami Kyostila865d1d32017-12-12 18:37:04 +0000443
444
Sami Kyostila865d1d32017-12-12 18:37:04 +0000445def is_supported_source_file(name):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100446 """Returns True if |name| can appear in a 'srcs' list."""
447 return os.path.splitext(name)[1] in ['.c', '.cc', '.proto']
Sami Kyostila865d1d32017-12-12 18:37:04 +0000448
449
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100450def create_proto_modules(blueprint, gn, target):
451 """Generate genrules for a proto GN target.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000452
453 GN actions are used to dynamically generate files during the build. The
454 Soong equivalent is a genrule. This function turns a specific kind of
455 genrule which turns .proto files into source and header files into a pair
Sami Kyostila71625d72017-12-18 10:29:49 +0000456 equivalent genrules.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000457
458 Args:
459 blueprint: Blueprint instance which is being generated.
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100460 target: gn_utils.Target object.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000461
462 Returns:
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100463 The source_genrule module.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000464 """
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100465 assert (target.type == 'proto_library')
466 cpp_out_dir = '$(genDir)/%s/' % tree_path
Primiano Tucci3aa027d2019-11-22 21:43:43 +0000467 cmd = ['mkdir -p %s &&' % cpp_out_dir, '$(location aprotoc)']
Sami Kyostila865d1d32017-12-12 18:37:04 +0000468
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100469 # We create two genrules for each proto target: one for the headers and
470 # another for the sources. This is because the module that depends on the
471 # generated files needs to declare two different types of dependencies --
472 # source files in 'srcs' and headers in 'generated_headers' -- and it's not
473 # valid to generate .h files from a source dependency and vice versa.
474 source_module_name = label_to_module_name(target.name) + '_gen'
475 source_module = Module('genrule', source_module_name, target.name)
476 blueprint.add_module(source_module)
477 source_module.srcs.update(
478 gn_utils.label_to_path(src) for src in target.sources)
479 tools = {'aprotoc'}
Primiano Tucci20b760c2018-01-19 12:36:12 +0000480
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100481 header_module = Module('genrule', source_module_name + '_headers',
482 target.name)
483 blueprint.add_module(header_module)
484 header_module.srcs = set(source_module.srcs)
Primiano Tucci20b760c2018-01-19 12:36:12 +0000485
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100486 # TODO(primiano): at some point we should remove this. This was introduced
487 # by aosp/1108421 when adding "protos/" to .proto include paths, in order to
488 # avoid doing multi-repo changes and allow old clients in the android tree
489 # to still do the old #include "perfetto/..." rather than
490 # #include "protos/perfetto/...".
491 header_module.export_include_dirs = {'.', 'protos'}
Sami Kyostila865d1d32017-12-12 18:37:04 +0000492
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100493 source_module.genrule_srcs.add(':' + source_module.name)
494 source_module.genrule_headers.add(header_module.name)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000495
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100496 # In GN builds the proto path is always relative to the output directory
497 # (out/tmp.xxx).
498 cmd += ['--proto_path=%s' % tree_path]
Primiano Tucci355b8c82019-08-29 08:37:51 +0200499
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100500 if target.proto_plugin == 'proto':
Primiano Tucci3aa027d2019-11-22 21:43:43 +0000501 suffixes = ['pb']
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100502 source_module.genrule_shared_libs.add('libprotobuf-cpp-lite')
Primiano Tucci3aa027d2019-11-22 21:43:43 +0000503 cmd += ['--cpp_out=' + cpp_out_dir]
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100504 elif target.proto_plugin == 'protozero':
505 suffixes = ['pbzero']
506 plugin = create_modules_from_target(blueprint, gn, protozero_plugin)
507 tools.add(plugin.name)
508 cmd += ['--plugin=protoc-gen-plugin=$(location %s)' % plugin.name]
509 cmd += ['--plugin_out=wrapper_namespace=pbzero:' + cpp_out_dir]
Primiano Tucci57dd66b2019-10-15 23:09:04 +0100510 elif target.proto_plugin == 'cppgen':
511 suffixes = ['gen']
512 plugin = create_modules_from_target(blueprint, gn, cppgen_plugin)
513 tools.add(plugin.name)
514 cmd += ['--plugin=protoc-gen-plugin=$(location %s)' % plugin.name]
Primiano Tuccie8020f92019-11-26 13:24:01 +0000515 cmd += ['--plugin_out=wrapper_namespace=gen:' + cpp_out_dir]
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100516 elif target.proto_plugin == 'ipc':
Primiano Tucci3aa027d2019-11-22 21:43:43 +0000517 suffixes = ['ipc']
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100518 plugin = create_modules_from_target(blueprint, gn, ipc_plugin)
519 tools.add(plugin.name)
520 cmd += ['--plugin=protoc-gen-plugin=$(location %s)' % plugin.name]
Primiano Tuccie8020f92019-11-26 13:24:01 +0000521 cmd += ['--plugin_out=wrapper_namespace=gen:' + cpp_out_dir]
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100522 else:
523 raise Error('Unsupported proto plugin: %s' % target.proto_plugin)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000524
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100525 cmd += ['$(in)']
526 source_module.cmd = ' '.join(cmd)
527 header_module.cmd = source_module.cmd
528 source_module.tools = tools
529 header_module.tools = tools
Primiano Tucci20b760c2018-01-19 12:36:12 +0000530
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100531 for sfx in suffixes:
Matthew Clarkson63028d62019-10-10 15:48:23 +0100532 source_module.out.update('%s/%s' %
533 (tree_path, src.replace('.proto', '.%s.cc' % sfx))
534 for src in source_module.srcs)
535 header_module.out.update('%s/%s' %
536 (tree_path, src.replace('.proto', '.%s.h' % sfx))
537 for src in header_module.srcs)
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100538 return source_module
Sami Kyostila865d1d32017-12-12 18:37:04 +0000539
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100540
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100541def create_merged_sql_metrics_module(blueprint, target):
542 module = Module('genrule', 'gen_merged_sql_metrics',
543 '//src/trace_processor/metrics:gen_merged_sql_metrics')
544 module.genrule_headers.add('gen_merged_sql_metrics')
545 module.tool_files = [
546 'tools/gen_merged_sql_metrics.py',
547 ]
548 module.cmd = ' '.join([
549 '$(location tools/gen_merged_sql_metrics.py)',
550 '--cpp_out=$(out)',
551 '$(in)',
552 ])
553 module.out.update(target.outputs)
554 module.srcs.update(gn_utils.label_to_path(src) for src in target.inputs)
555 blueprint.add_module(module)
556 return module
Sami Kyostila865d1d32017-12-12 18:37:04 +0000557
Sami Kyostila3c88a1d2019-05-22 18:29:42 +0100558
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000559def _get_cflags(target):
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100560 cflags = {flag for flag in target.cflags if re.match(cflag_whitelist, flag)}
561 cflags |= set("-D%s" % define
562 for define in target.defines
563 if re.match(define_whitelist, define))
564 return cflags
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000565
566
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100567def create_modules_from_target(blueprint, gn, gn_target_name):
568 """Generate module(s) for a given GN target.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000569
570 Given a GN target name, generate one or more corresponding modules into a
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100571 blueprint. The only case when this generates >1 module is proto libraries.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000572
573 Args:
574 blueprint: Blueprint instance which is being generated.
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100575 gn: gn_utils.GnParser object.
576 gn_target_name: GN target for module generation.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000577 """
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100578 bp_module_name = label_to_module_name(gn_target_name)
579 if bp_module_name in blueprint.modules:
580 return blueprint.modules[bp_module_name]
581 target = gn.get_target(gn_target_name)
582 export_include_dirs = {'include', buildflags_dir}
583
584 if target.type == 'executable':
585 if target.toolchain == gn_utils.HOST_TOOLCHAIN:
586 module_type = 'cc_binary_host'
587 elif target.testonly:
588 module_type = 'cc_test'
Sami Kyostila865d1d32017-12-12 18:37:04 +0000589 else:
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100590 module_type = 'cc_binary'
591 module = Module(module_type, bp_module_name, gn_target_name)
592 elif target.type == 'static_library':
593 module = Module('cc_library_static', bp_module_name, gn_target_name)
594 module.export_include_dirs = export_include_dirs
595 elif target.type == 'shared_library':
596 module = Module('cc_library_shared', bp_module_name, gn_target_name)
597 module.export_include_dirs = export_include_dirs
598 elif target.type == 'source_set':
599 module = Module('filegroup', bp_module_name, gn_target_name)
600 elif target.type == 'group':
601 # "group" targets are resolved recursively by gn_utils.get_target().
602 # There's nothing we need to do at this level for them.
603 return None
604 elif target.type == 'proto_library':
605 module = create_proto_modules(blueprint, gn, target)
606 elif target.type == 'action' and 'gen_merged_sql_metrics' in target.name:
607 module = create_merged_sql_metrics_module(blueprint, target)
608 else:
609 raise Error('Unknown target %s (%s)' % (target.name, target.type))
Sami Kyostila865d1d32017-12-12 18:37:04 +0000610
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100611 blueprint.add_module(module)
612 module.host_supported = target.name in target_host_supported
613 module.init_rc = target_initrc.get(target.name, [])
614 module.srcs.update(
615 gn_utils.label_to_path(src)
616 for src in target.sources
617 if is_supported_source_file(src))
Primiano Tucci6067e732018-01-08 16:19:40 +0000618
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100619 if target.type in gn_utils.LINKER_UNIT_TYPES:
620 module.cflags.update(_get_cflags(target))
Sami Kyostila865d1d32017-12-12 18:37:04 +0000621
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100622 module_is_compiled = module.type not in ('genrule', 'filegroup')
623 if module_is_compiled:
624 # Don't try to inject library/source dependencies into genrules or
625 # filegroups because they are not compiled in the traditional sense.
626 module.defaults = [defaults_module]
627 for lib in target.libs:
628 # Generally library names should be mangled as 'libXXX', unless they
629 # are HAL libraries (e.g., android.hardware.health@2.0).
630 android_lib = lib if '@' in lib else 'lib' + lib
Hector Dearman6d2a11d2019-11-19 16:02:47 +0000631 if lib in library_whitelist:
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100632 module.shared_libs.add(android_lib)
Lalit Magantic5bcd792018-01-12 18:38:11 +0000633
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100634 # If the module is a static library, export all the generated headers.
635 if module.type == 'cc_library_static':
636 module.export_generated_headers = module.generated_headers
Ryan Savitskie65beca2019-01-29 18:29:13 +0000637
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100638 # Merge in additional hardcoded arguments.
639 for key, add_val in additional_args.get(module.name, []):
640 curr = getattr(module, key)
641 if add_val and isinstance(add_val, set) and isinstance(curr, set):
642 curr.update(add_val)
Lalit Maganticdda9112019-11-27 14:19:49 +0000643 elif isinstance(add_val, str) and (not curr or isinstance(curr, str)):
Lalit Maganti3d415ec2019-10-23 17:53:17 +0100644 setattr(module, key, add_val)
Lalit Maganticdda9112019-11-27 14:19:49 +0000645 elif isinstance(add_val, dict) and isinstance(curr, dict):
646 curr.update(add_val)
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100647 else:
648 raise Error('Unimplemented type of additional_args: %r' % key)
649
650 # dep_name is an unmangled GN target name (e.g. //foo:bar(toolchain)).
651 for dep_name in target.deps | target.source_set_deps | target.proto_deps:
652 # If the dependency refers to a library which we can replace with an
653 # Android equivalent, stop recursing and patch the dependency in.
654 # Don't recurse into //buildtools, builtin_deps are intercepted at
655 # the //gn:xxx level.
656 if dep_name.startswith('//buildtools'):
657 continue
658
659 # Ignore the dependency on the gen_buildflags genrule. That is run
660 # separately in this generator and the generated file is copied over
661 # into the repo (see usage of |buildflags_dir| in this script).
662 if dep_name.startswith(gn_utils.BUILDFLAGS_TARGET):
663 continue
664
665 dep_module = create_modules_from_target(blueprint, gn, dep_name)
666
667 # For filegroups and genrule, recurse but don't apply the deps.
668 if not module_is_compiled:
669 continue
670
671 # |builtin_deps| override GN deps with Android-specific ones. See the
672 # config in the top of this file.
673 if gn_utils.label_without_toolchain(dep_name) in builtin_deps:
674 builtin_deps[gn_utils.label_without_toolchain(dep_name)](module)
675 continue
676
677 # Don't recurse in any other //gn dep if not handled by builtin_deps.
678 if dep_name.startswith('//gn:'):
679 continue
680
681 if dep_module is None:
682 continue
683 if dep_module.type == 'cc_library_shared':
684 module.shared_libs.add(dep_module.name)
685 elif dep_module.type == 'cc_library_static':
686 module.static_libs.add(dep_module.name)
687 elif dep_module.type == 'filegroup':
688 module.srcs.add(':' + dep_module.name)
689 elif dep_module.type == 'genrule':
690 module.generated_headers.update(dep_module.genrule_headers)
691 module.srcs.update(dep_module.genrule_srcs)
692 module.shared_libs.update(dep_module.genrule_shared_libs)
693 else:
694 raise Error('Unknown dep %s (%s) for target %s' %
695 (dep_module.name, dep_module.type, module.name))
696
697 return module
Sami Kyostila865d1d32017-12-12 18:37:04 +0000698
699
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100700def create_blueprint_for_targets(gn, desc, targets):
701 """Generate a blueprint for a list of GN targets."""
702 blueprint = Blueprint()
Sami Kyostila865d1d32017-12-12 18:37:04 +0000703
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100704 # Default settings used by all modules.
705 defaults = Module('cc_defaults', defaults_module, '//gn:default_deps')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000706
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100707 # We have to use include_dirs passing the path relative to the android tree.
708 # This is because: (i) perfetto_cc_defaults is used also by
709 # test/**/Android.bp; (ii) if we use local_include_dirs instead, paths
710 # become relative to the Android.bp that *uses* cc_defaults (not the one
711 # that defines it).s
712 defaults.include_dirs = {
713 tree_path, tree_path + '/include', tree_path + '/' + buildflags_dir
714 }
715 defaults.cflags = [
716 '-Wno-error=return-type',
717 '-Wno-sign-compare',
718 '-Wno-sign-promo',
719 '-Wno-unused-parameter',
720 '-fvisibility=hidden',
721 '-O2',
722 ]
723 defaults.user_debug_flag = True
724 defaults.lto = True
Sami Kyostila865d1d32017-12-12 18:37:04 +0000725
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100726 blueprint.add_module(defaults)
727 gn = gn_utils.GnParser(desc)
728 for target in targets:
729 create_modules_from_target(blueprint, gn, target)
730 return blueprint
Sami Kyostila865d1d32017-12-12 18:37:04 +0000731
732
733def main():
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100734 parser = argparse.ArgumentParser(
735 description='Generate Android.bp from a GN description.')
736 parser.add_argument(
737 '--check-only',
738 help='Don\'t keep the generated files',
739 action='store_true')
740 parser.add_argument(
741 '--desc',
Matthew Clarkson63028d62019-10-10 15:48:23 +0100742 help='GN description (e.g., gn desc out --format=json --all-toolchains "//*"'
743 )
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100744 parser.add_argument(
745 '--extras',
746 help='Extra targets to include at the end of the Blueprint file',
747 default=os.path.join(gn_utils.repo_root(), 'Android.bp.extras'),
748 )
749 parser.add_argument(
750 '--output',
751 help='Blueprint file to create',
752 default=os.path.join(gn_utils.repo_root(), 'Android.bp'),
753 )
754 parser.add_argument(
755 'targets',
756 nargs=argparse.REMAINDER,
757 help='Targets to include in the blueprint (e.g., "//:perfetto_tests")')
758 args = parser.parse_args()
Sami Kyostila865d1d32017-12-12 18:37:04 +0000759
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100760 if args.desc:
761 with open(args.desc) as f:
762 desc = json.load(f)
763 else:
764 desc = gn_utils.create_build_description(gn_args)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000765
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100766 gn = gn_utils.GnParser(desc)
767 blueprint = create_blueprint_for_targets(gn, desc, args.targets or
768 default_targets)
769 output = [
770 """// Copyright (C) 2017 The Android Open Source Project
Sami Kyostila865d1d32017-12-12 18:37:04 +0000771//
772// Licensed under the Apache License, Version 2.0 (the "License");
773// you may not use this file except in compliance with the License.
774// You may obtain a copy of the License at
775//
776// http://www.apache.org/licenses/LICENSE-2.0
777//
778// Unless required by applicable law or agreed to in writing, software
779// distributed under the License is distributed on an "AS IS" BASIS,
780// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
781// See the License for the specific language governing permissions and
782// limitations under the License.
783//
784// This file is automatically generated by %s. Do not edit.
785""" % (__file__)
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100786 ]
787 blueprint.to_string(output)
788 with open(args.extras, 'r') as r:
789 for line in r:
790 output.append(line.rstrip("\n\r"))
Primiano Tucci9c411652019-08-27 07:13:59 +0200791
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100792 out_files = []
Primiano Tucci9c411652019-08-27 07:13:59 +0200793
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100794 # Generate the Android.bp file.
795 out_files.append(args.output + '.swp')
796 with open(out_files[-1], 'w') as f:
797 f.write('\n'.join(output))
Sami Kyostila865d1d32017-12-12 18:37:04 +0000798
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100799 # Generate the perfetto_build_flags.h file.
800 out_files.append(os.path.join(buildflags_dir, 'perfetto_build_flags.h.swp'))
801 gn_utils.gen_buildflags(gn_args, out_files[-1])
Sami Kyostila865d1d32017-12-12 18:37:04 +0000802
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100803 # Either check the contents or move the files to their final destination.
804 return gn_utils.check_or_commit_generated_files(out_files, args.check_only)
Primiano Tucci9c411652019-08-27 07:13:59 +0200805
806
Sami Kyostila865d1d32017-12-12 18:37:04 +0000807if __name__ == '__main__':
Primiano Tuccif0d7ef82019-10-04 15:35:24 +0100808 sys.exit(main())