| #!/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 |
| import sys |
| |
| from sdk_common import Atom, AtomId, detect_category_violations, detect_collisions, gather_dependencies |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser() |
| parser.add_argument('--id', |
| help='The atom\'s identifier', |
| required=True) |
| parser.add_argument('--domain', |
| help='Name of the domain the element belongs to', |
| required=True) |
| name_group = parser.add_mutually_exclusive_group(required=True) |
| name_group.add_argument('--name', |
| help='Name of the element') |
| name_group.add_argument('--name-file', |
| help='Path to the file containing the name of the element') |
| parser.add_argument('--out', |
| help='Path to the output file', |
| required=True) |
| parser.add_argument('--base', |
| help='Path to the element\'s source directory', |
| required=True) |
| parser.add_argument('--deps', |
| help='List of manifest paths for dependencies', |
| nargs='*') |
| parser.add_argument('--package-deps', |
| help='List of manifest paths for runtime dependencies', |
| nargs='*') |
| parser.add_argument('--files', |
| help='A source=destination mapping', |
| nargs='+') |
| # TODO(DX-340): merge new-files and files. The former is needed to |
| # transition to destination paths relative to the SDK root as opposed to |
| # the atom root. |
| parser.add_argument('--new-files', |
| help='Same as files, but new', |
| nargs="*", |
| default=[]) |
| parser.add_argument('--tags', |
| help='List of tags for the included elements', |
| nargs='*') |
| parser.add_argument('--tags-file', |
| help='A file containing tags', |
| required=False) |
| parser.add_argument('--gn-label', |
| help='GN label of the atom', |
| required=True) |
| parser.add_argument('--category', |
| help='Publication level', |
| required=True) |
| parser.add_argument('--meta', |
| help="Path to the atom's metadata file in the SDK", |
| default='', |
| required=False) |
| args = parser.parse_args() |
| |
| if args.name: |
| name = args.name |
| else: |
| with open(args.name_file, 'r') as name_file: |
| name = name_file.read() |
| |
| # Gather the definitions of other atoms this atom depends on. |
| (deps, atoms) = gather_dependencies(args.deps) |
| (_, package_atoms) = gather_dependencies(args.package_deps) |
| all_atoms = atoms |
| all_atoms.update(package_atoms) |
| |
| # Build the list of files making up this atom. |
| files = [] |
| has_packaged_files = False |
| base = os.path.realpath(args.base) |
| for mapping in args.files: |
| mode, pair = mapping.split(':', 1) |
| is_packaged = (mode == 'packaged') |
| destination, source = pair.split('=', 1) |
| real_source = os.path.realpath(source) |
| if not os.path.exists(real_source): |
| raise Exception('Missing source file: %s' % real_source) |
| if destination: |
| if destination.find('..') != -1: |
| raise Exception('Destination for %s cannot contain "..": %s.' % |
| (source, destination)) |
| else: |
| if not real_source.startswith(base): |
| raise Exception('Destination for %s must be given as it is not' |
| ' under source directory %s' % (source, base)) |
| destination = os.path.relpath(real_source, base) |
| if os.path.isabs(destination): |
| raise Exception('Destination cannot be absolute: %s' % destination) |
| files.append({ |
| 'source': real_source, |
| 'destination': destination, |
| 'packaged': is_packaged |
| }) |
| has_packaged_files = has_packaged_files or is_packaged |
| |
| new_files = [] |
| for mapping in args.new_files: |
| destination, source = mapping.split('=', 1) |
| new_files.append({ |
| 'source': source, |
| 'destination': destination, |
| # TODO(DX-340): remove this attribute as the presence of atom |
| # metadata in SDKs makes it obsolete. |
| 'packaged': False, |
| }) |
| |
| id = { |
| 'domain': args.domain, |
| 'name': name, |
| } |
| |
| all_package_deps = set() |
| if has_packaged_files: |
| all_package_deps.add(AtomId(id)) |
| for atom in all_atoms: |
| all_package_deps.update(atom.package_deps) |
| |
| tags = dict(map(lambda t: t.split(':', 1), args.tags)) |
| if args.tags_file: |
| with open(args.tags_file, 'r') as tags_file: |
| data = json.load(tags_file) |
| assert isinstance(data, dict) |
| tags.update(data) |
| tags['domain'] = args.domain |
| |
| all_atoms.update([Atom({ |
| 'id': id, |
| # TODO(DX-340): rename this to "id" once domain/name are gone. |
| 'identifier': args.id, |
| 'meta': args.meta, |
| 'gn-label': args.gn_label, |
| 'category': args.category, |
| 'tags': tags, |
| 'deps': map(lambda i: i.json, sorted(list(deps))), |
| 'package-deps': map(lambda i: i.json, sorted(list(all_package_deps))), |
| 'files': files, |
| 'new-files':new_files, |
| })]) |
| if detect_collisions(all_atoms): |
| print('Name collisions detected!') |
| return 1 |
| if detect_category_violations(args.category, all_atoms): |
| print('Publication level violations detected!') |
| return 1 |
| |
| manifest = { |
| 'ids': [id], |
| 'atoms': map(lambda a: a.json, sorted(list(all_atoms))), |
| } |
| |
| with open(os.path.abspath(args.out), 'w') as out: |
| json.dump(manifest, out, indent=2, sort_keys=True) |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |