diff options
author | Mike Frysinger <vapier@google.com> | 2015-10-02 16:38:58 -0400 |
---|---|---|
committer | Mike Frysinger <vapier@google.com> | 2015-10-02 16:38:58 -0400 |
commit | 29f9a9ef83d4897fe6997016bb547138cc32e9ff (patch) | |
tree | 537b62c18de09c51c37560cf305fdb37d549ea7a | |
parent | 9e03753dbe88db6b55a5a0ac634844e42b47dd3b (diff) | |
download | portage-29f9a9ef83d4897fe6997016bb547138cc32e9ff.tar.gz |
3rd-party-merge: helper tool to merge packages to /system
Rather than merge all of the files, parse the CONTENTS ourselves and
copy in a reduced subset of things.
Change-Id: I5e2f258d3f70896b2b80ca2124a90044920d5a99
-rwxr-xr-x | usr/bin/3rd-party-merge | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/usr/bin/3rd-party-merge b/usr/bin/3rd-party-merge new file mode 100755 index 0000000..9cbde58 --- /dev/null +++ b/usr/bin/3rd-party-merge @@ -0,0 +1,176 @@ +#!/usr/bin/python +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Merge packages into the target image.""" + +from __future__ import print_function + +import argparse +import errno +import glob +import os +import shutil +import sys + + +def makedirs(path): + """Create all the dirs for |path| ignoring already existing errors.""" + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + +def unlink(path): + """Delete |path| ignoring missing paths.""" + try: + os.unlink(path) + except OSError as e: + if e.errno != errno.ENOENT: + raise + + +def load_contents(pkg, root, filter=None): + """Read the CONTENTS file for |pkg| from |root|.""" + if not filter: + filter = lambda x: True + + ret = [] + + contents = os.path.join(root, 'var', 'db', 'pkg', pkg + '-*', 'CONTENTS') + files = glob.glob(contents) + if not files: + raise Exception('No packages found matching %s' % pkg) + + with open(files[0]) as f: + for line in f.read().splitlines(): + line = line.strip() + if not line: + continue + typ, data = line.split(' ', 1) + if typ == 'obj': + path, _hash, _mtime = data.rsplit(' ', 2) + if filter(path): + ret.append(('obj', path)) + elif typ == 'sym': + source, target = data.split(' -> ', 1) + target, _mtime = target.rsplit(' ', 1) + if filter(source): + ret.append(('sym', source, target)) + elif typ == 'dir': + pass + else: + raise Exception('Unhandled entry: %s' % line) + + return ret + + +def ignore_files(path): + """Figure out which files we actually care about.""" + if path.startswith('/usr/share/doc'): + return False + elif path.startswith('/usr/share/gtk-doc'): + return False + elif path.startswith('/usr/share/info'): + return False + elif path.startswith('/usr/share/man'): + return False + elif path.startswith('/usr/include'): + return False + elif path.startswith('/usr/bin') and path.endswith('-config'): + return False + elif path.startswith('/usr/lib'): + if path.endswith('.a'): + return False + elif path.endswith('.pc'): + return False + + return True + + +def merge(pkg, input_root, output_root, make_root, verbose=0): + """Merge |pkg| from |input_root| to |output_root|.""" + print('Merging %s ' % pkg, end='') + if verbose: + print('from %s to %s' % (input_root, output_root)) + + # TODO: Grab a lock for |pkg|. + + # First get the file listing to merge. + contents = load_contents(pkg, input_root, filter=ignore_files) + + # Now actually merge them. + for entry in contents: + if entry[0] == 'obj': + path = entry[1].lstrip('/') + output = os.path.join(output_root, path) + if verbose: + print('>>> %s' % path) + else: + print('.', end='') + makedirs(os.path.dirname(output)) + shutil.copy2(os.path.join(input_root, path), output) + elif entry[0] == 'sym': + path = entry[1].lstrip('/') + target = entry[2] + output = os.path.join(output_root, path) + if verbose: + print('>>> %s -> %s' % (path, target)) + else: + print('.', end='') + unlink(output) + makedirs(os.path.dirname(output)) + os.symlink(target, output) + else: + raise Exception('Unhandled entry: %r' % entry) + + make_target = os.path.join(make_root, pkg + '.emerge') + makedirs(os.path.dirname(make_target)) + open(make_target, 'w') + + if not verbose: + print('') + + +def get_parser(): + """Return a command line parser.""" + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('-p', '--package', action='append', required=True, + dest='packages', + help='Merge these package(s)') + parser.add_argument('--input-root', type=str, required=True, + help='Where to read the packages') + parser.add_argument('--output-root', type=str, required=True, + help='Where to write the packages') + parser.add_argument('--make-root', type=str, required=True, + help='Where make writes state files for rules') + parser.add_argument('-v', '--verbose', action='count', + help='Make output more verbose') + return parser + + +def main(argv): + parser = get_parser() + opts = parser.parse_args(argv) + + for pkg in opts.packages: + merge(pkg, opts.input_root, opts.output_root, opts.make_root, + verbose=opts.verbose) + + +if __name__ == '__main__': + main(sys.argv[1:]) |