blob: 13de7d18981f4ad1e6b0c30fedf948c9729112cf [file] [log] [blame]
#!/usr/bin/env python3.8
# Copyright 2021 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 subprocess
import sys
from typing import Set
from depfile import DepFile
from assembly import AssemblyInputBundle
def generate_package_creation_manifest(args: argparse.Namespace) -> None:
"""Generate a package creation manifest for an Assembly Input Bundle (AIB)
Each AIB has a contents manifest that was created with it. This file lists
all of the files in the AIB, and their path within the build dir::
AIB/path/to/file_1=outdir/path/to/AIB/path/to/file_1
AIB/path/to/file_2=outdir/path/to/AIB/path/to/file_2
This format locates all the files in the AIB, relative to the
root_build_dir, and then gives their destination path within the AIB package
and archive.
To generate the package the AIB, a creation manifest is required (also in
FINI format). This is the same file, with the addition of the path to the
package metadata file::
meta/package=path/to/metadata/file
This fn generates the package metadata file, and then generates the creation
manifest file by appending the path to the metadata file to the entries in
the AIB contents manifest.
"""
meta_package_content = {'name': args.name, 'version': 0}
json.dump(meta_package_content, args.meta_package)
contents_manifest = args.contents_manifest.read()
args.output.write(contents_manifest)
args.output.write("meta/package={}".format(args.meta_package.name))
def generate_archive(args: argparse.Namespace) -> None:
"""Generate an archive of an Assembly Input Bundle (AIB)
Each AIB has a contents manifest that was created with it. This file lists
all of the files in the AIB, and their path within the build dir::
AIB/path/to/file_1=outdir/path/to/AIB/path/to/file_1
AIB/path/to/file_2=outdir/path/to/AIB/path/to/file_2
This format locates all the files in the AIB, relative to the
root_build_dir, and then gives their destination path within the AIB package
and archive.
To generate the archive of the AIB, a creation manifest is required (also in
FINI format). This is the same file, with the addition of the path to the
package meta.far.
meta.far=path/to/meta.far
This fn generates the creation manifest, appending the package meta.far to
the contents manifest, and then calling the tarmaker tool to build the
archive itself, using the generated creation manifest.
"""
deps: Set[str] = set()
# Read the AIB's contents manifest, all of these files will be added to the
# creation manifest for the archive.
contents_manifest = args.contents_manifest.readlines()
deps.add(args.contents_manifest.name)
with open(args.creation_manifest, 'w') as creation_manifest:
# Add the AIB's package metafar to the creation manifest.
creation_manifest.write("meta.far={}\n".format(args.meta_far))
# Followed by all files from the AIB's contents manifest.
for line in contents_manifest:
# Split out the lines so that a depfile for the action can be made
# from the contents_manifest's source paths.
(dst, src) = line.split('=', 1)
deps.add(src.strip())
creation_manifest.write(line)
# Build the archive itself.
cmd_args = [
args.tarmaker, "-manifest", args.creation_manifest, "-output",
args.output
]
subprocess.run(cmd_args, check=True)
if args.depfile:
DepFile.from_deps(args.output, deps).write_to(args.depfile)
def diff_bundles(args: argparse.Namespace) -> None:
first = AssemblyInputBundle.load(args.first)
second = AssemblyInputBundle.load(args.second)
result = first.difference(second)
if args.output:
result.dump(args.output)
else:
print(result)
def intersect_bundles(args: argparse.Namespace) -> None:
bundles = [AssemblyInputBundle.load(file) for file in args.bundles]
result = bundles[0]
for next_bundle in bundles[1:]:
result = result.intersection(next_bundle)
if args.output:
result.dump(args.output)
else:
print(result)
def main():
parser = argparse.ArgumentParser(
description=
"Tool for creating Assembly Input Bundles in-tree, for use with out-of-tree assembly"
)
sub_parsers = parser.add_subparsers(
title="Commands",
description="Commands for working with Assembly Input Bundles")
diff_bundles_parser = sub_parsers.add_parser(
"diff",
help=
"Calculate the difference between the first and second bundles (A-B).")
diff_bundles_parser.add_argument(
"first", help='The first bundle (A)', type=argparse.FileType('r'))
diff_bundles_parser.add_argument(
"second", help='The second bundle (B)', type=argparse.FileType('r'))
diff_bundles_parser.add_argument(
"--output",
help='A file to write the output to, instead of stdout.',
type=argparse.FileType('w'))
diff_bundles_parser.set_defaults(handler=diff_bundles)
intersect_bundles_parser = sub_parsers.add_parser(
"intersect", help="Calculate the intersection of the provided bundles.")
intersect_bundles_parser.add_argument(
"bundles",
nargs="+",
action="extend",
help='Paths to the bundle configs.',
type=argparse.FileType('r'))
intersect_bundles_parser.add_argument(
"--output",
help='A file to write the output to, instead of stdout.',
type=argparse.FileType('w'))
intersect_bundles_parser.set_defaults(handler=intersect_bundles)
package_creation_manifest_parser = sub_parsers.add_parser(
"generate-package-creation-manifest",
help=
"(build tool) Generate the creation manifest for the package that contains an Assembly Input Bundle."
)
package_creation_manifest_parser.add_argument(
"--contents-manifest", type=argparse.FileType('r'), required=True)
package_creation_manifest_parser.add_argument("--name", required=True)
package_creation_manifest_parser.add_argument(
"--meta-package", type=argparse.FileType('w'), required=True)
package_creation_manifest_parser.add_argument(
"--output", type=argparse.FileType('w'), required=True)
package_creation_manifest_parser.set_defaults(
handler=generate_package_creation_manifest)
archive_creation_parser = sub_parsers.add_parser(
"generate-archive",
help=
"(build tool) Generate the tarmaker creation manifest for the tgz that contains an Assembly Input Bundle."
)
archive_creation_parser.add_argument("--tarmaker", required=True)
archive_creation_parser.add_argument(
"--contents-manifest", type=argparse.FileType('r'), required=True)
archive_creation_parser.add_argument("--meta-far", required=True)
archive_creation_parser.add_argument("--creation-manifest", required=True)
archive_creation_parser.add_argument("--output", required=True)
archive_creation_parser.add_argument(
"--depfile", type=argparse.FileType('w'))
archive_creation_parser.set_defaults(handler=generate_archive)
args = parser.parse_args()
if "handler" in args:
# Dispatch to the handler fn.
args.handler(args)
else:
# argparse doesn't seem to automatically catch that not subparser was
# called, and so if there isn't a handler function (which is set by
# having specified a subcommand), then just display usage instead of
# a cryptic KeyError.
parser.print_help()
if __name__ == "__main__":
sys.exit(main())