Ejemplo n.º 1
0
def _FixManifest(options, temp_dir):
    """Fix the APK's AndroidManifest.xml.

  This adds any missing namespaces for 'android' and 'tools', and
  sets certains elements like 'platformBuildVersionCode' or
  'android:debuggable' depending on the content of |options|.

  Args:
    options: The command-line arguments tuple.
    temp_dir: A temporary directory where the fixed manifest will be written to.
  Returns:
    Tuple of:
     * Manifest path within |temp_dir|.
     * Original package_name.
  """
    def maybe_extract_version(j):
        try:
            return resource_utils.ExtractBinaryManifestValues(
                options.aapt2_path, j)
        except build_utils.CalledProcessError:
            return None

    android_sdk_jars = [
        j for j in options.include_resources
        if os.path.basename(j) in ('android.jar', 'android_system.jar')
    ]
    extract_all = [maybe_extract_version(j) for j in android_sdk_jars]
    successful_extractions = [x for x in extract_all if x]
    if len(successful_extractions) == 0:
        raise Exception('Unable to find android SDK jar among candidates: %s' %
                        ', '.join(android_sdk_jars))
    elif len(successful_extractions) > 1:
        raise Exception(
            'Found multiple android SDK jars among candidates: %s' %
            ', '.join(android_sdk_jars))
    version_code, version_name = successful_extractions.pop()[:2]

    debug_manifest_path = os.path.join(temp_dir, 'AndroidManifest.xml')
    doc, manifest_node, app_node = manifest_utils.ParseManifest(
        options.android_manifest)

    manifest_utils.AssertUsesSdk(manifest_node, options.min_sdk_version,
                                 options.target_sdk_version)
    # We explicitly check that maxSdkVersion is set in the manifest since we don't
    # add it later like minSdkVersion and targetSdkVersion.
    manifest_utils.AssertUsesSdk(manifest_node,
                                 max_sdk_version=options.max_sdk_version,
                                 fail_if_not_exist=True)
    manifest_utils.AssertPackage(manifest_node, options.manifest_package)

    manifest_node.set('platformBuildVersionCode', version_code)
    manifest_node.set('platformBuildVersionName', version_name)

    orig_package = manifest_node.get('package')
    if options.arsc_package_name:
        manifest_node.set('package', options.arsc_package_name)

    if options.debuggable:
        app_node.set(
            '{%s}%s' % (manifest_utils.ANDROID_NAMESPACE, 'debuggable'),
            'true')

    manifest_utils.SaveManifest(doc, debug_manifest_path)
    return debug_manifest_path, orig_package
Ejemplo n.º 2
0
def _RunLint(lint_path,
             config_path,
             manifest_path,
             result_path,
             product_dir,
             sources,
             cache_dir,
             android_sdk_version,
             srcjars,
             min_sdk_version,
             manifest_package,
             resource_sources,
             resource_zips,
             can_fail_build=False,
             include_unexpected=False,
             silent=False):
    logging.info('Lint starting')

    def _RebasePath(path):
        """Returns relative path to top-level src dir.

    Args:
      path: A path relative to cwd.
    """
        ret = os.path.relpath(os.path.abspath(path),
                              build_utils.DIR_SOURCE_ROOT)
        # If it's outside of src/, just use abspath.
        if ret.startswith('..'):
            ret = os.path.abspath(path)
        return ret

    def _ProcessResultFile():
        with open(result_path, 'rb') as f:
            content = f.read().replace(_RebasePath(product_dir), 'PRODUCT_DIR')

        with open(result_path, 'wb') as f:
            f.write(content)

    def _ParseAndShowResultFile():
        dom = minidom.parse(result_path)
        issues = dom.getElementsByTagName('issue')
        if not silent:
            print(file=sys.stderr)
            for issue in issues:
                issue_id = issue.attributes['id'].value
                message = issue.attributes['message'].value
                location_elem = issue.getElementsByTagName('location')[0]
                path = location_elem.attributes['file'].value
                line = location_elem.getAttribute('line')
                error = '%s:%s %s: %s [warning]' % (path, line, message,
                                                    issue_id)
                print(error.encode('utf-8'), file=sys.stderr)
                for attr in ['errorLine1', 'errorLine2']:
                    error_line = issue.getAttribute(attr)
                    if error_line:
                        print(error_line.encode('utf-8'), file=sys.stderr)
        return len(issues)

    with build_utils.TempDir() as temp_dir:
        cmd = [
            _RebasePath(lint_path),
            '-Werror',
            '--exitcode',
            '--showall',
            '--xml',
            _RebasePath(result_path),
        ]
        if config_path:
            cmd.extend(['--config', _RebasePath(config_path)])

        tmp_dir_counter = [0]

        def _NewTempSubdir(prefix, append_digit=True):
            # Helper function to create a new sub directory based on the number of
            # subdirs created earlier.
            if append_digit:
                tmp_dir_counter[0] += 1
                prefix += str(tmp_dir_counter[0])
            new_dir = os.path.join(temp_dir, prefix)
            os.makedirs(new_dir)
            return new_dir

        resource_dirs = resource_utils.DeduceResourceDirsFromFileList(
            resource_sources)
        # These are zip files with generated resources (e. g. strings from GRD).
        for resource_zip in resource_zips:
            resource_dir = _NewTempSubdir(resource_zip, append_digit=False)
            resource_dirs.append(resource_dir)
            build_utils.ExtractAll(resource_zip, path=resource_dir)

        for resource_dir in resource_dirs:
            cmd.extend(['--resources', _RebasePath(resource_dir)])

        # There may be multiple source files with the same basename (but in
        # different directories). It is difficult to determine what part of the path
        # corresponds to the java package, and so instead just link the source files
        # into temporary directories (creating a new one whenever there is a name
        # conflict).
        def PathInDir(d, src):
            subpath = os.path.join(d, _RebasePath(src))
            subdir = os.path.dirname(subpath)
            if not os.path.exists(subdir):
                os.makedirs(subdir)
            return subpath

        src_dirs = []
        for src in sources:
            src_dir = None
            for d in src_dirs:
                if not os.path.exists(PathInDir(d, src)):
                    src_dir = d
                    break
            if not src_dir:
                src_dir = _NewTempSubdir('SRC_ROOT')
                src_dirs.append(src_dir)
                cmd.extend(['--sources', _RebasePath(src_dir)])
            # In cases where the build dir is outside of the src dir, this can
            # result in trying to symlink a file to itself for this file:
            # gen/components/version_info/android/java/org/chromium/
            #   components/version_info/VersionConstants.java
            src = os.path.abspath(src)
            dst = PathInDir(src_dir, src)
            if src == dst:
                continue
            os.symlink(src, dst)

        if srcjars:
            srcjar_dir = _NewTempSubdir('GENERATED_SRC_ROOT',
                                        append_digit=False)
            cmd.extend(['--sources', _RebasePath(srcjar_dir)])
            for srcjar in srcjars:
                # We choose to allow srcjars that contain java files which have the
                # same package and name to clobber each other. This happens for
                # generated files like BuildConfig.java. It is generated for
                # targets like base_build_config_gen as well as targets like
                # chrome_modern_public_base_bundle_module__build_config_srcjar.
                # Although we could extract each srcjar to a separate folder, that
                # slows down some invocations of lint by 20 seconds or more.
                # TODO(wnwen): Switch lint.py to generate a project.xml file which
                #              supports srcjar inputs by default.
                build_utils.ExtractAll(srcjar,
                                       path=srcjar_dir,
                                       no_clobber=False)

        project_dir = _NewTempSubdir('PROJECT_ROOT', append_digit=False)
        if android_sdk_version:
            # Create dummy project.properies file in a temporary "project" directory.
            # It is the only way to add Android SDK to the Lint's classpath. Proper
            # classpath is necessary for most source-level checks.
            with open(os.path.join(project_dir, 'project.properties'), 'w') \
                as propfile:
                print('target=android-{}'.format(android_sdk_version),
                      file=propfile)

        # Put the manifest in a temporary directory in order to avoid lint detecting
        # sibling res/ and src/ directories (which should be pass explicitly if they
        # are to be included).
        if not manifest_path:
            manifest_path = os.path.join(build_utils.DIR_SOURCE_ROOT, 'build',
                                         'android', 'AndroidManifest.xml')
        lint_manifest_path = os.path.join(project_dir, 'AndroidManifest.xml')
        shutil.copyfile(os.path.abspath(manifest_path), lint_manifest_path)

        # Check that minSdkVersion and package is correct and add it to the manifest
        # in case it does not exist.
        doc, manifest, _ = manifest_utils.ParseManifest(lint_manifest_path)
        manifest_utils.AssertUsesSdk(manifest, min_sdk_version)
        manifest_utils.AssertPackage(manifest, manifest_package)
        uses_sdk = manifest.find('./uses-sdk')
        if uses_sdk is None:
            uses_sdk = ElementTree.Element('uses-sdk')
            manifest.insert(0, uses_sdk)
        uses_sdk.set('{%s}minSdkVersion' % manifest_utils.ANDROID_NAMESPACE,
                     min_sdk_version)
        if manifest_package:
            manifest.set('package', manifest_package)
        manifest_utils.SaveManifest(doc, lint_manifest_path)

        cmd.append(project_dir)

        if os.path.exists(result_path):
            os.remove(result_path)

        env = os.environ.copy()
        stderr_filter = build_utils.FilterReflectiveAccessJavaWarnings
        if cache_dir:
            env['_JAVA_OPTIONS'] = '-Duser.home=%s' % _RebasePath(cache_dir)
            # When _JAVA_OPTIONS is set, java prints to stderr:
            # Picked up _JAVA_OPTIONS: ...
            #
            # We drop all lines that contain _JAVA_OPTIONS from the output
            stderr_filter = lambda l: re.sub(
                r'.*_JAVA_OPTIONS.*\n?', '',
                build_utils.FilterReflectiveAccessJavaWarnings(l))

        def fail_func(returncode, stderr):
            if returncode != 0:
                return True
            if (include_unexpected
                    and 'Unexpected failure during lint analysis' in stderr):
                return True
            return False

        try:
            env['JAVA_HOME'] = os.path.relpath(build_utils.JAVA_HOME,
                                               build_utils.DIR_SOURCE_ROOT)
            logging.debug('Lint command %s', cmd)
            start = time.time()
            build_utils.CheckOutput(cmd,
                                    cwd=build_utils.DIR_SOURCE_ROOT,
                                    env=env or None,
                                    stderr_filter=stderr_filter,
                                    fail_func=fail_func)
            end = time.time() - start
            logging.info('Lint command took %ss', end)
        except build_utils.CalledProcessError:
            # There is a problem with lint usage
            if not os.path.exists(result_path):
                raise

            # Sometimes produces empty (almost) files:
            if os.path.getsize(result_path) < 10:
                if can_fail_build:
                    raise
                elif not silent:
                    traceback.print_exc()
                return

            # There are actual lint issues
            try:
                num_issues = _ParseAndShowResultFile()
            except Exception:  # pylint: disable=broad-except
                if not silent:
                    print('Lint created unparseable xml file...')
                    print('File contents:')
                    with open(result_path) as f:
                        print(f.read())
                    if can_fail_build:
                        traceback.print_exc()
                if can_fail_build:
                    raise
                else:
                    return

            _ProcessResultFile()
            if num_issues == 0 and include_unexpected:
                msg = 'Please refer to output above for unexpected lint failures.\n'
            else:
                msg = (
                    '\nLint found %d new issues.\n'
                    ' - For full explanation, please refer to %s\n'
                    ' - For more information about lint and how to fix lint issues,'
                    ' please refer to %s\n' %
                    (num_issues, _RebasePath(result_path), _LINT_MD_URL))
            if not silent:
                print(msg, file=sys.stderr)
            if can_fail_build:
                raise Exception('Lint failed.')

    logging.info('Lint completed')
Ejemplo n.º 3
0
def main(argv):
    argv = build_utils.ExpandFileArgs(argv)
    parser = argparse.ArgumentParser(description=__doc__)
    build_utils.AddDepfileOption(parser)
    parser.add_argument(
        '--android-sdk-root',
        help='Path to root of SDK providing the manifest merger tool.',
        required=True)
    parser.add_argument(
        '--android-sdk-tools-version-suffix',
        help='Version suffix for SDK providing the manifest merger tool.',
        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')
    parser.add_argument('--min-sdk-version',
                        required=True,
                        help='android:minSdkVersion for merging.')
    parser.add_argument('--target-sdk-version',
                        required=True,
                        help='android:targetSdkVersion for merging.')
    parser.add_argument('--max-sdk-version',
                        help='android:maxSdkVersion for merging.')
    parser.add_argument('--manifest-package',
                        help='Package name of the merged AndroidManifest.xml.')
    args = parser.parse_args(argv)

    classpath = _BuildManifestMergerClasspath(
        args.android_sdk_root, args.android_sdk_tools_version_suffix)

    with build_utils.AtomicOutput(args.output) as output:
        cmd = [
            build_utils.JAVA_PATH,
            '-cp',
            classpath,
            _MANIFEST_MERGER_MAIN_CLASS,
            '--out',
            output.name,
            '--property',
            'MIN_SDK_VERSION=' + args.min_sdk_version,
            '--property',
            'TARGET_SDK_VERSION=' + args.target_sdk_version,
        ]

        if args.max_sdk_version:
            cmd += [
                '--property',
                'MAX_SDK_VERSION=' + args.max_sdk_version,
            ]

        extras = build_utils.ParseGnList(args.extras)
        if extras:
            cmd += ['--libs', ':'.join(extras)]

        with _ProcessManifest(args.root_manifest, args.min_sdk_version,
                              args.target_sdk_version, args.max_sdk_version,
                              args.manifest_package) 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(output.name, [root_manifest] + extras))

        # Check for correct output.
        _, manifest, _ = manifest_utils.ParseManifest(output.name)
        manifest_utils.AssertUsesSdk(manifest, args.min_sdk_version,
                                     args.target_sdk_version)
        manifest_utils.AssertPackage(manifest, package)

    if args.depfile:
        inputs = extras + classpath.split(':')
        build_utils.WriteDepfile(args.depfile,
                                 args.output,
                                 inputs=inputs,
                                 add_pydeps=False)
Ejemplo n.º 4
0
def _FixManifest(options, temp_dir, extra_manifest=None):
    """Fix the APK's AndroidManifest.xml.

  This adds any missing namespaces for 'android' and 'tools', and
  sets certains elements like 'platformBuildVersionCode' or
  'android:debuggable' depending on the content of |options|.

  Args:
    options: The command-line arguments tuple.
    temp_dir: A temporary directory where the fixed manifest will be written to.
    extra_manifest: Path to an AndroidManifest.xml file which will get merged
        into the application node of the base manifest.
  Returns:
    Tuple of:
     * Manifest path within |temp_dir|.
     * Original package_name.
  """
    def maybe_extract_version(j):
        try:
            return resource_utils.ExtractBinaryManifestValues(
                options.aapt2_path, j)
        except build_utils.CalledProcessError:
            return None

    android_sdk_jars = [
        j for j in options.include_resources
        if os.path.basename(j) in ('android.jar', 'android_system.jar')
    ]
    extract_all = [maybe_extract_version(j) for j in android_sdk_jars]
    successful_extractions = [x for x in extract_all if x]
    if len(successful_extractions) == 0:
        raise Exception('Unable to find android SDK jar among candidates: %s' %
                        ', '.join(android_sdk_jars))
    elif len(successful_extractions) > 1:
        raise Exception(
            'Found multiple android SDK jars among candidates: %s' %
            ', '.join(android_sdk_jars))
    version_code, version_name = successful_extractions.pop()[:2]

    debug_manifest_path = os.path.join(temp_dir, 'AndroidManifest.xml')
    doc, manifest_node, app_node = manifest_utils.ParseManifest(
        options.android_manifest)

    if extra_manifest:
        _, extra_manifest_node, extra_app_node = manifest_utils.ParseManifest(
            extra_manifest)
        for node in extra_app_node:
            app_node.append(node)
        for node in extra_manifest_node:
            # DFM manifests have a bunch of tags we don't care about inside
            # <manifest>, so only take <queries>.
            if node.tag == 'queries':
                manifest_node.append(node)

    manifest_utils.AssertUsesSdk(manifest_node, options.min_sdk_version,
                                 options.target_sdk_version)
    # We explicitly check that maxSdkVersion is set in the manifest since we don't
    # add it later like minSdkVersion and targetSdkVersion.
    manifest_utils.AssertUsesSdk(manifest_node,
                                 max_sdk_version=options.max_sdk_version,
                                 fail_if_not_exist=True)
    manifest_utils.AssertPackage(manifest_node, options.manifest_package)

    manifest_node.set('platformBuildVersionCode', version_code)
    manifest_node.set('platformBuildVersionName', version_name)

    orig_package = manifest_node.get('package')
    if options.arsc_package_name:
        manifest_node.set('package', options.arsc_package_name)

    if options.debuggable:
        app_node.set(
            '{%s}%s' % (manifest_utils.ANDROID_NAMESPACE, 'debuggable'),
            'true')

    if options.uses_split:
        uses_split = ElementTree.SubElement(manifest_node, 'uses-split')
        uses_split.set('{%s}name' % manifest_utils.ANDROID_NAMESPACE,
                       options.uses_split)

    # Make sure the min-sdk condition is not less than the min-sdk of the bundle.
    for min_sdk_node in manifest_node.iter('{%s}min-sdk' %
                                           manifest_utils.DIST_NAMESPACE):
        dist_value = '{%s}value' % manifest_utils.DIST_NAMESPACE
        if int(min_sdk_node.get(dist_value)) < int(options.min_sdk_version):
            min_sdk_node.set(dist_value, options.min_sdk_version)

    manifest_utils.SaveManifest(doc, debug_manifest_path)
    return debug_manifest_path, orig_package