| #!/usr/bin/env python |
| # Copyright 2017 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import argparse |
| import json |
| import os.path |
| import paths |
| import sys |
| |
| |
| USE_GN_LABELS = True |
| |
| |
| class PackageImportsResolver: |
| """Recursively resolves imports in build packages. See |
| https://fuchsia.googlesource.com/fuchsia/+/master/docs/development/build/packages.md |
| for more information about build packages. |
| |
| An observer may be used to perform additional work whenever an |
| import is resolved. This observer needs to implement a method with this |
| signature: |
| |
| def import_resolved(self, config, config_path) |
| |
| where config is the JSON file representing the build package. |
| |
| If there was an error reading some of the input files, `None` will be |
| returned. |
| """ |
| |
| def __init__(self, observer=None): |
| self.observer = observer |
| self._errored = False |
| |
| def resolve(self, imports): |
| return self.resolve_imports(imports) |
| |
| def errored(self): |
| return self._errored |
| |
| def resolve_imports(self, import_queue): |
| |
| def detect_duplicate_keys(pairs): |
| keys = set() |
| result = {} |
| for k, v in pairs: |
| if k in keys: |
| raise Exception("Duplicate key %s" % k) |
| keys.add(k) |
| result[k] = v |
| return result |
| |
| imported = set(import_queue) |
| while import_queue: |
| config_name = import_queue.pop() |
| config_path = os.path.join(paths.FUCHSIA_ROOT, config_name) |
| if USE_GN_LABELS and not os.path.isfile(config_path): |
| # If the bundle starts with "//", then it is already a label. |
| if config_name.startswith("//"): |
| self.observer.import_resolved_from_gn(config_name) |
| continue |
| dir_path = os.path.dirname(config_path) |
| if os.path.isfile(os.path.join(dir_path, "BUILD.gn")): |
| self.observer.import_resolved_from_gn_legacy(config_name) |
| continue |
| try: |
| with open(config_path) as f: |
| try: |
| config = json.load(f, |
| object_pairs_hook=detect_duplicate_keys) |
| self.observer.import_resolved(config, config_path) |
| for i in config.get("imports", []): |
| if i not in imported: |
| import_queue.append(i) |
| imported.add(i) |
| except Exception as e: |
| import traceback |
| traceback.print_exc() |
| sys.stderr.write( |
| "Failed to parse config %s, error %s\n" % |
| (config_path, str(e))) |
| self._errored = True |
| return None |
| except IOError, e: |
| self._errored = True |
| sys.stderr.write("Failed to read package '%s' from '%s'.\n" % |
| (config_name, config_path)) |
| if "/" not in config_name: |
| sys.stderr.write(""" |
| Package names are relative to the root of the source tree but the requested |
| path did not contain a '/'. Did you mean 'build/gn/%s' instead? |
| """ % config_name) |
| return None |
| return imported |
| |
| |
| def legacy_config_name_to_label(config_name): |
| parts = config_name.rsplit('/', 1) |
| return "//%s:%s" % (parts[0], parts[1]) |
| |
| |
| class PackageLabelObserver: |
| def __init__(self): |
| self.json_result = { |
| 'targets': [], |
| 'data_deps': [], |
| 'host_tests': [], |
| 'files_read': [], |
| } |
| |
| def import_resolved_from_gn(self, config_name): |
| self.json_result["targets"].append(config_name) |
| |
| def import_resolved_from_gn_legacy(self, config_name): |
| self.import_resolved_from_gn(legacy_config_name_to_label(config_name)) |
| |
| def import_resolved(self, config, config_path): |
| self.json_result['targets'] += config.get('packages', []) |
| self.json_result['data_deps'] += config.get('labels', []) |
| self.json_result['host_tests'] += config.get('host_tests', []) |
| self.json_result['files_read'].append(config_path) |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser(description=''' |
| Determine labels and Fuchsia packages included in the current build. |
| ''') |
| parser.add_argument('--packages', |
| help='JSON list of packages', |
| required=True) |
| args = parser.parse_args() |
| |
| observer = PackageLabelObserver() |
| imports_resolver = PackageImportsResolver(observer) |
| imported = imports_resolver.resolve_imports(json.loads(args.packages)) |
| |
| if imported == None: |
| return 1 |
| |
| json.dump(observer.json_result, sys.stdout, sort_keys=True) |
| |
| return 0 |
| |
| if __name__ == "__main__": |
| sys.exit(main()) |