blob: e6a6d6c613bf5e5bee1fbbd0be05154318e6fabb [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
Primiano Tucciedf099c2018-01-08 18:27:56 +000029import errno
Sami Kyostila865d1d32017-12-12 18:37:04 +000030import json
31import os
32import re
Sami Kyostilab27619f2017-12-13 19:22:16 +000033import shutil
34import subprocess
Sami Kyostila865d1d32017-12-12 18:37:04 +000035import sys
36
Sami Kyostilab27619f2017-12-13 19:22:16 +000037# Default targets to translate to the blueprint file.
Primiano Tucci4e49c022017-12-21 18:22:44 +010038default_targets = [
Primiano Tucciedf099c2018-01-08 18:27:56 +000039 '//:libtraced_shared',
Lalit Maganti79f2d7b2018-01-23 18:27:33 +000040 '//:perfetto_integrationtests',
Primiano Tucci6aa75572018-03-21 05:33:14 -070041 '//:perfetto_trace_protos',
Lalit Maganti79f2d7b2018-01-23 18:27:33 +000042 '//:perfetto_unittests',
Primiano Tucci3b729102018-01-08 18:16:36 +000043 '//:perfetto',
Primiano Tucci4e49c022017-12-21 18:22:44 +010044 '//:traced',
Primiano Tucci6067e732018-01-08 16:19:40 +000045 '//:traced_probes',
Primiano Tucci21c19d82018-03-29 12:35:08 +010046 '//:trace_to_text',
Primiano Tucci4e49c022017-12-21 18:22:44 +010047]
Sami Kyostilab27619f2017-12-13 19:22:16 +000048
Primiano Tucci6067e732018-01-08 16:19:40 +000049# Defines a custom init_rc argument to be applied to the corresponding output
50# blueprint target.
Primiano Tucci5a304532018-01-09 14:15:43 +000051target_initrc = {
52 '//:traced': 'perfetto.rc',
53}
Primiano Tucci6067e732018-01-08 16:19:40 +000054
Primiano Tucci6aa75572018-03-21 05:33:14 -070055target_host_supported = [
56 '//:perfetto_trace_protos',
57]
58
Primiano Tucci21c19d82018-03-29 12:35:08 +010059target_host_only = [
60 '//:trace_to_text',
61]
62
Sami Kyostilab27619f2017-12-13 19:22:16 +000063# Arguments for the GN output directory.
Primiano Tucciedf099c2018-01-08 18:27:56 +000064gn_args = 'target_os="android" target_cpu="arm" is_debug=false build_with_android=true'
Sami Kyostilab27619f2017-12-13 19:22:16 +000065
Sami Kyostila865d1d32017-12-12 18:37:04 +000066# All module names are prefixed with this string to avoid collisions.
67module_prefix = 'perfetto_'
68
69# Shared libraries which are directly translated to Android system equivalents.
70library_whitelist = [
71 'android',
Sami Kyostilab5b71692018-01-12 12:16:44 +000072 'binder',
Sami Kyostila865d1d32017-12-12 18:37:04 +000073 'log',
Sami Kyostilab5b71692018-01-12 12:16:44 +000074 'services',
Primiano Tucciedf099c2018-01-08 18:27:56 +000075 'utils',
Sami Kyostila865d1d32017-12-12 18:37:04 +000076]
77
78# Name of the module which settings such as compiler flags for all other
79# modules.
80defaults_module = module_prefix + 'defaults'
81
82# Location of the project in the Android source tree.
83tree_path = 'external/perfetto'
84
Primiano Tucciedf099c2018-01-08 18:27:56 +000085# Compiler flags which are passed through to the blueprint.
86cflag_whitelist = r'^-DPERFETTO.*$'
87
Florian Mayer3d5e7e62018-01-19 15:22:46 +000088# Compiler defines which are passed through to the blueprint.
89define_whitelist = r'^GOOGLE_PROTO.*$'
90
Logan Chien9bfaaf92018-02-13 18:49:24 +080091# Shared libraries which are not in PDK.
92library_not_in_pdk = {
93 'libandroid',
94 'libservices',
95}
96
Sami Kyostila865d1d32017-12-12 18:37:04 +000097
98def enable_gmock(module):
99 module.static_libs.append('libgmock')
100
101
Hector Dearman3e712a02017-12-19 16:39:59 +0000102def enable_gtest_prod(module):
103 module.static_libs.append('libgtest_prod')
104
105
Sami Kyostila865d1d32017-12-12 18:37:04 +0000106def enable_gtest(module):
107 assert module.type == 'cc_test'
108
109
110def enable_protobuf_full(module):
111 module.shared_libs.append('libprotobuf-cpp-full')
112
113
114def enable_protobuf_lite(module):
115 module.shared_libs.append('libprotobuf-cpp-lite')
116
117
118def enable_protoc_lib(module):
119 module.shared_libs.append('libprotoc')
120
Florian Mayera2fae262018-08-31 12:10:01 -0700121def enable_libunwindstack(module):
122 module.shared_libs.append('libunwindstack')
123 module.shared_libs.append('libprocinfo')
124 module.shared_libs.append('libbase')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000125
126def enable_libunwind(module):
Sami Kyostilafc074d42017-12-15 10:33:42 +0000127 # libunwind is disabled on Darwin so we cannot depend on it.
128 pass
Sami Kyostila865d1d32017-12-12 18:37:04 +0000129
Sami Kyostila865d1d32017-12-12 18:37:04 +0000130# Android equivalents for third-party libraries that the upstream project
131# depends on.
132builtin_deps = {
133 '//buildtools:gmock': enable_gmock,
134 '//buildtools:gtest': enable_gtest,
Hector Dearman3e712a02017-12-19 16:39:59 +0000135 '//gn:gtest_prod_config': enable_gtest_prod,
Sami Kyostila865d1d32017-12-12 18:37:04 +0000136 '//buildtools:gtest_main': enable_gtest,
137 '//buildtools:libunwind': enable_libunwind,
138 '//buildtools:protobuf_full': enable_protobuf_full,
139 '//buildtools:protobuf_lite': enable_protobuf_lite,
140 '//buildtools:protoc_lib': enable_protoc_lib,
Florian Mayera2fae262018-08-31 12:10:01 -0700141 '//buildtools:libunwindstack': enable_libunwindstack,
Sami Kyostila865d1d32017-12-12 18:37:04 +0000142}
143
144# ----------------------------------------------------------------------------
145# End of configuration.
146# ----------------------------------------------------------------------------
147
148
149class Error(Exception):
150 pass
151
152
153class ThrowingArgumentParser(argparse.ArgumentParser):
154 def __init__(self, context):
155 super(ThrowingArgumentParser, self).__init__()
156 self.context = context
157
158 def error(self, message):
159 raise Error('%s: %s' % (self.context, message))
160
161
162class Module(object):
163 """A single module (e.g., cc_binary, cc_test) in a blueprint."""
164
165 def __init__(self, mod_type, name):
166 self.type = mod_type
167 self.name = name
168 self.srcs = []
169 self.comment = None
170 self.shared_libs = []
171 self.static_libs = []
172 self.tools = []
173 self.cmd = None
Primiano Tucci6aa75572018-03-21 05:33:14 -0700174 self.host_supported = False
Primiano Tucci6067e732018-01-08 16:19:40 +0000175 self.init_rc = []
Sami Kyostila865d1d32017-12-12 18:37:04 +0000176 self.out = []
177 self.export_include_dirs = []
178 self.generated_headers = []
Lalit Magantic5bcd792018-01-12 18:38:11 +0000179 self.export_generated_headers = []
Sami Kyostila865d1d32017-12-12 18:37:04 +0000180 self.defaults = []
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000181 self.cflags = set()
Sami Kyostila865d1d32017-12-12 18:37:04 +0000182 self.local_include_dirs = []
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100183 self.user_debug_flag = False
Sami Kyostila865d1d32017-12-12 18:37:04 +0000184
185 def to_string(self, output):
186 if self.comment:
187 output.append('// %s' % self.comment)
188 output.append('%s {' % self.type)
189 self._output_field(output, 'name')
190 self._output_field(output, 'srcs')
191 self._output_field(output, 'shared_libs')
192 self._output_field(output, 'static_libs')
193 self._output_field(output, 'tools')
194 self._output_field(output, 'cmd', sort=False)
Primiano Tucci6aa75572018-03-21 05:33:14 -0700195 self._output_field(output, 'host_supported')
Primiano Tucci6067e732018-01-08 16:19:40 +0000196 self._output_field(output, 'init_rc')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000197 self._output_field(output, 'out')
198 self._output_field(output, 'export_include_dirs')
199 self._output_field(output, 'generated_headers')
Lalit Magantic5bcd792018-01-12 18:38:11 +0000200 self._output_field(output, 'export_generated_headers')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000201 self._output_field(output, 'defaults')
202 self._output_field(output, 'cflags')
203 self._output_field(output, 'local_include_dirs')
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100204
205 disable_pdk = any(name in library_not_in_pdk for name in self.shared_libs)
206 if self.user_debug_flag or disable_pdk:
Logan Chien9bfaaf92018-02-13 18:49:24 +0800207 output.append(' product_variables: {')
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100208 if disable_pdk:
209 output.append(' pdk: {')
210 output.append(' enabled: false,')
211 output.append(' },')
212 if self.user_debug_flag:
213 output.append(' debuggable: {')
214 output.append(' cflags: ["-DPERFETTO_BUILD_WITH_ANDROID_USERDEBUG"],')
215 output.append(' },')
Logan Chien9bfaaf92018-02-13 18:49:24 +0800216 output.append(' },')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000217 output.append('}')
218 output.append('')
219
220 def _output_field(self, output, name, sort=True):
221 value = getattr(self, name)
222 if not value:
223 return
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000224 if isinstance(value, set):
Logan Chien9bfaaf92018-02-13 18:49:24 +0800225 value = sorted(value)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000226 if isinstance(value, list):
227 output.append(' %s: [' % name)
228 for item in sorted(value) if sort else value:
229 output.append(' "%s",' % item)
230 output.append(' ],')
Primiano Tucci6aa75572018-03-21 05:33:14 -0700231 return
232 if isinstance(value, bool):
233 output.append(' %s: true,' % name)
234 return
235 output.append(' %s: "%s",' % (name, value))
Sami Kyostila865d1d32017-12-12 18:37:04 +0000236
237
238class Blueprint(object):
239 """In-memory representation of an Android.bp file."""
240
241 def __init__(self):
242 self.modules = {}
243
244 def add_module(self, module):
245 """Adds a new module to the blueprint, replacing any existing module
246 with the same name.
247
248 Args:
249 module: Module instance.
250 """
251 self.modules[module.name] = module
252
253 def to_string(self, output):
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000254 for m in sorted(self.modules.itervalues(), key=lambda m: m.name):
Sami Kyostila865d1d32017-12-12 18:37:04 +0000255 m.to_string(output)
256
257
258def label_to_path(label):
259 """Turn a GN output label (e.g., //some_dir/file.cc) into a path."""
260 assert label.startswith('//')
261 return label[2:]
262
263
264def label_to_module_name(label):
265 """Turn a GN label (e.g., //:perfetto_tests) into a module name."""
Primiano Tucci4e49c022017-12-21 18:22:44 +0100266 module = re.sub(r'^//:?', '', label)
267 module = re.sub(r'[^a-zA-Z0-9_]', '_', module)
268 if not module.startswith(module_prefix) and label not in default_targets:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000269 return module_prefix + module
270 return module
271
272
273def label_without_toolchain(label):
274 """Strips the toolchain from a GN label.
275
276 Return a GN label (e.g //buildtools:protobuf(//gn/standalone/toolchain:
277 gcc_like_host) without the parenthesised toolchain part.
278 """
279 return label.split('(')[0]
280
281
282def is_supported_source_file(name):
283 """Returns True if |name| can appear in a 'srcs' list."""
284 return os.path.splitext(name)[1] in ['.c', '.cc', '.proto']
285
286
287def is_generated_by_action(desc, label):
288 """Checks if a label is generated by an action.
289
290 Returns True if a GN output label |label| is an output for any action,
291 i.e., the file is generated dynamically.
292 """
293 for target in desc.itervalues():
294 if target['type'] == 'action' and label in target['outputs']:
295 return True
296 return False
297
298
299def apply_module_dependency(blueprint, desc, module, dep_name):
300 """Recursively collect dependencies for a given module.
301
302 Walk the transitive dependencies for a GN target and apply them to a given
303 module. This effectively flattens the dependency tree so that |module|
304 directly contains all the sources, libraries, etc. in the corresponding GN
305 dependency tree.
306
307 Args:
308 blueprint: Blueprint instance which is being generated.
309 desc: JSON GN description.
310 module: Module to which dependencies should be added.
311 dep_name: GN target of the dependency.
312 """
Sami Kyostila865d1d32017-12-12 18:37:04 +0000313 # If the dependency refers to a library which we can replace with an Android
314 # equivalent, stop recursing and patch the dependency in.
315 if label_without_toolchain(dep_name) in builtin_deps:
316 builtin_deps[label_without_toolchain(dep_name)](module)
317 return
318
319 # Similarly some shared libraries are directly mapped to Android
320 # equivalents.
321 target = desc[dep_name]
322 for lib in target.get('libs', []):
323 android_lib = 'lib' + lib
324 if lib in library_whitelist and not android_lib in module.shared_libs:
325 module.shared_libs.append(android_lib)
326
327 type = target['type']
328 if type == 'action':
329 create_modules_from_target(blueprint, desc, dep_name)
330 # Depend both on the generated sources and headers -- see
331 # make_genrules_for_action.
332 module.srcs.append(':' + label_to_module_name(dep_name))
333 module.generated_headers.append(
334 label_to_module_name(dep_name) + '_headers')
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000335 elif type == 'static_library' and label_to_module_name(
336 dep_name) != module.name:
337 create_modules_from_target(blueprint, desc, dep_name)
338 module.static_libs.append(label_to_module_name(dep_name))
Primiano Tucci6067e732018-01-08 16:19:40 +0000339 elif type == 'shared_library' and label_to_module_name(
340 dep_name) != module.name:
341 module.shared_libs.append(label_to_module_name(dep_name))
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000342 elif type in ['group', 'source_set', 'executable', 'static_library'
343 ] and 'sources' in target:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000344 # Ignore source files that are generated by actions since they will be
345 # implicitly added by the genrule dependencies.
346 module.srcs.extend(
347 label_to_path(src) for src in target['sources']
348 if is_supported_source_file(src)
349 and not is_generated_by_action(desc, src))
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000350 module.cflags |= _get_cflags(target)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000351
352
353def make_genrules_for_action(blueprint, desc, target_name):
354 """Generate genrules for a GN action.
355
356 GN actions are used to dynamically generate files during the build. The
357 Soong equivalent is a genrule. This function turns a specific kind of
358 genrule which turns .proto files into source and header files into a pair
Sami Kyostila71625d72017-12-18 10:29:49 +0000359 equivalent genrules.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000360
361 Args:
362 blueprint: Blueprint instance which is being generated.
363 desc: JSON GN description.
364 target_name: GN target for genrule generation.
365
366 Returns:
367 A (source_genrule, header_genrule) module tuple.
368 """
369 target = desc[target_name]
370
371 # We only support genrules which call protoc (with or without a plugin) to
372 # turn .proto files into header and source files.
373 args = target['args']
374 if not args[0].endswith('/protoc'):
375 raise Error('Unsupported action in target %s: %s' % (target_name,
376 target['args']))
Primiano Tucci20b760c2018-01-19 12:36:12 +0000377 parser = ThrowingArgumentParser('Action in target %s (%s)' %
378 (target_name, ' '.join(target['args'])))
379 parser.add_argument('--proto_path')
380 parser.add_argument('--cpp_out')
381 parser.add_argument('--plugin')
382 parser.add_argument('--plugin_out')
Florian Mayerab55e3d2018-04-19 11:56:01 +0100383 parser.add_argument('--descriptor_set_out')
Primiano Tucci20b760c2018-01-19 12:36:12 +0000384 parser.add_argument('protos', nargs=argparse.REMAINDER)
385 args = parser.parse_args(args[1:])
386
387 # Depending on whether we are using the default protoc C++ generator or the
388 # protozero plugin, the output dir is passed as:
389 # --cpp_out=gen/xxx or
390 # --plugin_out=:gen/xxx or
391 # --plugin_out=wrapper_namespace=pbzero:gen/xxx
392 gen_dir = args.cpp_out if args.cpp_out else args.plugin_out.split(':')[1]
393 assert gen_dir.startswith('gen/')
394 gen_dir = gen_dir[4:]
395 cpp_out_dir = ('$(genDir)/%s/%s' % (tree_path, gen_dir)).rstrip('/')
396
397 # TODO(skyostil): Is there a way to avoid hardcoding the tree path here?
398 # TODO(skyostil): Find a way to avoid creating the directory.
399 cmd = [
400 'mkdir -p %s &&' % cpp_out_dir,
401 '$(location aprotoc)',
402 '--cpp_out=%s' % cpp_out_dir
403 ]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000404
405 # We create two genrules for each action: one for the protobuf headers and
406 # another for the sources. This is because the module that depends on the
407 # generated files needs to declare two different types of dependencies --
408 # source files in 'srcs' and headers in 'generated_headers' -- and it's not
409 # valid to generate .h files from a source dependency and vice versa.
Sami Kyostila71625d72017-12-18 10:29:49 +0000410 source_module = Module('genrule', label_to_module_name(target_name))
Sami Kyostila865d1d32017-12-12 18:37:04 +0000411 source_module.srcs.extend(label_to_path(src) for src in target['sources'])
412 source_module.tools = ['aprotoc']
413
Sami Kyostila71625d72017-12-18 10:29:49 +0000414 header_module = Module('genrule',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000415 label_to_module_name(target_name) + '_headers')
416 header_module.srcs = source_module.srcs[:]
417 header_module.tools = source_module.tools[:]
Primiano Tucci20b760c2018-01-19 12:36:12 +0000418 header_module.export_include_dirs = [gen_dir or '.']
Sami Kyostila865d1d32017-12-12 18:37:04 +0000419
Primiano Tucci20b760c2018-01-19 12:36:12 +0000420 # In GN builds the proto path is always relative to the output directory
421 # (out/tmp.xxx).
422 assert args.proto_path.startswith('../../')
423 cmd += [ '--proto_path=%s/%s' % (tree_path, args.proto_path[6:])]
424
Sami Kyostila865d1d32017-12-12 18:37:04 +0000425 namespaces = ['pb']
Sami Kyostila865d1d32017-12-12 18:37:04 +0000426 if args.plugin:
427 _, plugin = os.path.split(args.plugin)
428 # TODO(skyostil): Can we detect this some other way?
429 if plugin == 'ipc_plugin':
430 namespaces.append('ipc')
431 elif plugin == 'protoc_plugin':
432 namespaces = ['pbzero']
433 for dep in target['deps']:
434 if desc[dep]['type'] != 'executable':
435 continue
436 _, executable = os.path.split(desc[dep]['outputs'][0])
437 if executable == plugin:
438 cmd += [
439 '--plugin=protoc-gen-plugin=$(location %s)' %
440 label_to_module_name(dep)
441 ]
442 source_module.tools.append(label_to_module_name(dep))
443 # Also make sure the module for the tool is generated.
444 create_modules_from_target(blueprint, desc, dep)
445 break
446 else:
447 raise Error('Unrecognized protoc plugin in target %s: %s' %
448 (target_name, args[i]))
449 if args.plugin_out:
450 plugin_args = args.plugin_out.split(':')[0]
Primiano Tucci20b760c2018-01-19 12:36:12 +0000451 cmd += ['--plugin_out=%s:%s' % (plugin_args, cpp_out_dir)]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000452
453 cmd += ['$(in)']
454 source_module.cmd = ' '.join(cmd)
455 header_module.cmd = source_module.cmd
456 header_module.tools = source_module.tools[:]
457
458 for ns in namespaces:
459 source_module.out += [
460 '%s/%s' % (tree_path, src.replace('.proto', '.%s.cc' % ns))
461 for src in source_module.srcs
462 ]
463 header_module.out += [
464 '%s/%s' % (tree_path, src.replace('.proto', '.%s.h' % ns))
465 for src in header_module.srcs
466 ]
467 return source_module, header_module
468
469
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000470def _get_cflags(target):
471 cflags = set(flag for flag in target.get('cflags', [])
472 if re.match(cflag_whitelist, flag))
473 cflags |= set("-D%s" % define for define in target.get('defines', [])
474 if re.match(define_whitelist, define))
475 return cflags
476
477
Sami Kyostila865d1d32017-12-12 18:37:04 +0000478def create_modules_from_target(blueprint, desc, target_name):
479 """Generate module(s) for a given GN target.
480
481 Given a GN target name, generate one or more corresponding modules into a
482 blueprint.
483
484 Args:
485 blueprint: Blueprint instance which is being generated.
486 desc: JSON GN description.
487 target_name: GN target for module generation.
488 """
489 target = desc[target_name]
490 if target['type'] == 'executable':
Primiano Tucci21c19d82018-03-29 12:35:08 +0100491 if 'host' in target['toolchain'] or target_name in target_host_only:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000492 module_type = 'cc_binary_host'
493 elif target.get('testonly'):
494 module_type = 'cc_test'
495 else:
496 module_type = 'cc_binary'
497 modules = [Module(module_type, label_to_module_name(target_name))]
498 elif target['type'] == 'action':
499 modules = make_genrules_for_action(blueprint, desc, target_name)
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000500 elif target['type'] == 'static_library':
Lalit Magantic5bcd792018-01-12 18:38:11 +0000501 module = Module('cc_library_static', label_to_module_name(target_name))
502 module.export_include_dirs = ['include']
503 modules = [module]
Primiano Tucci6067e732018-01-08 16:19:40 +0000504 elif target['type'] == 'shared_library':
505 modules = [
506 Module('cc_library_shared', label_to_module_name(target_name))
507 ]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000508 else:
509 raise Error('Unknown target type: %s' % target['type'])
510
511 for module in modules:
512 module.comment = 'GN target: %s' % target_name
Primiano Tucci6067e732018-01-08 16:19:40 +0000513 if target_name in target_initrc:
514 module.init_rc = [target_initrc[target_name]]
Primiano Tucci6aa75572018-03-21 05:33:14 -0700515 if target_name in target_host_supported:
516 module.host_supported = True
Primiano Tucci6067e732018-01-08 16:19:40 +0000517
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000518 # Don't try to inject library/source dependencies into genrules because
519 # they are not compiled in the traditional sense.
Sami Kyostila71625d72017-12-18 10:29:49 +0000520 if module.type != 'genrule':
Sami Kyostila865d1d32017-12-12 18:37:04 +0000521 module.defaults = [defaults_module]
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000522 apply_module_dependency(blueprint, desc, module, target_name)
523 for dep in resolve_dependencies(desc, target_name):
524 apply_module_dependency(blueprint, desc, module, dep)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000525
Lalit Magantic5bcd792018-01-12 18:38:11 +0000526 # If the module is a static library, export all the generated headers.
527 if module.type == 'cc_library_static':
528 module.export_generated_headers = module.generated_headers
529
Sami Kyostila865d1d32017-12-12 18:37:04 +0000530 blueprint.add_module(module)
531
532
533def resolve_dependencies(desc, target_name):
534 """Return the transitive set of dependent-on targets for a GN target.
535
536 Args:
537 blueprint: Blueprint instance which is being generated.
538 desc: JSON GN description.
539
540 Returns:
541 A set of transitive dependencies in the form of GN targets.
542 """
543
544 if label_without_toolchain(target_name) in builtin_deps:
545 return set()
546 target = desc[target_name]
547 resolved_deps = set()
548 for dep in target.get('deps', []):
549 resolved_deps.add(dep)
550 # Ignore the transitive dependencies of actions because they are
551 # explicitly converted to genrules.
552 if desc[dep]['type'] == 'action':
553 continue
Primiano Tucci6067e732018-01-08 16:19:40 +0000554 # Dependencies on shared libraries shouldn't propagate any transitive
555 # dependencies but only depend on the shared library target
556 if desc[dep]['type'] == 'shared_library':
557 continue
Sami Kyostila865d1d32017-12-12 18:37:04 +0000558 resolved_deps.update(resolve_dependencies(desc, dep))
559 return resolved_deps
560
561
562def create_blueprint_for_targets(desc, targets):
563 """Generate a blueprint for a list of GN targets."""
564 blueprint = Blueprint()
565
566 # Default settings used by all modules.
567 defaults = Module('cc_defaults', defaults_module)
568 defaults.local_include_dirs = ['include']
569 defaults.cflags = [
570 '-Wno-error=return-type',
571 '-Wno-sign-compare',
572 '-Wno-sign-promo',
573 '-Wno-unused-parameter',
Florian Mayercc424fd2018-01-15 11:19:01 +0000574 '-fvisibility=hidden',
Florian Mayerc2a38ea2018-01-19 11:48:43 +0000575 '-Oz',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000576 ]
Lalit Magantid8b1a1d2018-05-23 14:41:43 +0100577 defaults.user_debug_flag = True
Sami Kyostila865d1d32017-12-12 18:37:04 +0000578
579 blueprint.add_module(defaults)
580 for target in targets:
581 create_modules_from_target(blueprint, desc, target)
582 return blueprint
583
584
Sami Kyostilab27619f2017-12-13 19:22:16 +0000585def repo_root():
586 """Returns an absolute path to the repository root."""
587
588 return os.path.join(
589 os.path.realpath(os.path.dirname(__file__)), os.path.pardir)
590
591
592def create_build_description():
593 """Creates the JSON build description by running GN."""
594
595 out = os.path.join(repo_root(), 'out', 'tmp.gen_android_bp')
596 try:
597 try:
598 os.makedirs(out)
599 except OSError as e:
600 if e.errno != errno.EEXIST:
601 raise
602 subprocess.check_output(
603 ['gn', 'gen', out, '--args=%s' % gn_args], cwd=repo_root())
604 desc = subprocess.check_output(
605 ['gn', 'desc', out, '--format=json', '--all-toolchains', '//*'],
606 cwd=repo_root())
607 return json.loads(desc)
608 finally:
609 shutil.rmtree(out)
610
611
Sami Kyostila865d1d32017-12-12 18:37:04 +0000612def main():
613 parser = argparse.ArgumentParser(
614 description='Generate Android.bp from a GN description.')
615 parser.add_argument(
Sami Kyostilab27619f2017-12-13 19:22:16 +0000616 '--desc',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000617 help=
618 'GN description (e.g., gn desc out --format=json --all-toolchains "//*"'
619 )
620 parser.add_argument(
Lalit Magantic5bcd792018-01-12 18:38:11 +0000621 '--extras',
622 help='Extra targets to include at the end of the Blueprint file',
623 default=os.path.join(repo_root(), 'Android.bp.extras'),
624 )
625 parser.add_argument(
Sami Kyostilab27619f2017-12-13 19:22:16 +0000626 '--output',
627 help='Blueprint file to create',
628 default=os.path.join(repo_root(), 'Android.bp'),
629 )
630 parser.add_argument(
Sami Kyostila865d1d32017-12-12 18:37:04 +0000631 'targets',
632 nargs=argparse.REMAINDER,
633 help='Targets to include in the blueprint (e.g., "//:perfetto_tests")')
634 args = parser.parse_args()
635
Sami Kyostilab27619f2017-12-13 19:22:16 +0000636 if args.desc:
637 with open(args.desc) as f:
638 desc = json.load(f)
639 else:
640 desc = create_build_description()
Sami Kyostila865d1d32017-12-12 18:37:04 +0000641
Sami Kyostilab27619f2017-12-13 19:22:16 +0000642 blueprint = create_blueprint_for_targets(desc, args.targets
643 or default_targets)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000644 output = [
645 """// Copyright (C) 2017 The Android Open Source Project
646//
647// Licensed under the Apache License, Version 2.0 (the "License");
648// you may not use this file except in compliance with the License.
649// You may obtain a copy of the License at
650//
651// http://www.apache.org/licenses/LICENSE-2.0
652//
653// Unless required by applicable law or agreed to in writing, software
654// distributed under the License is distributed on an "AS IS" BASIS,
655// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
656// See the License for the specific language governing permissions and
657// limitations under the License.
658//
659// This file is automatically generated by %s. Do not edit.
660""" % (__file__)
661 ]
662 blueprint.to_string(output)
Lalit Magantic5bcd792018-01-12 18:38:11 +0000663 with open(args.extras, 'r') as r:
664 for line in r:
665 output.append(line.rstrip("\n\r"))
Sami Kyostilab27619f2017-12-13 19:22:16 +0000666 with open(args.output, 'w') as f:
667 f.write('\n'.join(output))
Sami Kyostila865d1d32017-12-12 18:37:04 +0000668
669
670if __name__ == '__main__':
671 sys.exit(main())