blob: a7dafc5e9b0872f571e2f7e971c1e499be424cd7 [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',
41 '//:perfetto_unittests',
Primiano Tucci3b729102018-01-08 18:16:36 +000042 '//:perfetto',
Primiano Tucci4e49c022017-12-21 18:22:44 +010043 '//:traced',
Primiano Tucci6067e732018-01-08 16:19:40 +000044 '//:traced_probes',
Primiano Tucci4e49c022017-12-21 18:22:44 +010045]
Sami Kyostilab27619f2017-12-13 19:22:16 +000046
Primiano Tucci6067e732018-01-08 16:19:40 +000047# Defines a custom init_rc argument to be applied to the corresponding output
48# blueprint target.
Primiano Tucci5a304532018-01-09 14:15:43 +000049target_initrc = {
50 '//:traced': 'perfetto.rc',
51}
Primiano Tucci6067e732018-01-08 16:19:40 +000052
Sami Kyostilab27619f2017-12-13 19:22:16 +000053# Arguments for the GN output directory.
Primiano Tucciedf099c2018-01-08 18:27:56 +000054gn_args = 'target_os="android" target_cpu="arm" is_debug=false build_with_android=true'
Sami Kyostilab27619f2017-12-13 19:22:16 +000055
Sami Kyostila865d1d32017-12-12 18:37:04 +000056# All module names are prefixed with this string to avoid collisions.
57module_prefix = 'perfetto_'
58
59# Shared libraries which are directly translated to Android system equivalents.
60library_whitelist = [
61 'android',
Sami Kyostilab5b71692018-01-12 12:16:44 +000062 'binder',
Sami Kyostila865d1d32017-12-12 18:37:04 +000063 'log',
Sami Kyostilab5b71692018-01-12 12:16:44 +000064 'services',
Primiano Tucciedf099c2018-01-08 18:27:56 +000065 'utils',
Sami Kyostila865d1d32017-12-12 18:37:04 +000066]
67
68# Name of the module which settings such as compiler flags for all other
69# modules.
70defaults_module = module_prefix + 'defaults'
71
72# Location of the project in the Android source tree.
73tree_path = 'external/perfetto'
74
Primiano Tucciedf099c2018-01-08 18:27:56 +000075# Compiler flags which are passed through to the blueprint.
76cflag_whitelist = r'^-DPERFETTO.*$'
77
Florian Mayer3d5e7e62018-01-19 15:22:46 +000078# Compiler defines which are passed through to the blueprint.
79define_whitelist = r'^GOOGLE_PROTO.*$'
80
Logan Chien9bfaaf92018-02-13 18:49:24 +080081# Shared libraries which are not in PDK.
82library_not_in_pdk = {
83 'libandroid',
84 'libservices',
85}
86
Sami Kyostila865d1d32017-12-12 18:37:04 +000087
88def enable_gmock(module):
89 module.static_libs.append('libgmock')
90
91
Hector Dearman3e712a02017-12-19 16:39:59 +000092def enable_gtest_prod(module):
93 module.static_libs.append('libgtest_prod')
94
95
Sami Kyostila865d1d32017-12-12 18:37:04 +000096def enable_gtest(module):
97 assert module.type == 'cc_test'
98
99
100def enable_protobuf_full(module):
101 module.shared_libs.append('libprotobuf-cpp-full')
102
103
104def enable_protobuf_lite(module):
105 module.shared_libs.append('libprotobuf-cpp-lite')
106
107
108def enable_protoc_lib(module):
109 module.shared_libs.append('libprotoc')
110
111
112def enable_libunwind(module):
Sami Kyostilafc074d42017-12-15 10:33:42 +0000113 # libunwind is disabled on Darwin so we cannot depend on it.
114 pass
Sami Kyostila865d1d32017-12-12 18:37:04 +0000115
116
117# Android equivalents for third-party libraries that the upstream project
118# depends on.
119builtin_deps = {
120 '//buildtools:gmock': enable_gmock,
121 '//buildtools:gtest': enable_gtest,
Hector Dearman3e712a02017-12-19 16:39:59 +0000122 '//gn:gtest_prod_config': enable_gtest_prod,
Sami Kyostila865d1d32017-12-12 18:37:04 +0000123 '//buildtools:gtest_main': enable_gtest,
124 '//buildtools:libunwind': enable_libunwind,
125 '//buildtools:protobuf_full': enable_protobuf_full,
126 '//buildtools:protobuf_lite': enable_protobuf_lite,
127 '//buildtools:protoc_lib': enable_protoc_lib,
128}
129
130# ----------------------------------------------------------------------------
131# End of configuration.
132# ----------------------------------------------------------------------------
133
134
135class Error(Exception):
136 pass
137
138
139class ThrowingArgumentParser(argparse.ArgumentParser):
140 def __init__(self, context):
141 super(ThrowingArgumentParser, self).__init__()
142 self.context = context
143
144 def error(self, message):
145 raise Error('%s: %s' % (self.context, message))
146
147
148class Module(object):
149 """A single module (e.g., cc_binary, cc_test) in a blueprint."""
150
151 def __init__(self, mod_type, name):
152 self.type = mod_type
153 self.name = name
154 self.srcs = []
155 self.comment = None
156 self.shared_libs = []
157 self.static_libs = []
158 self.tools = []
159 self.cmd = None
Primiano Tucci6067e732018-01-08 16:19:40 +0000160 self.init_rc = []
Sami Kyostila865d1d32017-12-12 18:37:04 +0000161 self.out = []
162 self.export_include_dirs = []
163 self.generated_headers = []
Lalit Magantic5bcd792018-01-12 18:38:11 +0000164 self.export_generated_headers = []
Sami Kyostila865d1d32017-12-12 18:37:04 +0000165 self.defaults = []
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000166 self.cflags = set()
Sami Kyostila865d1d32017-12-12 18:37:04 +0000167 self.local_include_dirs = []
168
169 def to_string(self, output):
170 if self.comment:
171 output.append('// %s' % self.comment)
172 output.append('%s {' % self.type)
173 self._output_field(output, 'name')
174 self._output_field(output, 'srcs')
175 self._output_field(output, 'shared_libs')
176 self._output_field(output, 'static_libs')
177 self._output_field(output, 'tools')
178 self._output_field(output, 'cmd', sort=False)
Primiano Tucci6067e732018-01-08 16:19:40 +0000179 self._output_field(output, 'init_rc')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000180 self._output_field(output, 'out')
181 self._output_field(output, 'export_include_dirs')
182 self._output_field(output, 'generated_headers')
Lalit Magantic5bcd792018-01-12 18:38:11 +0000183 self._output_field(output, 'export_generated_headers')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000184 self._output_field(output, 'defaults')
185 self._output_field(output, 'cflags')
186 self._output_field(output, 'local_include_dirs')
Logan Chien9bfaaf92018-02-13 18:49:24 +0800187 if any(name in library_not_in_pdk for name in self.shared_libs):
188 output.append(' product_variables: {')
189 output.append(' pdk: {')
190 output.append(' enabled: false,')
191 output.append(' },')
192 output.append(' },')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000193 output.append('}')
194 output.append('')
195
196 def _output_field(self, output, name, sort=True):
197 value = getattr(self, name)
198 if not value:
199 return
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000200 if isinstance(value, set):
Logan Chien9bfaaf92018-02-13 18:49:24 +0800201 value = sorted(value)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000202 if isinstance(value, list):
203 output.append(' %s: [' % name)
204 for item in sorted(value) if sort else value:
205 output.append(' "%s",' % item)
206 output.append(' ],')
207 else:
208 output.append(' %s: "%s",' % (name, value))
209
210
211class Blueprint(object):
212 """In-memory representation of an Android.bp file."""
213
214 def __init__(self):
215 self.modules = {}
216
217 def add_module(self, module):
218 """Adds a new module to the blueprint, replacing any existing module
219 with the same name.
220
221 Args:
222 module: Module instance.
223 """
224 self.modules[module.name] = module
225
226 def to_string(self, output):
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000227 for m in sorted(self.modules.itervalues(), key=lambda m: m.name):
Sami Kyostila865d1d32017-12-12 18:37:04 +0000228 m.to_string(output)
229
230
231def label_to_path(label):
232 """Turn a GN output label (e.g., //some_dir/file.cc) into a path."""
233 assert label.startswith('//')
234 return label[2:]
235
236
237def label_to_module_name(label):
238 """Turn a GN label (e.g., //:perfetto_tests) into a module name."""
Primiano Tucci4e49c022017-12-21 18:22:44 +0100239 module = re.sub(r'^//:?', '', label)
240 module = re.sub(r'[^a-zA-Z0-9_]', '_', module)
241 if not module.startswith(module_prefix) and label not in default_targets:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000242 return module_prefix + module
243 return module
244
245
246def label_without_toolchain(label):
247 """Strips the toolchain from a GN label.
248
249 Return a GN label (e.g //buildtools:protobuf(//gn/standalone/toolchain:
250 gcc_like_host) without the parenthesised toolchain part.
251 """
252 return label.split('(')[0]
253
254
255def is_supported_source_file(name):
256 """Returns True if |name| can appear in a 'srcs' list."""
257 return os.path.splitext(name)[1] in ['.c', '.cc', '.proto']
258
259
260def is_generated_by_action(desc, label):
261 """Checks if a label is generated by an action.
262
263 Returns True if a GN output label |label| is an output for any action,
264 i.e., the file is generated dynamically.
265 """
266 for target in desc.itervalues():
267 if target['type'] == 'action' and label in target['outputs']:
268 return True
269 return False
270
271
272def apply_module_dependency(blueprint, desc, module, dep_name):
273 """Recursively collect dependencies for a given module.
274
275 Walk the transitive dependencies for a GN target and apply them to a given
276 module. This effectively flattens the dependency tree so that |module|
277 directly contains all the sources, libraries, etc. in the corresponding GN
278 dependency tree.
279
280 Args:
281 blueprint: Blueprint instance which is being generated.
282 desc: JSON GN description.
283 module: Module to which dependencies should be added.
284 dep_name: GN target of the dependency.
285 """
Sami Kyostila865d1d32017-12-12 18:37:04 +0000286 # If the dependency refers to a library which we can replace with an Android
287 # equivalent, stop recursing and patch the dependency in.
288 if label_without_toolchain(dep_name) in builtin_deps:
289 builtin_deps[label_without_toolchain(dep_name)](module)
290 return
291
292 # Similarly some shared libraries are directly mapped to Android
293 # equivalents.
294 target = desc[dep_name]
295 for lib in target.get('libs', []):
296 android_lib = 'lib' + lib
297 if lib in library_whitelist and not android_lib in module.shared_libs:
298 module.shared_libs.append(android_lib)
299
300 type = target['type']
301 if type == 'action':
302 create_modules_from_target(blueprint, desc, dep_name)
303 # Depend both on the generated sources and headers -- see
304 # make_genrules_for_action.
305 module.srcs.append(':' + label_to_module_name(dep_name))
306 module.generated_headers.append(
307 label_to_module_name(dep_name) + '_headers')
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000308 elif type == 'static_library' and label_to_module_name(
309 dep_name) != module.name:
310 create_modules_from_target(blueprint, desc, dep_name)
311 module.static_libs.append(label_to_module_name(dep_name))
Primiano Tucci6067e732018-01-08 16:19:40 +0000312 elif type == 'shared_library' and label_to_module_name(
313 dep_name) != module.name:
314 module.shared_libs.append(label_to_module_name(dep_name))
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000315 elif type in ['group', 'source_set', 'executable', 'static_library'
316 ] and 'sources' in target:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000317 # Ignore source files that are generated by actions since they will be
318 # implicitly added by the genrule dependencies.
319 module.srcs.extend(
320 label_to_path(src) for src in target['sources']
321 if is_supported_source_file(src)
322 and not is_generated_by_action(desc, src))
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000323 module.cflags |= _get_cflags(target)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000324
325
326def make_genrules_for_action(blueprint, desc, target_name):
327 """Generate genrules for a GN action.
328
329 GN actions are used to dynamically generate files during the build. The
330 Soong equivalent is a genrule. This function turns a specific kind of
331 genrule which turns .proto files into source and header files into a pair
Sami Kyostila71625d72017-12-18 10:29:49 +0000332 equivalent genrules.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000333
334 Args:
335 blueprint: Blueprint instance which is being generated.
336 desc: JSON GN description.
337 target_name: GN target for genrule generation.
338
339 Returns:
340 A (source_genrule, header_genrule) module tuple.
341 """
342 target = desc[target_name]
343
344 # We only support genrules which call protoc (with or without a plugin) to
345 # turn .proto files into header and source files.
346 args = target['args']
347 if not args[0].endswith('/protoc'):
348 raise Error('Unsupported action in target %s: %s' % (target_name,
349 target['args']))
Primiano Tucci20b760c2018-01-19 12:36:12 +0000350 parser = ThrowingArgumentParser('Action in target %s (%s)' %
351 (target_name, ' '.join(target['args'])))
352 parser.add_argument('--proto_path')
353 parser.add_argument('--cpp_out')
354 parser.add_argument('--plugin')
355 parser.add_argument('--plugin_out')
356 parser.add_argument('protos', nargs=argparse.REMAINDER)
357 args = parser.parse_args(args[1:])
358
359 # Depending on whether we are using the default protoc C++ generator or the
360 # protozero plugin, the output dir is passed as:
361 # --cpp_out=gen/xxx or
362 # --plugin_out=:gen/xxx or
363 # --plugin_out=wrapper_namespace=pbzero:gen/xxx
364 gen_dir = args.cpp_out if args.cpp_out else args.plugin_out.split(':')[1]
365 assert gen_dir.startswith('gen/')
366 gen_dir = gen_dir[4:]
367 cpp_out_dir = ('$(genDir)/%s/%s' % (tree_path, gen_dir)).rstrip('/')
368
369 # TODO(skyostil): Is there a way to avoid hardcoding the tree path here?
370 # TODO(skyostil): Find a way to avoid creating the directory.
371 cmd = [
372 'mkdir -p %s &&' % cpp_out_dir,
373 '$(location aprotoc)',
374 '--cpp_out=%s' % cpp_out_dir
375 ]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000376
377 # We create two genrules for each action: one for the protobuf headers and
378 # another for the sources. This is because the module that depends on the
379 # generated files needs to declare two different types of dependencies --
380 # source files in 'srcs' and headers in 'generated_headers' -- and it's not
381 # valid to generate .h files from a source dependency and vice versa.
Sami Kyostila71625d72017-12-18 10:29:49 +0000382 source_module = Module('genrule', label_to_module_name(target_name))
Sami Kyostila865d1d32017-12-12 18:37:04 +0000383 source_module.srcs.extend(label_to_path(src) for src in target['sources'])
384 source_module.tools = ['aprotoc']
385
Sami Kyostila71625d72017-12-18 10:29:49 +0000386 header_module = Module('genrule',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000387 label_to_module_name(target_name) + '_headers')
388 header_module.srcs = source_module.srcs[:]
389 header_module.tools = source_module.tools[:]
Primiano Tucci20b760c2018-01-19 12:36:12 +0000390 header_module.export_include_dirs = [gen_dir or '.']
Sami Kyostila865d1d32017-12-12 18:37:04 +0000391
Primiano Tucci20b760c2018-01-19 12:36:12 +0000392 # In GN builds the proto path is always relative to the output directory
393 # (out/tmp.xxx).
394 assert args.proto_path.startswith('../../')
395 cmd += [ '--proto_path=%s/%s' % (tree_path, args.proto_path[6:])]
396
Sami Kyostila865d1d32017-12-12 18:37:04 +0000397 namespaces = ['pb']
Sami Kyostila865d1d32017-12-12 18:37:04 +0000398 if args.plugin:
399 _, plugin = os.path.split(args.plugin)
400 # TODO(skyostil): Can we detect this some other way?
401 if plugin == 'ipc_plugin':
402 namespaces.append('ipc')
403 elif plugin == 'protoc_plugin':
404 namespaces = ['pbzero']
405 for dep in target['deps']:
406 if desc[dep]['type'] != 'executable':
407 continue
408 _, executable = os.path.split(desc[dep]['outputs'][0])
409 if executable == plugin:
410 cmd += [
411 '--plugin=protoc-gen-plugin=$(location %s)' %
412 label_to_module_name(dep)
413 ]
414 source_module.tools.append(label_to_module_name(dep))
415 # Also make sure the module for the tool is generated.
416 create_modules_from_target(blueprint, desc, dep)
417 break
418 else:
419 raise Error('Unrecognized protoc plugin in target %s: %s' %
420 (target_name, args[i]))
421 if args.plugin_out:
422 plugin_args = args.plugin_out.split(':')[0]
Primiano Tucci20b760c2018-01-19 12:36:12 +0000423 cmd += ['--plugin_out=%s:%s' % (plugin_args, cpp_out_dir)]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000424
425 cmd += ['$(in)']
426 source_module.cmd = ' '.join(cmd)
427 header_module.cmd = source_module.cmd
428 header_module.tools = source_module.tools[:]
429
430 for ns in namespaces:
431 source_module.out += [
432 '%s/%s' % (tree_path, src.replace('.proto', '.%s.cc' % ns))
433 for src in source_module.srcs
434 ]
435 header_module.out += [
436 '%s/%s' % (tree_path, src.replace('.proto', '.%s.h' % ns))
437 for src in header_module.srcs
438 ]
439 return source_module, header_module
440
441
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000442def _get_cflags(target):
443 cflags = set(flag for flag in target.get('cflags', [])
444 if re.match(cflag_whitelist, flag))
445 cflags |= set("-D%s" % define for define in target.get('defines', [])
446 if re.match(define_whitelist, define))
447 return cflags
448
449
Sami Kyostila865d1d32017-12-12 18:37:04 +0000450def create_modules_from_target(blueprint, desc, target_name):
451 """Generate module(s) for a given GN target.
452
453 Given a GN target name, generate one or more corresponding modules into a
454 blueprint.
455
456 Args:
457 blueprint: Blueprint instance which is being generated.
458 desc: JSON GN description.
459 target_name: GN target for module generation.
460 """
461 target = desc[target_name]
462 if target['type'] == 'executable':
463 if 'host' in target['toolchain']:
464 module_type = 'cc_binary_host'
465 elif target.get('testonly'):
466 module_type = 'cc_test'
467 else:
468 module_type = 'cc_binary'
469 modules = [Module(module_type, label_to_module_name(target_name))]
470 elif target['type'] == 'action':
471 modules = make_genrules_for_action(blueprint, desc, target_name)
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000472 elif target['type'] == 'static_library':
Lalit Magantic5bcd792018-01-12 18:38:11 +0000473 module = Module('cc_library_static', label_to_module_name(target_name))
474 module.export_include_dirs = ['include']
475 modules = [module]
Primiano Tucci6067e732018-01-08 16:19:40 +0000476 elif target['type'] == 'shared_library':
477 modules = [
478 Module('cc_library_shared', label_to_module_name(target_name))
479 ]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000480 else:
481 raise Error('Unknown target type: %s' % target['type'])
482
483 for module in modules:
484 module.comment = 'GN target: %s' % target_name
Primiano Tucci6067e732018-01-08 16:19:40 +0000485 if target_name in target_initrc:
486 module.init_rc = [target_initrc[target_name]]
487
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000488 # Don't try to inject library/source dependencies into genrules because
489 # they are not compiled in the traditional sense.
Sami Kyostila71625d72017-12-18 10:29:49 +0000490 if module.type != 'genrule':
Sami Kyostila865d1d32017-12-12 18:37:04 +0000491 module.defaults = [defaults_module]
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000492 apply_module_dependency(blueprint, desc, module, target_name)
493 for dep in resolve_dependencies(desc, target_name):
494 apply_module_dependency(blueprint, desc, module, dep)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000495
Lalit Magantic5bcd792018-01-12 18:38:11 +0000496 # If the module is a static library, export all the generated headers.
497 if module.type == 'cc_library_static':
498 module.export_generated_headers = module.generated_headers
499
Sami Kyostila865d1d32017-12-12 18:37:04 +0000500 blueprint.add_module(module)
501
502
503def resolve_dependencies(desc, target_name):
504 """Return the transitive set of dependent-on targets for a GN target.
505
506 Args:
507 blueprint: Blueprint instance which is being generated.
508 desc: JSON GN description.
509
510 Returns:
511 A set of transitive dependencies in the form of GN targets.
512 """
513
514 if label_without_toolchain(target_name) in builtin_deps:
515 return set()
516 target = desc[target_name]
517 resolved_deps = set()
518 for dep in target.get('deps', []):
519 resolved_deps.add(dep)
520 # Ignore the transitive dependencies of actions because they are
521 # explicitly converted to genrules.
522 if desc[dep]['type'] == 'action':
523 continue
Primiano Tucci6067e732018-01-08 16:19:40 +0000524 # Dependencies on shared libraries shouldn't propagate any transitive
525 # dependencies but only depend on the shared library target
526 if desc[dep]['type'] == 'shared_library':
527 continue
Sami Kyostila865d1d32017-12-12 18:37:04 +0000528 resolved_deps.update(resolve_dependencies(desc, dep))
529 return resolved_deps
530
531
532def create_blueprint_for_targets(desc, targets):
533 """Generate a blueprint for a list of GN targets."""
534 blueprint = Blueprint()
535
536 # Default settings used by all modules.
537 defaults = Module('cc_defaults', defaults_module)
538 defaults.local_include_dirs = ['include']
539 defaults.cflags = [
540 '-Wno-error=return-type',
541 '-Wno-sign-compare',
542 '-Wno-sign-promo',
543 '-Wno-unused-parameter',
Florian Mayercc424fd2018-01-15 11:19:01 +0000544 '-fvisibility=hidden',
Florian Mayerc2a38ea2018-01-19 11:48:43 +0000545 '-Oz',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000546 ]
547
548 blueprint.add_module(defaults)
549 for target in targets:
550 create_modules_from_target(blueprint, desc, target)
551 return blueprint
552
553
Sami Kyostilab27619f2017-12-13 19:22:16 +0000554def repo_root():
555 """Returns an absolute path to the repository root."""
556
557 return os.path.join(
558 os.path.realpath(os.path.dirname(__file__)), os.path.pardir)
559
560
561def create_build_description():
562 """Creates the JSON build description by running GN."""
563
564 out = os.path.join(repo_root(), 'out', 'tmp.gen_android_bp')
565 try:
566 try:
567 os.makedirs(out)
568 except OSError as e:
569 if e.errno != errno.EEXIST:
570 raise
571 subprocess.check_output(
572 ['gn', 'gen', out, '--args=%s' % gn_args], cwd=repo_root())
573 desc = subprocess.check_output(
574 ['gn', 'desc', out, '--format=json', '--all-toolchains', '//*'],
575 cwd=repo_root())
576 return json.loads(desc)
577 finally:
578 shutil.rmtree(out)
579
580
Sami Kyostila865d1d32017-12-12 18:37:04 +0000581def main():
582 parser = argparse.ArgumentParser(
583 description='Generate Android.bp from a GN description.')
584 parser.add_argument(
Sami Kyostilab27619f2017-12-13 19:22:16 +0000585 '--desc',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000586 help=
587 'GN description (e.g., gn desc out --format=json --all-toolchains "//*"'
588 )
589 parser.add_argument(
Lalit Magantic5bcd792018-01-12 18:38:11 +0000590 '--extras',
591 help='Extra targets to include at the end of the Blueprint file',
592 default=os.path.join(repo_root(), 'Android.bp.extras'),
593 )
594 parser.add_argument(
Sami Kyostilab27619f2017-12-13 19:22:16 +0000595 '--output',
596 help='Blueprint file to create',
597 default=os.path.join(repo_root(), 'Android.bp'),
598 )
599 parser.add_argument(
Sami Kyostila865d1d32017-12-12 18:37:04 +0000600 'targets',
601 nargs=argparse.REMAINDER,
602 help='Targets to include in the blueprint (e.g., "//:perfetto_tests")')
603 args = parser.parse_args()
604
Sami Kyostilab27619f2017-12-13 19:22:16 +0000605 if args.desc:
606 with open(args.desc) as f:
607 desc = json.load(f)
608 else:
609 desc = create_build_description()
Sami Kyostila865d1d32017-12-12 18:37:04 +0000610
Sami Kyostilab27619f2017-12-13 19:22:16 +0000611 blueprint = create_blueprint_for_targets(desc, args.targets
612 or default_targets)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000613 output = [
614 """// Copyright (C) 2017 The Android Open Source Project
615//
616// Licensed under the Apache License, Version 2.0 (the "License");
617// you may not use this file except in compliance with the License.
618// You may obtain a copy of the License at
619//
620// http://www.apache.org/licenses/LICENSE-2.0
621//
622// Unless required by applicable law or agreed to in writing, software
623// distributed under the License is distributed on an "AS IS" BASIS,
624// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
625// See the License for the specific language governing permissions and
626// limitations under the License.
627//
628// This file is automatically generated by %s. Do not edit.
629""" % (__file__)
630 ]
631 blueprint.to_string(output)
Lalit Magantic5bcd792018-01-12 18:38:11 +0000632 with open(args.extras, 'r') as r:
633 for line in r:
634 output.append(line.rstrip("\n\r"))
Sami Kyostilab27619f2017-12-13 19:22:16 +0000635 with open(args.output, 'w') as f:
636 f.write('\n'.join(output))
Sami Kyostila865d1d32017-12-12 18:37:04 +0000637
638
639if __name__ == '__main__':
640 sys.exit(main())