1#!/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 errno 30import json 31import os 32import re 33import shutil 34import subprocess 35import sys 36 37# Default targets to translate to the blueprint file. 38default_targets = [ 39 '//:libperfetto', 40 '//:libperfetto_android_internal', 41 '//:perfetto_integrationtests', 42 '//:perfetto_trace_protos', 43 '//:perfetto_unittests', 44 '//:perfetto', 45 '//:traced', 46 '//:traced_probes', 47 '//:trace_to_text', 48 '//:heapprofd_client', 49 '//:heapprofd', 50 '//:trigger_perfetto', 51] 52 53# Defines a custom init_rc argument to be applied to the corresponding output 54# blueprint target. 55target_initrc = { 56 '//:traced': 'perfetto.rc', 57 '//:heapprofd': 'heapprofd.rc', 58} 59 60target_host_supported = [ 61 '//:perfetto_trace_protos', 62] 63 64target_host_only = [ 65 '//:trace_to_text', 66] 67 68# Arguments for the GN output directory. 69gn_args = 'target_os="android" target_cpu="arm" is_debug=false perfetto_build_with_android=true' 70 71# All module names are prefixed with this string to avoid collisions. 72module_prefix = 'perfetto_' 73 74# Shared libraries which are directly translated to Android system equivalents. 75library_whitelist = [ 76 "android.hardware.atrace@1.0", 77 'android.hardware.health@2.0', 78 "android.hardware.power.stats@1.0", 79 'android', 80 'base', 81 'binder', 82 'hidlbase', 83 'hidltransport', 84 'hwbinder', 85 'incident', 86 'log', 87 'services', 88 'utils', 89] 90 91# Name of the module which settings such as compiler flags for all other 92# modules. 93defaults_module = module_prefix + 'defaults' 94 95# Location of the project in the Android source tree. 96tree_path = 'external/perfetto' 97 98# Compiler flags which are passed through to the blueprint. 99cflag_whitelist = r'^-DPERFETTO.*$' 100 101# Compiler defines which are passed through to the blueprint. 102define_whitelist = r'^(GOOGLE_PROTO.*)|(PERFETTO_BUILD_WITH_ANDROID)|(ZLIB_.*)|(USE_MMAP)|(HAVE_HIDDEN)$' 103 104# Shared libraries which are not in PDK. 105library_not_in_pdk = { 106 'libandroid', 107 'libservices', 108} 109 110# Additional arguments to apply to Android.bp rules. 111additional_args = { 112 'heapprofd_client': [ 113 ('include_dirs', ['bionic/libc']), 114 ('static_libs', ['libasync_safe']), 115 ], 116 'traced_probes': [ 117 ('required', ['libperfetto_android_internal', 'trigger_perfetto']), 118 ], 119 'libperfetto_android_internal': [ 120 ('static_libs', ['libhealthhalutils']), 121 ], 122} 123 124 125def enable_gmock(module): 126 module.static_libs.append('libgmock') 127 128 129def enable_gtest_prod(module): 130 module.static_libs.append('libgtest_prod') 131 132 133def enable_gtest(module): 134 assert module.type == 'cc_test' 135 136 137def enable_protobuf_full(module): 138 module.shared_libs.append('libprotobuf-cpp-full') 139 140 141def enable_protobuf_lite(module): 142 module.shared_libs.append('libprotobuf-cpp-lite') 143 144 145def enable_protoc_lib(module): 146 module.shared_libs.append('libprotoc') 147 148def enable_libunwindstack(module): 149 module.shared_libs.append('libunwindstack') 150 module.shared_libs.append('libprocinfo') 151 module.shared_libs.append('libbase') 152 153def enable_libunwind(module): 154 # libunwind is disabled on Darwin so we cannot depend on it. 155 pass 156 157def enable_sqlite(module): 158 module.static_libs.append('libsqlite') 159 160def enable_zlib(module): 161 module.shared_libs.append('libz') 162 163# Android equivalents for third-party libraries that the upstream project 164# depends on. 165builtin_deps = { 166 '//buildtools:gmock': enable_gmock, 167 '//buildtools:gtest': enable_gtest, 168 '//gn:gtest_prod_config': enable_gtest_prod, 169 '//buildtools:gtest_main': enable_gtest, 170 '//buildtools:libunwind': enable_libunwind, 171 '//buildtools:protobuf_full': enable_protobuf_full, 172 '//buildtools:protobuf_lite': enable_protobuf_lite, 173 '//buildtools:protoc_lib': enable_protoc_lib, 174 '//buildtools:libunwindstack': enable_libunwindstack, 175 '//buildtools:sqlite': enable_sqlite, 176 '//buildtools:zlib': enable_zlib, 177} 178 179# ---------------------------------------------------------------------------- 180# End of configuration. 181# ---------------------------------------------------------------------------- 182 183 184class Error(Exception): 185 pass 186 187 188class ThrowingArgumentParser(argparse.ArgumentParser): 189 def __init__(self, context): 190 super(ThrowingArgumentParser, self).__init__() 191 self.context = context 192 193 def error(self, message): 194 raise Error('%s: %s' % (self.context, message)) 195 196 197class Module(object): 198 """A single module (e.g., cc_binary, cc_test) in a blueprint.""" 199 200 def __init__(self, mod_type, name): 201 self.type = mod_type 202 self.name = name 203 self.srcs = [] 204 self.comment = None 205 self.shared_libs = [] 206 self.static_libs = [] 207 self.tools = [] 208 self.cmd = None 209 self.host_supported = False 210 self.init_rc = [] 211 self.out = [] 212 self.export_include_dirs = [] 213 self.generated_headers = [] 214 self.export_generated_headers = [] 215 self.defaults = [] 216 self.cflags = set() 217 self.local_include_dirs = [] 218 self.include_dirs = [] 219 self.required = [] 220 self.user_debug_flag = False 221 self.tool_files = None 222 223 def to_string(self, output): 224 if self.comment: 225 output.append('// %s' % self.comment) 226 output.append('%s {' % self.type) 227 self._output_field(output, 'name') 228 self._output_field(output, 'srcs') 229 self._output_field(output, 'shared_libs') 230 self._output_field(output, 'static_libs') 231 self._output_field(output, 'tools') 232 self._output_field(output, 'cmd', sort=False) 233 self._output_field(output, 'host_supported') 234 self._output_field(output, 'init_rc') 235 self._output_field(output, 'out') 236 self._output_field(output, 'export_include_dirs') 237 self._output_field(output, 'generated_headers') 238 self._output_field(output, 'export_generated_headers') 239 self._output_field(output, 'defaults') 240 self._output_field(output, 'cflags') 241 self._output_field(output, 'local_include_dirs') 242 self._output_field(output, 'include_dirs') 243 self._output_field(output, 'required') 244 self._output_field(output, 'tool_files') 245 246 disable_pdk = any(name in library_not_in_pdk for name in self.shared_libs) 247 if self.user_debug_flag or disable_pdk: 248 output.append(' product_variables: {') 249 if disable_pdk: 250 output.append(' pdk: {') 251 output.append(' enabled: false,') 252 output.append(' },') 253 if self.user_debug_flag: 254 output.append(' debuggable: {') 255 output.append(' cflags: ["-DPERFETTO_BUILD_WITH_ANDROID_USERDEBUG"],') 256 output.append(' },') 257 output.append(' },') 258 output.append('}') 259 output.append('') 260 261 def _output_field(self, output, name, sort=True): 262 value = getattr(self, name) 263 return self._write_value(output, name, value, sort) 264 265 def _write_value(self, output, name, value, sort=True): 266 if not value: 267 return 268 if isinstance(value, set): 269 value = sorted(value) 270 if isinstance(value, list): 271 output.append(' %s: [' % name) 272 for item in sorted(value) if sort else value: 273 output.append(' "%s",' % item) 274 output.append(' ],') 275 return 276 if isinstance(value, bool): 277 output.append(' %s: true,' % name) 278 return 279 output.append(' %s: "%s",' % (name, value)) 280 281 282class Blueprint(object): 283 """In-memory representation of an Android.bp file.""" 284 285 def __init__(self): 286 self.modules = {} 287 288 def add_module(self, module): 289 """Adds a new module to the blueprint, replacing any existing module 290 with the same name. 291 292 Args: 293 module: Module instance. 294 """ 295 self.modules[module.name] = module 296 297 def to_string(self, output): 298 for m in sorted(self.modules.itervalues(), key=lambda m: m.name): 299 m.to_string(output) 300 301 302def label_to_path(label): 303 """Turn a GN output label (e.g., //some_dir/file.cc) into a path.""" 304 assert label.startswith('//') 305 return label[2:] 306 307 308def label_to_module_name(label): 309 """Turn a GN label (e.g., //:perfetto_tests) into a module name.""" 310 module = re.sub(r'^//:?', '', label) 311 module = re.sub(r'[^a-zA-Z0-9_]', '_', module) 312 if not module.startswith(module_prefix) and label not in default_targets: 313 return module_prefix + module 314 return module 315 316 317def label_without_toolchain(label): 318 """Strips the toolchain from a GN label. 319 320 Return a GN label (e.g //buildtools:protobuf(//gn/standalone/toolchain: 321 gcc_like_host) without the parenthesised toolchain part. 322 """ 323 return label.split('(')[0] 324 325 326def is_supported_source_file(name): 327 """Returns True if |name| can appear in a 'srcs' list.""" 328 return os.path.splitext(name)[1] in ['.c', '.cc', '.proto'] 329 330 331def is_generated_by_action(desc, label): 332 """Checks if a label is generated by an action. 333 334 Returns True if a GN output label |label| is an output for any action, 335 i.e., the file is generated dynamically. 336 """ 337 for target in desc.itervalues(): 338 if target['type'] == 'action' and label in target['outputs']: 339 return True 340 return False 341 342 343def apply_module_dependency(blueprint, desc, module, dep_name): 344 """Recursively collect dependencies for a given module. 345 346 Walk the transitive dependencies for a GN target and apply them to a given 347 module. This effectively flattens the dependency tree so that |module| 348 directly contains all the sources, libraries, etc. in the corresponding GN 349 dependency tree. 350 351 Args: 352 blueprint: Blueprint instance which is being generated. 353 desc: JSON GN description. 354 module: Module to which dependencies should be added. 355 dep_name: GN target of the dependency. 356 """ 357 # If the dependency refers to a library which we can replace with an Android 358 # equivalent, stop recursing and patch the dependency in. 359 if label_without_toolchain(dep_name) in builtin_deps: 360 builtin_deps[label_without_toolchain(dep_name)](module) 361 return 362 363 # Similarly some shared libraries are directly mapped to Android 364 # equivalents. 365 target = desc[dep_name] 366 for lib in target.get('libs', []): 367 # Generally library names sould be mangled as 'libXXX', unless they are 368 # HAL libraries (e.g., android.hardware.health@2.0). 369 android_lib = lib if '@' in lib else 'lib' + lib 370 if lib in library_whitelist and not android_lib in module.shared_libs: 371 module.shared_libs.append(android_lib) 372 373 type = target['type'] 374 if type == 'action': 375 if "gen_merged_sql_metrics" in dep_name: 376 dep_mod = create_merged_sql_metrics_target(blueprint, desc, dep_name) 377 module.generated_headers.append(dep_mod.name) 378 else: 379 create_modules_from_target(blueprint, desc, dep_name) 380 # Depend both on the generated sources and headers -- see 381 # make_genrules_for_action. 382 module.srcs.append(':' + label_to_module_name(dep_name)) 383 module.generated_headers.append( 384 label_to_module_name(dep_name) + '_headers') 385 elif type == 'static_library' and label_to_module_name( 386 dep_name) != module.name: 387 create_modules_from_target(blueprint, desc, dep_name) 388 module.static_libs.append(label_to_module_name(dep_name)) 389 elif type == 'shared_library' and label_to_module_name( 390 dep_name) != module.name: 391 module.shared_libs.append(label_to_module_name(dep_name)) 392 elif type in ['group', 'source_set', 'executable', 'static_library' 393 ] and 'sources' in target: 394 # Ignore source files that are generated by actions since they will be 395 # implicitly added by the genrule dependencies. 396 module.srcs.extend( 397 label_to_path(src) for src in target['sources'] 398 if is_supported_source_file(src) 399 and not is_generated_by_action(desc, src)) 400 module.cflags |= _get_cflags(target) 401 402 403def make_genrules_for_action(blueprint, desc, target_name): 404 """Generate genrules for a GN action. 405 406 GN actions are used to dynamically generate files during the build. The 407 Soong equivalent is a genrule. This function turns a specific kind of 408 genrule which turns .proto files into source and header files into a pair 409 equivalent genrules. 410 411 Args: 412 blueprint: Blueprint instance which is being generated. 413 desc: JSON GN description. 414 target_name: GN target for genrule generation. 415 416 Returns: 417 A (source_genrule, header_genrule) module tuple. 418 """ 419 target = desc[target_name] 420 421 # We only support genrules which call protoc (with or without a plugin) to 422 # turn .proto files into header and source files. 423 args = target['args'] 424 if not args[0].endswith('/protoc'): 425 raise Error('Unsupported action in target %s: %s' % (target_name, 426 target['args'])) 427 parser = ThrowingArgumentParser('Action in target %s (%s)' % 428 (target_name, ' '.join(target['args']))) 429 parser.add_argument('--proto_path') 430 parser.add_argument('--cpp_out') 431 parser.add_argument('--plugin') 432 parser.add_argument('--plugin_out') 433 parser.add_argument('--descriptor_set_out') 434 parser.add_argument('--include_imports', action='store_true') 435 parser.add_argument('protos', nargs=argparse.REMAINDER) 436 args = parser.parse_args(args[1:]) 437 438 # Depending on whether we are using the default protoc C++ generator or the 439 # protozero plugin, the output dir is passed as: 440 # --cpp_out=gen/xxx or 441 # --plugin_out=:gen/xxx or 442 # --plugin_out=wrapper_namespace=pbzero:gen/xxx 443 gen_dir = args.cpp_out if args.cpp_out else args.plugin_out.split(':')[1] 444 assert gen_dir.startswith('gen/') 445 gen_dir = gen_dir[4:] 446 cpp_out_dir = ('$(genDir)/%s/%s' % (tree_path, gen_dir)).rstrip('/') 447 448 # TODO(skyostil): Is there a way to avoid hardcoding the tree path here? 449 # TODO(skyostil): Find a way to avoid creating the directory. 450 cmd = [ 451 'mkdir -p %s &&' % cpp_out_dir, 452 '$(location aprotoc)', 453 '--cpp_out=%s' % cpp_out_dir 454 ] 455 456 # We create two genrules for each action: one for the protobuf headers and 457 # another for the sources. This is because the module that depends on the 458 # generated files needs to declare two different types of dependencies -- 459 # source files in 'srcs' and headers in 'generated_headers' -- and it's not 460 # valid to generate .h files from a source dependency and vice versa. 461 source_module = Module('genrule', label_to_module_name(target_name)) 462 source_module.srcs.extend(label_to_path(src) for src in target['sources']) 463 source_module.tools = ['aprotoc'] 464 465 header_module = Module('genrule', 466 label_to_module_name(target_name) + '_headers') 467 header_module.srcs = source_module.srcs[:] 468 header_module.tools = source_module.tools[:] 469 header_module.export_include_dirs = [gen_dir or '.'] 470 471 # In GN builds the proto path is always relative to the output directory 472 # (out/tmp.xxx). 473 assert args.proto_path.startswith('../../') 474 cmd += [ '--proto_path=%s/%s' % (tree_path, args.proto_path[6:])] 475 476 namespaces = ['pb'] 477 if args.plugin: 478 _, plugin = os.path.split(args.plugin) 479 # TODO(skyostil): Can we detect this some other way? 480 if plugin == 'ipc_plugin': 481 namespaces.append('ipc') 482 elif plugin == 'protoc_plugin': 483 namespaces = ['pbzero'] 484 for dep in target['deps']: 485 if desc[dep]['type'] != 'executable': 486 continue 487 _, executable = os.path.split(desc[dep]['outputs'][0]) 488 if executable == plugin: 489 cmd += [ 490 '--plugin=protoc-gen-plugin=$(location %s)' % 491 label_to_module_name(dep) 492 ] 493 source_module.tools.append(label_to_module_name(dep)) 494 # Also make sure the module for the tool is generated. 495 create_modules_from_target(blueprint, desc, dep) 496 break 497 else: 498 raise Error('Unrecognized protoc plugin in target %s: %s' % 499 (target_name, args[i])) 500 if args.plugin_out: 501 plugin_args = args.plugin_out.split(':')[0] 502 cmd += ['--plugin_out=%s:%s' % (plugin_args, cpp_out_dir)] 503 504 cmd += ['$(in)'] 505 source_module.cmd = ' '.join(cmd) 506 header_module.cmd = source_module.cmd 507 header_module.tools = source_module.tools[:] 508 509 for ns in namespaces: 510 source_module.out += [ 511 '%s/%s' % (tree_path, src.replace('.proto', '.%s.cc' % ns)) 512 for src in source_module.srcs 513 ] 514 header_module.out += [ 515 '%s/%s' % (tree_path, src.replace('.proto', '.%s.h' % ns)) 516 for src in header_module.srcs 517 ] 518 return source_module, header_module 519 520def create_merged_sql_metrics_target(blueprint, desc, gn_target_name): 521 target_desc = desc[gn_target_name] 522 module = Module( 523 'genrule', 524 'gen_merged_sql_metrics', 525 ) 526 module.tool_files = [ 527 'tools/gen_merged_sql_metrics.py', 528 ] 529 module.cmd = ' '.join([ 530 '$(location tools/gen_merged_sql_metrics.py)', 531 '--cpp_out=$(out)', 532 '$(in)', 533 ]) 534 module.out = set( 535 src[src.index('gen/') + len('gen/'):] 536 for src in target_desc.get('outputs', []) 537 ) 538 module.srcs.extend( 539 label_to_path(src) 540 for src in target_desc.get('inputs', []) 541 ) 542 blueprint.add_module(module) 543 return module 544 545def _get_cflags(target): 546 cflags = set(flag for flag in target.get('cflags', []) 547 if re.match(cflag_whitelist, flag)) 548 cflags |= set("-D%s" % define for define in target.get('defines', []) 549 if re.match(define_whitelist, define)) 550 return cflags 551 552 553def create_modules_from_target(blueprint, desc, target_name): 554 """Generate module(s) for a given GN target. 555 556 Given a GN target name, generate one or more corresponding modules into a 557 blueprint. 558 559 Args: 560 blueprint: Blueprint instance which is being generated. 561 desc: JSON GN description. 562 target_name: GN target for module generation. 563 """ 564 target = desc[target_name] 565 if target['type'] == 'executable': 566 if 'host' in target['toolchain'] or target_name in target_host_only: 567 module_type = 'cc_binary_host' 568 elif target.get('testonly'): 569 module_type = 'cc_test' 570 else: 571 module_type = 'cc_binary' 572 modules = [Module(module_type, label_to_module_name(target_name))] 573 elif target['type'] == 'action': 574 modules = make_genrules_for_action(blueprint, desc, target_name) 575 elif target['type'] == 'static_library': 576 module = Module('cc_library_static', label_to_module_name(target_name)) 577 module.export_include_dirs = ['include'] 578 modules = [module] 579 elif target['type'] == 'shared_library': 580 modules = [ 581 Module('cc_library_shared', label_to_module_name(target_name)) 582 ] 583 else: 584 raise Error('Unknown target type: %s' % target['type']) 585 586 for module in modules: 587 module.comment = 'GN target: %s' % target_name 588 if target_name in target_initrc: 589 module.init_rc = [target_initrc[target_name]] 590 if target_name in target_host_supported: 591 module.host_supported = True 592 593 # Don't try to inject library/source dependencies into genrules because 594 # they are not compiled in the traditional sense. 595 if module.type != 'genrule': 596 module.defaults = [defaults_module] 597 apply_module_dependency(blueprint, desc, module, target_name) 598 for dep in resolve_dependencies(desc, target_name): 599 apply_module_dependency(blueprint, desc, module, dep) 600 601 # If the module is a static library, export all the generated headers. 602 if module.type == 'cc_library_static': 603 module.export_generated_headers = module.generated_headers 604 605 # Merge in additional hardcoded arguments. 606 for key, add_val in additional_args.get(module.name, []): 607 curr = getattr(module, key) 608 if add_val and isinstance(add_val, list) and isinstance(curr, list): 609 curr.extend(add_val) 610 else: 611 raise Error('Unimplemented type of additional_args') 612 613 blueprint.add_module(module) 614 615 616def resolve_dependencies(desc, target_name): 617 """Return the transitive set of dependent-on targets for a GN target. 618 619 Args: 620 blueprint: Blueprint instance which is being generated. 621 desc: JSON GN description. 622 623 Returns: 624 A set of transitive dependencies in the form of GN targets. 625 """ 626 627 if label_without_toolchain(target_name) in builtin_deps: 628 return set() 629 target = desc[target_name] 630 resolved_deps = set() 631 for dep in target.get('deps', []): 632 resolved_deps.add(dep) 633 # Ignore the transitive dependencies of actions because they are 634 # explicitly converted to genrules. 635 if desc[dep]['type'] == 'action': 636 continue 637 # Dependencies on shared libraries shouldn't propagate any transitive 638 # dependencies but only depend on the shared library target 639 if desc[dep]['type'] == 'shared_library': 640 continue 641 resolved_deps.update(resolve_dependencies(desc, dep)) 642 return resolved_deps 643 644 645def create_blueprint_for_targets(desc, targets): 646 """Generate a blueprint for a list of GN targets.""" 647 blueprint = Blueprint() 648 649 # Default settings used by all modules. 650 defaults = Module('cc_defaults', defaults_module) 651 defaults.local_include_dirs = ['include'] 652 defaults.cflags = [ 653 '-Wno-error=return-type', 654 '-Wno-sign-compare', 655 '-Wno-sign-promo', 656 '-Wno-unused-parameter', 657 '-fvisibility=hidden', 658 '-Oz', 659 ] 660 defaults.user_debug_flag = True 661 662 blueprint.add_module(defaults) 663 for target in targets: 664 create_modules_from_target(blueprint, desc, target) 665 return blueprint 666 667 668def repo_root(): 669 """Returns an absolute path to the repository root.""" 670 671 return os.path.join( 672 os.path.realpath(os.path.dirname(__file__)), os.path.pardir) 673 674 675def create_build_description(): 676 """Creates the JSON build description by running GN.""" 677 678 out = os.path.join(repo_root(), 'out', 'tmp.gen_android_bp') 679 try: 680 try: 681 os.makedirs(out) 682 except OSError as e: 683 if e.errno != errno.EEXIST: 684 raise 685 subprocess.check_output( 686 ['gn', 'gen', out, '--args=%s' % gn_args], cwd=repo_root()) 687 desc = subprocess.check_output( 688 ['gn', 'desc', out, '--format=json', '--all-toolchains', '//*'], 689 cwd=repo_root()) 690 return json.loads(desc) 691 finally: 692 shutil.rmtree(out) 693 694 695def main(): 696 parser = argparse.ArgumentParser( 697 description='Generate Android.bp from a GN description.') 698 parser.add_argument( 699 '--desc', 700 help= 701 'GN description (e.g., gn desc out --format=json --all-toolchains "//*"' 702 ) 703 parser.add_argument( 704 '--extras', 705 help='Extra targets to include at the end of the Blueprint file', 706 default=os.path.join(repo_root(), 'Android.bp.extras'), 707 ) 708 parser.add_argument( 709 '--output', 710 help='Blueprint file to create', 711 default=os.path.join(repo_root(), 'Android.bp'), 712 ) 713 parser.add_argument( 714 'targets', 715 nargs=argparse.REMAINDER, 716 help='Targets to include in the blueprint (e.g., "//:perfetto_tests")') 717 args = parser.parse_args() 718 719 if args.desc: 720 with open(args.desc) as f: 721 desc = json.load(f) 722 else: 723 desc = create_build_description() 724 725 blueprint = create_blueprint_for_targets(desc, args.targets or 726 default_targets) 727 output = [ 728 """// Copyright (C) 2017 The Android Open Source Project 729// 730// Licensed under the Apache License, Version 2.0 (the "License"); 731// you may not use this file except in compliance with the License. 732// You may obtain a copy of the License at 733// 734// http://www.apache.org/licenses/LICENSE-2.0 735// 736// Unless required by applicable law or agreed to in writing, software 737// distributed under the License is distributed on an "AS IS" BASIS, 738// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 739// See the License for the specific language governing permissions and 740// limitations under the License. 741// 742// This file is automatically generated by %s. Do not edit. 743""" % (__file__) 744 ] 745 blueprint.to_string(output) 746 with open(args.extras, 'r') as r: 747 for line in r: 748 output.append(line.rstrip("\n\r")) 749 with open(args.output, 'w') as f: 750 f.write('\n'.join(output)) 751 752 753if __name__ == '__main__': 754 sys.exit(main()) 755