blob: f07ce7a2d2bdf4df4eb0b3c2119c451b4a316ee9 [file] [log] [blame]
#!/usr/bin/env python
# Copyright 2017 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.
"""Merges dependency Android manifests into a root manifest."""
import argparse
import contextlib
import os
import sys
import tempfile
import xml.dom.minidom as minidom
from util import build_utils
# Tools library directory - relative to Android SDK root
SDK_TOOLS_LIB_DIR = os.path.join('tools', 'lib')
MANIFEST_MERGER_MAIN_CLASS = 'com.android.manifmerger.Merger'
MANIFEST_MERGER_JARS = [
'common{suffix}.jar',
'manifest-merger{suffix}.jar',
'sdk-common{suffix}.jar',
'sdklib{suffix}.jar',
]
TOOLS_NAMESPACE_PREFIX = 'tools'
TOOLS_NAMESPACE = 'http://schemas.android.com/tools'
@contextlib.contextmanager
def _ProcessManifest(manifest_path):
"""Patches an Android manifest to always include the 'tools' namespace
declaration, as it is not propagated by the manifest merger from the SDK.
See https://issuetracker.google.com/issues/63411481
"""
doc = minidom.parse(manifest_path)
manifests = doc.getElementsByTagName('manifest')
assert len(manifests) == 1
manifest = manifests[0]
package = manifest.getAttribute('package')
manifest.setAttribute('xmlns:%s' % TOOLS_NAMESPACE_PREFIX, TOOLS_NAMESPACE)
tmp_prefix = os.path.basename(manifest_path)
with tempfile.NamedTemporaryFile(prefix=tmp_prefix) as patched_manifest:
doc.writexml(patched_manifest)
patched_manifest.flush()
yield patched_manifest.name, package
def _BuildManifestMergerClasspath(build_vars):
return ':'.join([
os.path.join(
build_vars['android_sdk_root'],
SDK_TOOLS_LIB_DIR,
jar.format(suffix=build_vars['android_sdk_tools_version_suffix']))
for jar in MANIFEST_MERGER_JARS
])
def main(argv):
argv = build_utils.ExpandFileArgs(argv)
parser = argparse.ArgumentParser(description=__doc__)
build_utils.AddDepfileOption(parser)
parser.add_argument('--build-vars',
help='Path to GN build vars file',
required=True)
parser.add_argument('--root-manifest',
help='Root manifest which to merge into',
required=True)
parser.add_argument('--output', help='Output manifest path', required=True)
parser.add_argument('--extras',
help='GN list of additional manifest to merge')
args = parser.parse_args(argv)
classpath = _BuildManifestMergerClasspath(
build_utils.ReadBuildVars(args.build_vars))
with build_utils.AtomicOutput(args.output) as f:
cmd = [
'java',
'-cp',
classpath,
MANIFEST_MERGER_MAIN_CLASS,
'--out', f.name,
]
extras = build_utils.ParseGnList(args.extras)
if extras:
cmd += ['--libs', ':'.join(extras)]
with _ProcessManifest(args.root_manifest) as tup:
root_manifest, package = tup
cmd += ['--main', root_manifest, '--property', 'PACKAGE=' + package]
build_utils.CheckOutput(cmd,
# https://issuetracker.google.com/issues/63514300:
# The merger doesn't set a nonzero exit code for failures.
fail_func=lambda returncode, stderr: returncode != 0 or
build_utils.IsTimeStale(f.name, [root_manifest] + extras))
if args.depfile:
inputs = extras + classpath.split(':')
build_utils.WriteDepfile(args.depfile, args.output, inputs=inputs,
add_pydeps=False)
if __name__ == '__main__':
main(sys.argv[1:])