blob: ee1356843e6758b34a243707fc513d177a5f5fc0 [file] [log] [blame]
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Creates a archive manifest used for Fuchsia package generation."""
import argparse
import json
import os
import re
import subprocess
import sys
import tempfile
def MakePackagePath(file_path, roots):
"""Computes a path for |file_path| that is relative to one of the directory
paths in |roots|.
file_path: The file path to relativize.
roots: A list of directory paths which may serve as a relative root
for |file_path|.
Examples:
>>> MakePackagePath('/foo/bar.txt', ['/foo/'])
'bar.txt'
>>> MakePackagePath('/foo/dir/bar.txt', ['/foo/'])
'dir/bar.txt'
>>> MakePackagePath('/foo/out/Debug/bar.exe', ['/foo/', '/foo/out/Debug/'])
'bar.exe'
"""
# Prevents greedily matching against a shallow path when a deeper, better
# matching path exists.
roots.sort(key=len, reverse=True)
for next_root in roots:
if not next_root.endswith(os.sep):
next_root += os.sep
if file_path.startswith(next_root):
relative_path = file_path[len(next_root):]
return relative_path
return file_path
def _GetStrippedPath(bin_path):
"""Finds the stripped version of the binary |bin_path| in the build
output directory."""
return bin_path.replace('lib.unstripped/', 'lib/').replace(
'exe.unstripped/', '')
def _IsBinary(path):
"""Checks if the file at |path| is an ELF executable by inspecting its FourCC
header."""
with open(path, 'rb') as f:
file_tag = f.read(4)
return file_tag == '\x7fELF'
def BuildManifest(args):
with open(args.output_path, 'w') as manifest, \
open(args.depfile_path, 'w') as depfile:
# Process the runtime deps file for file paths, recursively walking
# directories as needed.
# MakePackagePath() may relativize to either the source root or output
# directory.
# runtime_deps may contain duplicate paths, so use a set for
# de-duplication.
expanded_files = set()
for next_path in open(args.runtime_deps_file, 'r'):
next_path = next_path.strip()
if os.path.isdir(next_path):
for root, _, files in os.walk(next_path):
for current_file in files:
if current_file.startswith('.'):
continue
expanded_files.add(
os.path.join(root, current_file))
else:
expanded_files.add(next_path)
# Format and write out the manifest contents.
gen_dir = os.path.normpath(os.path.join(args.out_dir, "gen"))
app_found = False
excluded_files_set = set(args.exclude_file)
for current_file in expanded_files:
if _IsBinary(current_file):
current_file = _GetStrippedPath(current_file)
in_package_path = MakePackagePath(current_file,
[gen_dir, args.root_dir, args.out_dir])
if in_package_path == args.app_filename:
app_found = True
if in_package_path in excluded_files_set:
excluded_files_set.remove(in_package_path)
continue
manifest.write('%s=%s\n' % (in_package_path, current_file))
if len(excluded_files_set) > 0:
raise Exception('Some files were excluded with --exclude-file, but '
'not found in the deps list: %s' %
', '.join(excluded_files_set));
if not app_found:
raise Exception('Could not locate executable inside runtime_deps.')
# Write meta/package manifest file.
with open(os.path.join(os.path.dirname(args.output_path), 'package'), 'w') \
as package_json:
json.dump({'version': '0', 'name': args.app_name}, package_json)
manifest.write('meta/package=%s\n' %
os.path.relpath(package_json.name, args.out_dir))
# Write component manifest file.
cmx_file_path = os.path.join(os.path.dirname(args.output_path),
args.app_name + '.cmx')
with open(cmx_file_path, 'w') as component_manifest_file:
component_manifest = {
'program': { 'binary': args.app_filename },
'sandbox': json.load(open(args.sandbox_policy_path, 'r')),
}
json.dump(component_manifest, component_manifest_file)
manifest.write('meta/%s=%s\n' %
(os.path.basename(component_manifest_file.name),
os.path.relpath(cmx_file_path, args.out_dir)))
depfile.write(
"%s: %s" % (os.path.relpath(args.output_path, args.out_dir),
" ".join([os.path.relpath(f, args.out_dir)
for f in expanded_files])))
return 0
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--root-dir', required=True, help='Build root directory')
parser.add_argument('--out-dir', required=True, help='Build output directory')
parser.add_argument('--app-name', required=True, help='Package name')
parser.add_argument('--app-filename', required=True,
help='Path to the main application binary relative to the output dir.')
parser.add_argument('--sandbox-policy-path', required=True,
help='Path to the sandbox policy file relative to the output dir.')
parser.add_argument('--runtime-deps-file', required=True,
help='File with the list of runtime dependencies.')
parser.add_argument('--depfile-path', required=True,
help='Path to write GN deps file.')
parser.add_argument('--exclude-file', action='append', default=[],
help='Package-relative file path to exclude from the package.')
parser.add_argument('--output-path', required=True, help='Output file path.')
args = parser.parse_args()
return BuildManifest(args)
if __name__ == '__main__':
sys.exit(main())