Ingemar Ådahl | 6ada47b | 2017-07-10 17:14:01 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | |
| 3 | # Copyright 2017 The Chromium Authors. All rights reserved. |
| 4 | # Use of this source code is governed by a BSD-style license that can be |
| 5 | # found in the LICENSE file. |
| 6 | |
| 7 | """Merges dependency Android manifests into a root manifest.""" |
| 8 | |
| 9 | import argparse |
| 10 | import contextlib |
| 11 | import os |
| 12 | import sys |
| 13 | import tempfile |
| 14 | import xml.dom.minidom as minidom |
| 15 | |
| 16 | from util import build_utils |
| 17 | |
| 18 | # Tools library directory - relative to Android SDK root |
| 19 | SDK_TOOLS_LIB_DIR = os.path.join('tools', 'lib') |
| 20 | |
| 21 | MANIFEST_MERGER_MAIN_CLASS = 'com.android.manifmerger.Merger' |
| 22 | MANIFEST_MERGER_JARS = [ |
| 23 | 'common{suffix}.jar', |
| 24 | 'manifest-merger{suffix}.jar', |
| 25 | 'sdk-common{suffix}.jar', |
| 26 | 'sdklib{suffix}.jar', |
| 27 | ] |
| 28 | |
| 29 | TOOLS_NAMESPACE_PREFIX = 'tools' |
| 30 | TOOLS_NAMESPACE = 'http://schemas.android.com/tools' |
| 31 | |
| 32 | |
| 33 | @contextlib.contextmanager |
| 34 | def _PatchedManifest(manifest_path): |
| 35 | """Patches an Android manifest to always include the 'tools' namespace |
| 36 | declaration, as it is not propagated by the manifest merger from the SDK. |
| 37 | |
| 38 | See https://issuetracker.google.com/issues/63411481 |
| 39 | """ |
| 40 | doc = minidom.parse(manifest_path) |
| 41 | manifests = doc.getElementsByTagName('manifest') |
| 42 | assert len(manifests) == 1 |
| 43 | manifest = manifests[0] |
| 44 | |
| 45 | manifest.setAttribute('xmlns:%s' % TOOLS_NAMESPACE_PREFIX, TOOLS_NAMESPACE) |
| 46 | |
| 47 | tmp_prefix = os.path.basename(manifest_path) |
| 48 | with tempfile.NamedTemporaryFile(prefix=tmp_prefix) as patched_manifest: |
| 49 | doc.writexml(patched_manifest) |
| 50 | patched_manifest.flush() |
| 51 | yield patched_manifest.name |
| 52 | |
| 53 | |
| 54 | def _BuildManifestMergerClasspath(build_vars): |
| 55 | return ':'.join([ |
| 56 | os.path.join( |
| 57 | build_vars['android_sdk_root'], |
| 58 | SDK_TOOLS_LIB_DIR, |
| 59 | jar.format(suffix=build_vars['android_sdk_tools_version_suffix'])) |
| 60 | for jar in MANIFEST_MERGER_JARS |
| 61 | ]) |
| 62 | |
| 63 | |
| 64 | def main(argv): |
| 65 | argv = build_utils.ExpandFileArgs(argv) |
| 66 | parser = argparse.ArgumentParser(description=__doc__) |
Andrew Grieve | 9ce44b9 | 2017-07-25 02:43:05 | [diff] [blame^] | 67 | build_utils.AddDepfileOption(parser) |
Ingemar Ådahl | 6ada47b | 2017-07-10 17:14:01 | [diff] [blame] | 68 | parser.add_argument('--build-vars', |
| 69 | help='Path to GN build vars file', |
| 70 | required=True) |
| 71 | parser.add_argument('--root-manifest', |
| 72 | help='Root manifest which to merge into', |
| 73 | required=True) |
| 74 | parser.add_argument('--output', help='Output manifest path', required=True) |
| 75 | parser.add_argument('--extras', |
| 76 | help='GN list of additional manifest to merge') |
| 77 | args = parser.parse_args(argv) |
| 78 | |
Andrew Grieve | 9ce44b9 | 2017-07-25 02:43:05 | [diff] [blame^] | 79 | classpath = _BuildManifestMergerClasspath( |
| 80 | build_utils.ReadBuildVars(args.build_vars)) |
Ingemar Ådahl | 6ada47b | 2017-07-10 17:14:01 | [diff] [blame] | 81 | cmd = [ |
| 82 | 'java', |
| 83 | '-cp', |
Andrew Grieve | 9ce44b9 | 2017-07-25 02:43:05 | [diff] [blame^] | 84 | classpath, |
Ingemar Ådahl | 6ada47b | 2017-07-10 17:14:01 | [diff] [blame] | 85 | MANIFEST_MERGER_MAIN_CLASS, |
| 86 | '--out', args.output, |
| 87 | ] |
| 88 | |
| 89 | extras = build_utils.ParseGnList(args.extras) |
| 90 | if extras: |
| 91 | cmd += ['--libs', ':'.join(extras)] |
| 92 | |
| 93 | with _PatchedManifest(args.root_manifest) as root_manifest: |
| 94 | cmd += ['--main', root_manifest] |
| 95 | build_utils.CheckOutput(cmd, |
| 96 | # https://issuetracker.google.com/issues/63514300: The merger doesn't set |
| 97 | # a nonzero exit code for failures. |
| 98 | fail_func=lambda returncode, stderr: returncode != 0 or |
| 99 | build_utils.IsTimeStale(args.output, [root_manifest] + extras)) |
Andrew Grieve | 9ce44b9 | 2017-07-25 02:43:05 | [diff] [blame^] | 100 | if args.depfile: |
| 101 | inputs = extras + classpath.split(':') |
| 102 | build_utils.WriteDepfile(args.depfile, args.output, inputs=inputs) |
Ingemar Ådahl | 6ada47b | 2017-07-10 17:14:01 | [diff] [blame] | 103 | |
| 104 | |
| 105 | if __name__ == '__main__': |
| 106 | main(sys.argv[1:]) |