summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frysinger <vapier@google.com>2015-10-02 16:38:58 -0400
committerMike Frysinger <vapier@google.com>2015-10-02 16:38:58 -0400
commit29f9a9ef83d4897fe6997016bb547138cc32e9ff (patch)
tree537b62c18de09c51c37560cf305fdb37d549ea7a
parent9e03753dbe88db6b55a5a0ac634844e42b47dd3b (diff)
downloadportage-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-xusr/bin/3rd-party-merge176
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:])