| #!/usr/bin/env python3.8 |
| # Copyright 2020 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 io |
| import json |
| import os |
| import re |
| import shutil |
| import sys |
| import tarfile |
| |
| |
| class GatherPackageDeps: |
| """Helper class to take a `package_manifest.json` and copy all files referenced |
| into an archive that will then be available at runtime. |
| |
| Args: |
| package_json (string): Path to the package's `package_manifest.json` file. |
| meta_far (string): Path to the package's `meta.far` file. |
| depfile (string): Path to the depfile to write to. |
| |
| Raises: ValueError if any parameter is empty. |
| """ |
| |
| def __init__(self, package_json, meta_far, output_tar, depfile): |
| if package_json and os.path.exists(package_json): |
| self.package_json = package_json |
| else: |
| raise ValueError('package_json must be to a valid file') |
| |
| if meta_far and os.path.exists(meta_far): |
| self.meta_far = meta_far |
| else: |
| raise ValueError('meta_far must be to a valid file') |
| |
| if output_tar: |
| self.output_tar = output_tar |
| else: |
| raise ValueError('output_tar cannot be empty') |
| |
| if depfile: |
| self.depfile = depfile |
| else: |
| raise ValueError('depfile cannot be empty') |
| |
| def parse_package_json(self): |
| manifest_paths = [] |
| with open(self.package_json) as f: |
| data = json.load(f) |
| for file in data['blobs']: |
| if file['path'].startswith('meta/'): |
| continue |
| manifest_paths.append((file['path'], file['source_path'])) |
| return manifest_paths |
| |
| def create_archive(self, manifest_paths): |
| # Explicitly use the GNU_FORMAT because the current dart library |
| # (v.3.0.0) does not support parsing other tar formats that allow for |
| # filenames longer than 100 characters. |
| with tarfile.open(self.output_tar, 'w', |
| format=tarfile.GNU_FORMAT) as tar: |
| # Follow symlinks |
| tar.dereference = True |
| # Create package.manifest in memory and add it to archive. |
| manifest_lines = [] |
| # Add all source files to archive and add manfiest lines. |
| for (archive_path, source_path) in manifest_paths: |
| tar.add(source_path, arcname=archive_path) |
| manifest_lines.append(f'{archive_path}={archive_path}') |
| |
| # Add meta.far to archive and insert corresponding manfiest line. |
| tar.add(self.meta_far, arcname='meta.far') |
| manifest_lines.append('meta/package=meta.far\n') |
| |
| with io.BytesIO('\n'.join(manifest_lines).encode()) as manifest: |
| tarinfo = tarfile.TarInfo('package.manifest') |
| tarinfo.size = len(manifest.getvalue()) |
| tar.addfile(tarinfo, fileobj=manifest) |
| |
| def run(self): |
| manifest_paths = self.parse_package_json() |
| self.create_archive(manifest_paths) |
| with open(self.depfile, 'w') as f: |
| f.write( |
| "{}: {}\n".format( |
| self.output_tar, ' '.join( |
| os.path.relpath(source_path) |
| for (_, source_path) in manifest_paths))) |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser() |
| parser.add_argument( |
| '--package_json', |
| required=True, |
| help= |
| 'The path to the package_manifest.json generated by a `fuchsia_package`.' |
| ) |
| parser.add_argument( |
| '--meta_far', required=True, help="The path to the package's meta.far.") |
| parser.add_argument( |
| '--output_tar', required=True, help='The path to the output archive.') |
| parser.add_argument( |
| '--depfile', |
| required=True, |
| help='The path to write a depfile, see depfile from GN.', |
| ) |
| args = parser.parse_args() |
| |
| gatherer = GatherPackageDeps( |
| args.package_json, args.meta_far, args.output_tar, args.depfile).run() |
| |
| return 0 |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |