blob: b2c4c0f5f7628aa6cc47c45f34c52a70bac5d874 [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',
Primiano Tucci4e49c022017-12-21 18:22:44 +010040 '//:perfetto_tests',
Primiano Tucci3b729102018-01-08 18:16:36 +000041 '//:perfetto',
Primiano Tucci4e49c022017-12-21 18:22:44 +010042 '//:traced',
Primiano Tucci6067e732018-01-08 16:19:40 +000043 '//:traced_probes',
Primiano Tucci4e49c022017-12-21 18:22:44 +010044]
Sami Kyostilab27619f2017-12-13 19:22:16 +000045
Primiano Tucci6067e732018-01-08 16:19:40 +000046# Defines a custom init_rc argument to be applied to the corresponding output
47# blueprint target.
Primiano Tucci5a304532018-01-09 14:15:43 +000048target_initrc = {
49 '//:traced': 'perfetto.rc',
50}
Primiano Tucci6067e732018-01-08 16:19:40 +000051
Sami Kyostilab27619f2017-12-13 19:22:16 +000052# Arguments for the GN output directory.
Primiano Tucciedf099c2018-01-08 18:27:56 +000053gn_args = 'target_os="android" target_cpu="arm" is_debug=false build_with_android=true'
Sami Kyostilab27619f2017-12-13 19:22:16 +000054
Sami Kyostila865d1d32017-12-12 18:37:04 +000055# All module names are prefixed with this string to avoid collisions.
56module_prefix = 'perfetto_'
57
58# Shared libraries which are directly translated to Android system equivalents.
59library_whitelist = [
60 'android',
Sami Kyostilab5b71692018-01-12 12:16:44 +000061 'binder',
Sami Kyostila865d1d32017-12-12 18:37:04 +000062 'log',
Sami Kyostilab5b71692018-01-12 12:16:44 +000063 'services',
Primiano Tucciedf099c2018-01-08 18:27:56 +000064 'utils',
Sami Kyostila865d1d32017-12-12 18:37:04 +000065]
66
67# Name of the module which settings such as compiler flags for all other
68# modules.
69defaults_module = module_prefix + 'defaults'
70
71# Location of the project in the Android source tree.
72tree_path = 'external/perfetto'
73
Primiano Tucciedf099c2018-01-08 18:27:56 +000074# Compiler flags which are passed through to the blueprint.
75cflag_whitelist = r'^-DPERFETTO.*$'
76
Florian Mayer3d5e7e62018-01-19 15:22:46 +000077# Compiler defines which are passed through to the blueprint.
78define_whitelist = r'^GOOGLE_PROTO.*$'
79
Sami Kyostila865d1d32017-12-12 18:37:04 +000080
81def enable_gmock(module):
82 module.static_libs.append('libgmock')
83
84
Hector Dearman3e712a02017-12-19 16:39:59 +000085def enable_gtest_prod(module):
86 module.static_libs.append('libgtest_prod')
87
88
Sami Kyostila865d1d32017-12-12 18:37:04 +000089def enable_gtest(module):
90 assert module.type == 'cc_test'
91
92
93def enable_protobuf_full(module):
94 module.shared_libs.append('libprotobuf-cpp-full')
95
96
97def enable_protobuf_lite(module):
98 module.shared_libs.append('libprotobuf-cpp-lite')
99
100
101def enable_protoc_lib(module):
102 module.shared_libs.append('libprotoc')
103
104
105def enable_libunwind(module):
Sami Kyostilafc074d42017-12-15 10:33:42 +0000106 # libunwind is disabled on Darwin so we cannot depend on it.
107 pass
Sami Kyostila865d1d32017-12-12 18:37:04 +0000108
109
110# Android equivalents for third-party libraries that the upstream project
111# depends on.
112builtin_deps = {
113 '//buildtools:gmock': enable_gmock,
114 '//buildtools:gtest': enable_gtest,
Hector Dearman3e712a02017-12-19 16:39:59 +0000115 '//gn:gtest_prod_config': enable_gtest_prod,
Sami Kyostila865d1d32017-12-12 18:37:04 +0000116 '//buildtools:gtest_main': enable_gtest,
117 '//buildtools:libunwind': enable_libunwind,
118 '//buildtools:protobuf_full': enable_protobuf_full,
119 '//buildtools:protobuf_lite': enable_protobuf_lite,
120 '//buildtools:protoc_lib': enable_protoc_lib,
121}
122
123# ----------------------------------------------------------------------------
124# End of configuration.
125# ----------------------------------------------------------------------------
126
127
128class Error(Exception):
129 pass
130
131
132class ThrowingArgumentParser(argparse.ArgumentParser):
133 def __init__(self, context):
134 super(ThrowingArgumentParser, self).__init__()
135 self.context = context
136
137 def error(self, message):
138 raise Error('%s: %s' % (self.context, message))
139
140
141class Module(object):
142 """A single module (e.g., cc_binary, cc_test) in a blueprint."""
143
144 def __init__(self, mod_type, name):
145 self.type = mod_type
146 self.name = name
147 self.srcs = []
148 self.comment = None
149 self.shared_libs = []
150 self.static_libs = []
151 self.tools = []
152 self.cmd = None
Primiano Tucci6067e732018-01-08 16:19:40 +0000153 self.init_rc = []
Sami Kyostila865d1d32017-12-12 18:37:04 +0000154 self.out = []
155 self.export_include_dirs = []
156 self.generated_headers = []
Lalit Magantic5bcd792018-01-12 18:38:11 +0000157 self.export_generated_headers = []
Sami Kyostila865d1d32017-12-12 18:37:04 +0000158 self.defaults = []
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000159 self.cflags = set()
Sami Kyostila865d1d32017-12-12 18:37:04 +0000160 self.local_include_dirs = []
161
162 def to_string(self, output):
163 if self.comment:
164 output.append('// %s' % self.comment)
165 output.append('%s {' % self.type)
166 self._output_field(output, 'name')
167 self._output_field(output, 'srcs')
168 self._output_field(output, 'shared_libs')
169 self._output_field(output, 'static_libs')
170 self._output_field(output, 'tools')
171 self._output_field(output, 'cmd', sort=False)
Primiano Tucci6067e732018-01-08 16:19:40 +0000172 self._output_field(output, 'init_rc')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000173 self._output_field(output, 'out')
174 self._output_field(output, 'export_include_dirs')
175 self._output_field(output, 'generated_headers')
Lalit Magantic5bcd792018-01-12 18:38:11 +0000176 self._output_field(output, 'export_generated_headers')
Sami Kyostila865d1d32017-12-12 18:37:04 +0000177 self._output_field(output, 'defaults')
178 self._output_field(output, 'cflags')
179 self._output_field(output, 'local_include_dirs')
180 output.append('}')
181 output.append('')
182
183 def _output_field(self, output, name, sort=True):
184 value = getattr(self, name)
185 if not value:
186 return
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000187 if isinstance(value, set):
188 value = sorted(value)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000189 if isinstance(value, list):
190 output.append(' %s: [' % name)
191 for item in sorted(value) if sort else value:
192 output.append(' "%s",' % item)
193 output.append(' ],')
194 else:
195 output.append(' %s: "%s",' % (name, value))
196
197
198class Blueprint(object):
199 """In-memory representation of an Android.bp file."""
200
201 def __init__(self):
202 self.modules = {}
203
204 def add_module(self, module):
205 """Adds a new module to the blueprint, replacing any existing module
206 with the same name.
207
208 Args:
209 module: Module instance.
210 """
211 self.modules[module.name] = module
212
213 def to_string(self, output):
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000214 for m in sorted(self.modules.itervalues(), key=lambda m: m.name):
Sami Kyostila865d1d32017-12-12 18:37:04 +0000215 m.to_string(output)
216
217
218def label_to_path(label):
219 """Turn a GN output label (e.g., //some_dir/file.cc) into a path."""
220 assert label.startswith('//')
221 return label[2:]
222
223
224def label_to_module_name(label):
225 """Turn a GN label (e.g., //:perfetto_tests) into a module name."""
Primiano Tucci4e49c022017-12-21 18:22:44 +0100226 module = re.sub(r'^//:?', '', label)
227 module = re.sub(r'[^a-zA-Z0-9_]', '_', module)
228 if not module.startswith(module_prefix) and label not in default_targets:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000229 return module_prefix + module
230 return module
231
232
233def label_without_toolchain(label):
234 """Strips the toolchain from a GN label.
235
236 Return a GN label (e.g //buildtools:protobuf(//gn/standalone/toolchain:
237 gcc_like_host) without the parenthesised toolchain part.
238 """
239 return label.split('(')[0]
240
241
242def is_supported_source_file(name):
243 """Returns True if |name| can appear in a 'srcs' list."""
244 return os.path.splitext(name)[1] in ['.c', '.cc', '.proto']
245
246
247def is_generated_by_action(desc, label):
248 """Checks if a label is generated by an action.
249
250 Returns True if a GN output label |label| is an output for any action,
251 i.e., the file is generated dynamically.
252 """
253 for target in desc.itervalues():
254 if target['type'] == 'action' and label in target['outputs']:
255 return True
256 return False
257
258
259def apply_module_dependency(blueprint, desc, module, dep_name):
260 """Recursively collect dependencies for a given module.
261
262 Walk the transitive dependencies for a GN target and apply them to a given
263 module. This effectively flattens the dependency tree so that |module|
264 directly contains all the sources, libraries, etc. in the corresponding GN
265 dependency tree.
266
267 Args:
268 blueprint: Blueprint instance which is being generated.
269 desc: JSON GN description.
270 module: Module to which dependencies should be added.
271 dep_name: GN target of the dependency.
272 """
Sami Kyostila865d1d32017-12-12 18:37:04 +0000273 # If the dependency refers to a library which we can replace with an Android
274 # equivalent, stop recursing and patch the dependency in.
275 if label_without_toolchain(dep_name) in builtin_deps:
276 builtin_deps[label_without_toolchain(dep_name)](module)
277 return
278
279 # Similarly some shared libraries are directly mapped to Android
280 # equivalents.
281 target = desc[dep_name]
282 for lib in target.get('libs', []):
283 android_lib = 'lib' + lib
284 if lib in library_whitelist and not android_lib in module.shared_libs:
285 module.shared_libs.append(android_lib)
286
287 type = target['type']
288 if type == 'action':
289 create_modules_from_target(blueprint, desc, dep_name)
290 # Depend both on the generated sources and headers -- see
291 # make_genrules_for_action.
292 module.srcs.append(':' + label_to_module_name(dep_name))
293 module.generated_headers.append(
294 label_to_module_name(dep_name) + '_headers')
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000295 elif type == 'static_library' and label_to_module_name(
296 dep_name) != module.name:
297 create_modules_from_target(blueprint, desc, dep_name)
298 module.static_libs.append(label_to_module_name(dep_name))
Primiano Tucci6067e732018-01-08 16:19:40 +0000299 elif type == 'shared_library' and label_to_module_name(
300 dep_name) != module.name:
301 module.shared_libs.append(label_to_module_name(dep_name))
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000302 elif type in ['group', 'source_set', 'executable', 'static_library'
303 ] and 'sources' in target:
Sami Kyostila865d1d32017-12-12 18:37:04 +0000304 # Ignore source files that are generated by actions since they will be
305 # implicitly added by the genrule dependencies.
306 module.srcs.extend(
307 label_to_path(src) for src in target['sources']
308 if is_supported_source_file(src)
309 and not is_generated_by_action(desc, src))
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000310 module.cflags |= _get_cflags(target)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000311
312
313def make_genrules_for_action(blueprint, desc, target_name):
314 """Generate genrules for a GN action.
315
316 GN actions are used to dynamically generate files during the build. The
317 Soong equivalent is a genrule. This function turns a specific kind of
318 genrule which turns .proto files into source and header files into a pair
Sami Kyostila71625d72017-12-18 10:29:49 +0000319 equivalent genrules.
Sami Kyostila865d1d32017-12-12 18:37:04 +0000320
321 Args:
322 blueprint: Blueprint instance which is being generated.
323 desc: JSON GN description.
324 target_name: GN target for genrule generation.
325
326 Returns:
327 A (source_genrule, header_genrule) module tuple.
328 """
329 target = desc[target_name]
330
331 # We only support genrules which call protoc (with or without a plugin) to
332 # turn .proto files into header and source files.
333 args = target['args']
334 if not args[0].endswith('/protoc'):
335 raise Error('Unsupported action in target %s: %s' % (target_name,
336 target['args']))
Primiano Tucci20b760c2018-01-19 12:36:12 +0000337 parser = ThrowingArgumentParser('Action in target %s (%s)' %
338 (target_name, ' '.join(target['args'])))
339 parser.add_argument('--proto_path')
340 parser.add_argument('--cpp_out')
341 parser.add_argument('--plugin')
342 parser.add_argument('--plugin_out')
343 parser.add_argument('protos', nargs=argparse.REMAINDER)
344 args = parser.parse_args(args[1:])
345
346 # Depending on whether we are using the default protoc C++ generator or the
347 # protozero plugin, the output dir is passed as:
348 # --cpp_out=gen/xxx or
349 # --plugin_out=:gen/xxx or
350 # --plugin_out=wrapper_namespace=pbzero:gen/xxx
351 gen_dir = args.cpp_out if args.cpp_out else args.plugin_out.split(':')[1]
352 assert gen_dir.startswith('gen/')
353 gen_dir = gen_dir[4:]
354 cpp_out_dir = ('$(genDir)/%s/%s' % (tree_path, gen_dir)).rstrip('/')
355
356 # TODO(skyostil): Is there a way to avoid hardcoding the tree path here?
357 # TODO(skyostil): Find a way to avoid creating the directory.
358 cmd = [
359 'mkdir -p %s &&' % cpp_out_dir,
360 '$(location aprotoc)',
361 '--cpp_out=%s' % cpp_out_dir
362 ]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000363
364 # We create two genrules for each action: one for the protobuf headers and
365 # another for the sources. This is because the module that depends on the
366 # generated files needs to declare two different types of dependencies --
367 # source files in 'srcs' and headers in 'generated_headers' -- and it's not
368 # valid to generate .h files from a source dependency and vice versa.
Sami Kyostila71625d72017-12-18 10:29:49 +0000369 source_module = Module('genrule', label_to_module_name(target_name))
Sami Kyostila865d1d32017-12-12 18:37:04 +0000370 source_module.srcs.extend(label_to_path(src) for src in target['sources'])
371 source_module.tools = ['aprotoc']
372
Sami Kyostila71625d72017-12-18 10:29:49 +0000373 header_module = Module('genrule',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000374 label_to_module_name(target_name) + '_headers')
375 header_module.srcs = source_module.srcs[:]
376 header_module.tools = source_module.tools[:]
Primiano Tucci20b760c2018-01-19 12:36:12 +0000377 header_module.export_include_dirs = [gen_dir or '.']
Sami Kyostila865d1d32017-12-12 18:37:04 +0000378
Primiano Tucci20b760c2018-01-19 12:36:12 +0000379 # In GN builds the proto path is always relative to the output directory
380 # (out/tmp.xxx).
381 assert args.proto_path.startswith('../../')
382 cmd += [ '--proto_path=%s/%s' % (tree_path, args.proto_path[6:])]
383
Sami Kyostila865d1d32017-12-12 18:37:04 +0000384 namespaces = ['pb']
Sami Kyostila865d1d32017-12-12 18:37:04 +0000385 if args.plugin:
386 _, plugin = os.path.split(args.plugin)
387 # TODO(skyostil): Can we detect this some other way?
388 if plugin == 'ipc_plugin':
389 namespaces.append('ipc')
390 elif plugin == 'protoc_plugin':
391 namespaces = ['pbzero']
392 for dep in target['deps']:
393 if desc[dep]['type'] != 'executable':
394 continue
395 _, executable = os.path.split(desc[dep]['outputs'][0])
396 if executable == plugin:
397 cmd += [
398 '--plugin=protoc-gen-plugin=$(location %s)' %
399 label_to_module_name(dep)
400 ]
401 source_module.tools.append(label_to_module_name(dep))
402 # Also make sure the module for the tool is generated.
403 create_modules_from_target(blueprint, desc, dep)
404 break
405 else:
406 raise Error('Unrecognized protoc plugin in target %s: %s' %
407 (target_name, args[i]))
408 if args.plugin_out:
409 plugin_args = args.plugin_out.split(':')[0]
Primiano Tucci20b760c2018-01-19 12:36:12 +0000410 cmd += ['--plugin_out=%s:%s' % (plugin_args, cpp_out_dir)]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000411
412 cmd += ['$(in)']
413 source_module.cmd = ' '.join(cmd)
414 header_module.cmd = source_module.cmd
415 header_module.tools = source_module.tools[:]
416
417 for ns in namespaces:
418 source_module.out += [
419 '%s/%s' % (tree_path, src.replace('.proto', '.%s.cc' % ns))
420 for src in source_module.srcs
421 ]
422 header_module.out += [
423 '%s/%s' % (tree_path, src.replace('.proto', '.%s.h' % ns))
424 for src in header_module.srcs
425 ]
426 return source_module, header_module
427
428
Florian Mayer3d5e7e62018-01-19 15:22:46 +0000429def _get_cflags(target):
430 cflags = set(flag for flag in target.get('cflags', [])
431 if re.match(cflag_whitelist, flag))
432 cflags |= set("-D%s" % define for define in target.get('defines', [])
433 if re.match(define_whitelist, define))
434 return cflags
435
436
Sami Kyostila865d1d32017-12-12 18:37:04 +0000437def create_modules_from_target(blueprint, desc, target_name):
438 """Generate module(s) for a given GN target.
439
440 Given a GN target name, generate one or more corresponding modules into a
441 blueprint.
442
443 Args:
444 blueprint: Blueprint instance which is being generated.
445 desc: JSON GN description.
446 target_name: GN target for module generation.
447 """
448 target = desc[target_name]
449 if target['type'] == 'executable':
450 if 'host' in target['toolchain']:
451 module_type = 'cc_binary_host'
452 elif target.get('testonly'):
453 module_type = 'cc_test'
454 else:
455 module_type = 'cc_binary'
456 modules = [Module(module_type, label_to_module_name(target_name))]
457 elif target['type'] == 'action':
458 modules = make_genrules_for_action(blueprint, desc, target_name)
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000459 elif target['type'] == 'static_library':
Lalit Magantic5bcd792018-01-12 18:38:11 +0000460 module = Module('cc_library_static', label_to_module_name(target_name))
461 module.export_include_dirs = ['include']
462 modules = [module]
Primiano Tucci6067e732018-01-08 16:19:40 +0000463 elif target['type'] == 'shared_library':
464 modules = [
465 Module('cc_library_shared', label_to_module_name(target_name))
466 ]
Sami Kyostila865d1d32017-12-12 18:37:04 +0000467 else:
468 raise Error('Unknown target type: %s' % target['type'])
469
470 for module in modules:
471 module.comment = 'GN target: %s' % target_name
Primiano Tucci6067e732018-01-08 16:19:40 +0000472 if target_name in target_initrc:
473 module.init_rc = [target_initrc[target_name]]
474
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000475 # Don't try to inject library/source dependencies into genrules because
476 # they are not compiled in the traditional sense.
Sami Kyostila71625d72017-12-18 10:29:49 +0000477 if module.type != 'genrule':
Sami Kyostila865d1d32017-12-12 18:37:04 +0000478 module.defaults = [defaults_module]
Sami Kyostilaebba0fe2017-12-19 14:01:52 +0000479 apply_module_dependency(blueprint, desc, module, target_name)
480 for dep in resolve_dependencies(desc, target_name):
481 apply_module_dependency(blueprint, desc, module, dep)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000482
Lalit Magantic5bcd792018-01-12 18:38:11 +0000483 # If the module is a static library, export all the generated headers.
484 if module.type == 'cc_library_static':
485 module.export_generated_headers = module.generated_headers
486
Sami Kyostila865d1d32017-12-12 18:37:04 +0000487 blueprint.add_module(module)
488
489
490def resolve_dependencies(desc, target_name):
491 """Return the transitive set of dependent-on targets for a GN target.
492
493 Args:
494 blueprint: Blueprint instance which is being generated.
495 desc: JSON GN description.
496
497 Returns:
498 A set of transitive dependencies in the form of GN targets.
499 """
500
501 if label_without_toolchain(target_name) in builtin_deps:
502 return set()
503 target = desc[target_name]
504 resolved_deps = set()
505 for dep in target.get('deps', []):
506 resolved_deps.add(dep)
507 # Ignore the transitive dependencies of actions because they are
508 # explicitly converted to genrules.
509 if desc[dep]['type'] == 'action':
510 continue
Primiano Tucci6067e732018-01-08 16:19:40 +0000511 # Dependencies on shared libraries shouldn't propagate any transitive
512 # dependencies but only depend on the shared library target
513 if desc[dep]['type'] == 'shared_library':
514 continue
Sami Kyostila865d1d32017-12-12 18:37:04 +0000515 resolved_deps.update(resolve_dependencies(desc, dep))
516 return resolved_deps
517
518
519def create_blueprint_for_targets(desc, targets):
520 """Generate a blueprint for a list of GN targets."""
521 blueprint = Blueprint()
522
523 # Default settings used by all modules.
524 defaults = Module('cc_defaults', defaults_module)
525 defaults.local_include_dirs = ['include']
526 defaults.cflags = [
527 '-Wno-error=return-type',
528 '-Wno-sign-compare',
529 '-Wno-sign-promo',
530 '-Wno-unused-parameter',
Florian Mayercc424fd2018-01-15 11:19:01 +0000531 '-fvisibility=hidden',
Florian Mayerc2a38ea2018-01-19 11:48:43 +0000532 '-Oz',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000533 ]
534
535 blueprint.add_module(defaults)
536 for target in targets:
537 create_modules_from_target(blueprint, desc, target)
538 return blueprint
539
540
Sami Kyostilab27619f2017-12-13 19:22:16 +0000541def repo_root():
542 """Returns an absolute path to the repository root."""
543
544 return os.path.join(
545 os.path.realpath(os.path.dirname(__file__)), os.path.pardir)
546
547
548def create_build_description():
549 """Creates the JSON build description by running GN."""
550
551 out = os.path.join(repo_root(), 'out', 'tmp.gen_android_bp')
552 try:
553 try:
554 os.makedirs(out)
555 except OSError as e:
556 if e.errno != errno.EEXIST:
557 raise
558 subprocess.check_output(
559 ['gn', 'gen', out, '--args=%s' % gn_args], cwd=repo_root())
560 desc = subprocess.check_output(
561 ['gn', 'desc', out, '--format=json', '--all-toolchains', '//*'],
562 cwd=repo_root())
563 return json.loads(desc)
564 finally:
565 shutil.rmtree(out)
566
567
Sami Kyostila865d1d32017-12-12 18:37:04 +0000568def main():
569 parser = argparse.ArgumentParser(
570 description='Generate Android.bp from a GN description.')
571 parser.add_argument(
Sami Kyostilab27619f2017-12-13 19:22:16 +0000572 '--desc',
Sami Kyostila865d1d32017-12-12 18:37:04 +0000573 help=
574 'GN description (e.g., gn desc out --format=json --all-toolchains "//*"'
575 )
576 parser.add_argument(
Lalit Magantic5bcd792018-01-12 18:38:11 +0000577 '--extras',
578 help='Extra targets to include at the end of the Blueprint file',
579 default=os.path.join(repo_root(), 'Android.bp.extras'),
580 )
581 parser.add_argument(
Sami Kyostilab27619f2017-12-13 19:22:16 +0000582 '--output',
583 help='Blueprint file to create',
584 default=os.path.join(repo_root(), 'Android.bp'),
585 )
586 parser.add_argument(
Sami Kyostila865d1d32017-12-12 18:37:04 +0000587 'targets',
588 nargs=argparse.REMAINDER,
589 help='Targets to include in the blueprint (e.g., "//:perfetto_tests")')
590 args = parser.parse_args()
591
Sami Kyostilab27619f2017-12-13 19:22:16 +0000592 if args.desc:
593 with open(args.desc) as f:
594 desc = json.load(f)
595 else:
596 desc = create_build_description()
Sami Kyostila865d1d32017-12-12 18:37:04 +0000597
Sami Kyostilab27619f2017-12-13 19:22:16 +0000598 blueprint = create_blueprint_for_targets(desc, args.targets
599 or default_targets)
Sami Kyostila865d1d32017-12-12 18:37:04 +0000600 output = [
601 """// Copyright (C) 2017 The Android Open Source Project
602//
603// Licensed under the Apache License, Version 2.0 (the "License");
604// you may not use this file except in compliance with the License.
605// You may obtain a copy of the License at
606//
607// http://www.apache.org/licenses/LICENSE-2.0
608//
609// Unless required by applicable law or agreed to in writing, software
610// distributed under the License is distributed on an "AS IS" BASIS,
611// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
612// See the License for the specific language governing permissions and
613// limitations under the License.
614//
615// This file is automatically generated by %s. Do not edit.
616""" % (__file__)
617 ]
618 blueprint.to_string(output)
Lalit Magantic5bcd792018-01-12 18:38:11 +0000619 with open(args.extras, 'r') as r:
620 for line in r:
621 output.append(line.rstrip("\n\r"))
Sami Kyostilab27619f2017-12-13 19:22:16 +0000622 with open(args.output, 'w') as f:
623 f.write('\n'.join(output))
Sami Kyostila865d1d32017-12-12 18:37:04 +0000624
625
626if __name__ == '__main__':
627 sys.exit(main())