| #!/usr/bin/env python3.8 |
| # Copyright 2018 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 |
| import sys |
| |
| |
| class Source(object): |
| |
| def __init__(self, name, path, file): |
| self.name = name |
| self.path = path |
| self.file = file |
| |
| def __str__(self): |
| return '%s[%s]' % (self.name, self.path) |
| |
| def __hash__(self): |
| return hash((self.name, self.path)) |
| |
| def __eq__(self, other): |
| return self.name == other.name and self.path == other.path |
| |
| |
| def get_sources(dep_files, extra_sources=None): |
| # Aggregate source data from dependencies. |
| sources = set() |
| if extra_sources: |
| sources.update(extra_sources) |
| for dep in dep_files: |
| with open(dep, 'r') as dep_file: |
| for name, path in json.load(dep_file).items(): |
| sources.add(Source(name, path, dep)) |
| |
| # Verify duplicates. |
| sources_by_name = {} |
| for src in sources: |
| sources_by_name.setdefault(src.name, []).append(src) |
| for name, srcs in sources_by_name.items(): |
| if len(srcs) <= 1: |
| continue |
| print('Error: source "%s" has multiple paths.' % name) |
| for src in srcs: |
| print(' - %s (%s)' % (src.path, src.file)) |
| raise Exception('Could not aggregate sources') |
| |
| return {s.name: s.path for s in sources} |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser() |
| name_group = parser.add_mutually_exclusive_group(required=True) |
| name_group.add_argument('--name', help='Name of the current library') |
| name_group.add_argument( |
| '--name-file', |
| help='Path to a file containing the name of the current library') |
| parser.add_argument( |
| '--source-dir', |
| help='Path to the library\'s source directory', |
| required=True) |
| sources_group = parser.add_mutually_exclusive_group(required=True) |
| sources_group.add_argument( |
| '--sources', help='List of source files', nargs='*') |
| sources_group.add_argument( |
| '--allow-globbing', |
| action='store_true', |
| help='Allow globbing the entire source directory') |
| parser.add_argument( |
| '--output', help='Path to the file to generate', required=True) |
| parser.add_argument( |
| '--deps', help='Dependencies of the current library', nargs='*') |
| args = parser.parse_args() |
| if args.name: |
| name = args.name |
| elif args.name_file: |
| with open(args.name_file, 'r') as name_file: |
| name = name_file.read() |
| |
| current_sources = [] |
| if args.sources: |
| for source in args.sources: |
| p = os.path.join(args.source_dir, source) |
| # Explicit sources must be files. |
| if not os.path.isfile(p): |
| raise ValueError(f'Source {p} is not a file') |
| current_sources.append( |
| Source(os.path.join(name, source), p, args.output)) |
| if not name.endswith('/...'): |
| go_sources = {f for f in args.sources if f.endswith('.go')} |
| |
| # Go sources are constrained to live top-level under `source_dir`; |
| # others (e.g., template files) are free to live further down. |
| for s in go_sources: |
| if os.path.dirname(s): |
| raise ValueError( |
| f'Source "{s}" for "{name}" comes from a subdirectory.' |
| f' Specify source_dir instead.') |
| |
| # TODO: Use `glob.glob("*.go", root_dir=args.source_dir)` instead of |
| # os.listdir after upgrading to Python 3.10. |
| go_files = { |
| f for f in os.listdir(args.source_dir) if f.endswith('.go') |
| } |
| missing = go_files - go_sources |
| if missing: |
| raise ValueError( |
| f'go_library requires that all Go files in source_dir be listed' |
| f' as sources, but the following files are missing from sources' |
| f' for target {name}: {", ".join(sorted(missing))}') |
| elif args.allow_globbing: |
| current_sources.append(Source(name, args.source_dir, args.output)) |
| result = get_sources(args.deps, extra_sources=current_sources) |
| with open(args.output, 'w') as output_file: |
| json.dump(result, output_file, indent=2, sort_keys=True) |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |