| #!/usr/bin/env python |
| # |
| # Copyright 2014 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. |
| |
| """Writes a build_config file. |
| |
| The build_config file for a target is a json file containing information about |
| how to build that target based on the target's dependencies. This includes |
| things like: the javac classpath, the list of android resources dependencies, |
| etc. It also includes the information needed to create the build_config for |
| other targets that depend on that one. |
| |
| Android build scripts should not refer to the build_config directly, and the |
| build specification should instead pass information in using the special |
| file-arg syntax (see build_utils.py:ExpandFileArgs). That syntax allows passing |
| of values in a json dict in a file and looks like this: |
| --python-arg=@FileArg(build_config_path:javac:classpath) |
| |
| Note: If paths to input files are passed in this way, it is important that: |
| 1. inputs/deps of the action ensure that the files are available the first |
| time the action runs. |
| 2. Either (a) or (b) |
| a. inputs/deps ensure that the action runs whenever one of the files changes |
| b. the files are added to the action's depfile |
| |
| NOTE: All paths within .build_config files are relative to $OUTPUT_CHROMIUM_DIR. |
| |
| This is a technical note describing the format of .build_config files. |
| Please keep it updated when changing this script. For extraction and |
| visualization instructions, see build/android/docs/build_config.md |
| |
| ------------- BEGIN_MARKDOWN --------------------------------------------------- |
| The .build_config file format |
| === |
| |
| # Introduction |
| |
| This document tries to explain the format of `.build_config` generated during |
| the Android build of Chromium. For a higher-level explanation of these files, |
| please read |
| [build/android/docs/build_config.md](build/android/docs/build_config.md). |
| |
| # The `deps_info` top-level dictionary: |
| |
| All `.build_config` files have a required `'deps_info'` key, whose value is a |
| dictionary describing the target and its dependencies. The latter has the |
| following required keys: |
| |
| ## Required keys in `deps_info`: |
| |
| * `deps_info['type']`: The target type as a string. |
| |
| The following types are known by the internal GN build rules and the |
| build scripts altogether: |
| |
| * [java_binary](#target_java_binary) |
| * [java_annotation_processor](#target_java_annotation_processor) |
| * [junit_binary](#target_junit_binary) |
| * [java_library](#target_java_library) |
| * [android_assets](#target_android_assets) |
| * [android_resources](#target_android_resources) |
| * [android_apk](#target_android_apk) |
| * [android_app_bundle_module](#target_android_app_bundle_module) |
| * [android_app_bundle](#target_android_app_bundle) |
| * [dist_jar](#target_dist_jar) |
| * [dist_aar](#target_dist_aar) |
| * [resource_rewriter](#target_resource_rewriter) |
| * [group](#target_group) |
| |
| See later sections for more details of some of these. |
| |
| * `deps_info['path']`: Path to the target's `.build_config` file. |
| |
| * `deps_info['name']`: Nothing more than the basename of `deps_info['path']` |
| at the moment. |
| |
| * `deps_info['deps_configs']`: List of paths to the `.build_config` files of |
| all *direct* dependencies of the current target. |
| |
| NOTE: Because the `.build_config` of a given target is always generated |
| after the `.build_config` of its dependencies, the `write_build_config.py` |
| script can use chains of `deps_configs` to compute transitive dependencies |
| for each target when needed. |
| |
| ## Optional keys in `deps_info`: |
| |
| The following keys will only appear in the `.build_config` files of certain |
| target types: |
| |
| * `deps_info['requires_android']`: True to indicate that the corresponding |
| code uses Android-specific APIs, and thus cannot run on the host within a |
| regular JVM. May only appear in Java-related targets. |
| |
| * `deps_info['supports_android']`: |
| May appear in Java-related targets, and indicates that |
| the corresponding code doesn't use Java APIs that are not available on |
| Android. As such it may run either on the host or on an Android device. |
| |
| * `deps_info['assets']`: |
| Only seen for the [`android_assets`](#target_android_assets) type. See below. |
| |
| * `deps_info['package_name']`: Java package name associated with this target. |
| |
| NOTE: For `android_resources` targets, |
| this is the package name for the corresponding R class. For `android_apk` |
| targets, this is the corresponding package name. This does *not* appear for |
| other target types. |
| |
| * `deps_info['android_manifest']`: |
| Path to an AndroidManifest.xml file related to the current target. |
| |
| * `deps_info['base_module_config']`: |
| Only seen for the [`android_app_bundle`](#target_android_app_bundle) type. |
| Path to the base module for the bundle. |
| |
| * `deps_info['is_base_module']`: |
| Only seen for the |
| [`android_app_bundle_module`](#target_android_app_bundle_module) |
| type. Whether or not this module is the base module for some bundle. |
| |
| # Top-level `resources` dictionary: |
| |
| This dictionary only appears for a few target types that can contain or |
| relate to Android resources (e.g. `android_resources` or `android_apk`): |
| |
| * `resources['dependency_zips']`: |
| List of `deps_info['resources_zip']` entries for all `android_resources` |
| dependencies for the current target. |
| |
| * `resource['extra_package_names']`: |
| Always empty for `android_resources` types. Otherwise, |
| the list of `deps_info['package_name']` entries for all `android_resources` |
| dependencies for the current target. Computed automatically by |
| `write_build_config.py`. |
| |
| * `resources['extra_r_text_files']`: |
| Always empty for `android_resources` types. Otherwise, the list of |
| `deps_info['r_text']` entries for all `android_resources` dependencies for |
| the current target. Computed automatically. |
| |
| |
| # `.build_config` target types description: |
| |
| ## <a name="target_group">Target type `group`</a>: |
| |
| This type corresponds to a simple target that is only used to group |
| dependencies. It matches the `java_group()` GN template. Its only top-level |
| `deps_info` keys are `supports_android` (always True), and `deps_configs`. |
| |
| |
| ## <a name="target_android_resources">Target type `android_resources`</a>: |
| |
| This type corresponds to targets that are used to group Android resource files. |
| For example, all `android_resources` dependencies of an `android_apk` will |
| end up packaged into the final APK by the build system. |
| |
| It uses the following keys: |
| |
| * `deps_info['resource_dirs']`: |
| List of paths to the source directories containing the resources for this |
| target. This key is optional, because some targets can refer to prebuilt |
| `.aar` archives. |
| |
| |
| * `deps_info['resources_zip']`: |
| *Required*. Path to the `.resources.zip` file that contains all raw/uncompiled |
| resource files for this target (and also no `R.txt`, `R.java` or `R.class`). |
| |
| If `deps_info['resource_dirs']` is missing, this must point to a prebuilt |
| `.aar` archive containing resources. Otherwise, this will point to a |
| zip archive generated at build time, wrapping the content of |
| `deps_info['resource_dirs']` into a single zip file. |
| |
| * `deps_info['package_name']`: |
| Java package name that the R class for this target belongs to. |
| |
| * `deps_info['android_manifest']`: |
| Optional. Path to the top-level Android manifest file associated with these |
| resources (if not provided, an empty manifest will be used to generate R.txt). |
| |
| * `deps_info['r_text']`: |
| Provide the path to the `R.txt` file that describes the resources wrapped by |
| this target. Normally this file is generated from the content of the resource |
| directories or zip file, but some targets can provide their own `R.txt` file |
| if they want. |
| |
| * `deps_info['srcjar_path']`: |
| Path to the `.srcjar` file that contains the auto-generated `R.java` source |
| file corresponding to the content of `deps_info['r_text']`. This is *always* |
| generated from the content of `deps_info['r_text']` by the |
| `build/android/gyp/process_resources.py` script. |
| |
| |
| ## <a name="target_android_assets">Target type `android_assets`</a>: |
| |
| This type corresponds to targets used to group Android assets, i.e. liberal |
| files that will be placed under `//assets/` within the final APK. |
| |
| These use an `deps_info['assets']` key to hold a dictionary of values related |
| to assets covered by this target. |
| |
| * `assets['sources']`: |
| The list of all asset source paths for this target. Each source path can |
| use an optional `:<zipPath>` suffix, where `<zipPath>` is the final location |
| of the assets (relative to `//assets/`) within the APK. |
| |
| * `assets['outputs']`: |
| Optional. Some of the sources might be renamed before being stored in the |
| final //assets/ sub-directory. When this happens, this contains a list of |
| all renamed output file paths |
| |
| NOTE: When not empty, the first items of `assets['sources']` must match |
| every item in this list. Extra sources correspond to non-renamed sources. |
| |
| NOTE: This comes from the `asset_renaming_destinations` parameter for the |
| `android_assets()` GN template. |
| |
| * `assets['disable_compression']`: |
| Optional. Will be True to indicate that these assets should be stored |
| uncompressed in the final APK. For example, this is necessary for locale |
| .pak files used by the System WebView feature. |
| |
| * `assets['treat_as_locale_paks']`: |
| Optional. Will be True to indicate that these assets are locale `.pak` files |
| (containing localized strings for C++). These are later processed to generate |
| a special ``.build_config`.java` source file, listing all supported Locales in |
| the current build. |
| |
| |
| ## <a name="target_java_library">Target type `java_library`</a>: |
| |
| This type is used to describe target that wrap Java bytecode, either created |
| by compiling sources, or providing them with a prebuilt jar. |
| |
| * `deps_info['unprocessed_jar_path']`: |
| Path to the original .jar file for this target, before any kind of processing |
| through Proguard or other tools. For most targets this is generated |
| from sources, with a name like `$target_name.javac.jar`. However, when using |
| a prebuilt jar, this will point to the source archive directly. |
| |
| * `deps_info['jar_path']`: |
| Path to a file that is the result of processing |
| `deps_info['unprocessed_jar_path']` with various tools. |
| |
| * `deps_info['interface_jar_path']: |
| Path to the interface jar generated for this library. This corresponds to |
| a jar file that only contains declarations. Generated by running the `ijar` |
| tool on `deps_info['jar_path']` |
| |
| * `deps_info['dex_path']`: |
| Path to the `.dex` file generated for this target, from `deps_info['jar_path']` |
| unless this comes from a prebuilt `.aar` archive. |
| |
| * `deps_info['is_prebuilt']`: |
| True to indicate that this target corresponds to a prebuilt `.jar` file. |
| In this case, `deps_info['unprocessed_jar_path']` will point to the source |
| `.jar` file. Otherwise, it will be point to a build-generated file. |
| |
| * `deps_info['java_sources_file']`: |
| Path to a single `.sources` file listing all the Java sources that were used |
| to generate the library (simple text format, one `.jar` path per line). |
| |
| * `deps_info['owned_resource_dirs']`: |
| List of all resource directories belonging to all resource dependencies for |
| this target. |
| |
| * `deps_info['owned_resource_zips']`: |
| List of all resource zip files belonging to all resource dependencies for this |
| target. |
| |
| * `deps_info['owned_resource_srcjars']`: |
| List of all .srcjar files belonging to all resource dependencies for this |
| target. |
| |
| * `deps_info['javac']`: |
| A dictionary containing information about the way the sources in this library |
| are compiled. Appears also on other Java-related targets. See the [dedicated |
| section about this](#dict_javac) below for details. |
| |
| * `deps_info['javac_full_classpath']`: |
| The classpath used when performing bytecode processing. Essentially the |
| collection of all `deps_info['unprocessed_jar_path']` entries for the target |
| and all its dependencies. |
| |
| * `deps_info['javac_full_interface_classpath']`: |
| The classpath used when using the errorprone compiler. |
| |
| * `deps_info['proguard_enabled"]`: |
| True to indicate that ProGuard processing is enabled for this target. |
| |
| * `deps_info['proguard_configs"]`: |
| A list of paths to ProGuard configuration files related to this library. |
| |
| * `deps_info['extra_classpath_jars']: |
| For some Java related types, a list of extra `.jar` files to use at build time |
| but not at runtime. |
| |
| * `deps_info['extra_classpath_interface_jars']: |
| The interface jars corresponding to extra_classpath_jars. |
| |
| ## <a name="target_java_binary">Target type `java_binary`</a>: |
| |
| This type corresponds to a Java binary, which is nothing more than a |
| `java_library` target that also provides a main class name. It thus inherits |
| all entries from the `java_library` type, and adds: |
| |
| * `deps_info['main_class']`: |
| Name of the main Java class that serves as an entry point for the binary. |
| |
| * `deps_info['java_runtime_classpath']`: |
| The classpath used when running a Java or Android binary. Essentially the |
| collection of all `deps_info['jar_path']` entries for the target and all its |
| dependencies. |
| |
| |
| ## <a name="target_junit_binary">Target type `junit_binary`</a>: |
| |
| A target type for JUnit-specific binaries. Identical to |
| [`java_binary`](#target_java_binary) in the context of `.build_config` files, |
| except the name. |
| |
| |
| ## <a name="target_java_annotation_processor">Target type \ |
| `java_annotation_processor`</a>: |
| |
| A target type for Java annotation processors. Identical to |
| [`java_binary`](#target_java_binary) in the context of `.build_config` files, |
| except the name, except that it requires a `deps_info['main_class']` entry. |
| |
| |
| ## <a name="target_android_apk">Target type `android_apk`</a>: |
| |
| Corresponds to an Android APK. Inherits from the |
| [`java_binary`](#target_java_binary) type and adds: |
| |
| * `deps_info['apk_path']`: |
| Path to the raw, unsigned, APK generated by this target. |
| |
| * `deps_info['incremental_apk_path']`: |
| Path to the raw, unsigned, incremental APK generated by this target. |
| |
| * `deps_info['incremental_install_json_path']`: |
| Path to the JSON file with per-apk details for incremental install. |
| See `build/android/gyp/incremental/write_installer_json.py` for more |
| details about its content. |
| |
| * `deps_info['dist_jar']['all_interface_jars']`: |
| For `android_apk` and `dist_jar` targets, a list of all interface jar files |
| that will be merged into the final `.jar` file for distribution. |
| |
| * `deps_info['final_dex']['path']`: |
| Path to the final classes.dex file (or classes.zip in case of multi-dex) |
| for this APK. |
| |
| * `deps_info['final_dex']['dependency_dex_files']`: |
| The list of paths to all `deps_info['dex_path']` entries for all library |
| dependencies for this APK. |
| |
| * `native['libraries']` |
| List of native libraries for the primary ABI to be embedded in this APK. |
| E.g. [ "libchrome.so" ] (i.e. this doesn't include any ABI sub-directory |
| prefix). |
| |
| * `native['java_libraries_list']` |
| The same list as `native['libraries']` as a string holding a Java source |
| fragment, e.g. `"{\"chrome\"}"`, without any `lib` prefix, and `.so` |
| suffix (as expected by `System.loadLibrary()`). |
| |
| * `native['second_abi_libraries']` |
| List of native libraries for the secondary ABI to be embedded in this APK. |
| Empty if only a single ABI is supported. |
| |
| * `native['uncompress_shared_libraries']` |
| A boolean indicating whether native libraries are stored uncompressed in the |
| APK. |
| |
| * `native['extra_shared_libraries']` |
| A list of native libraries to store within the APK, in addition to those from |
| `native['libraries']`. These correspond to things like the Chromium linker |
| or instrumentation libraries. |
| |
| * `assets` |
| A list of assets stored compressed in the APK. Each entry has the format |
| `<source-path>:<destination-path>`, where `<source-path>` is relative to |
| `$CHROMIUM_OUTPUT_DIR`, and `<destination-path>` is relative to `//assets/` |
| within the APK. |
| |
| NOTE: Not to be confused with the `deps_info['assets']` dictionary that |
| belongs to `android_assets` targets only. |
| |
| * `uncompressed_assets` |
| A list of uncompressed assets stored in the APK. Each entry has the format |
| `<source-path>:<destination-path>` too. |
| |
| * `compressed_locales_java_list` |
| A string holding a Java source fragment that gives the list of locales stored |
| compressed in the `//assets/` directory. E.g. `"{\"am\","\ar\",\"en-US\"}"`. |
| Note that the files will be stored with the `.pak` extension (e.g. |
| `//assets/en-US.pak`). |
| |
| * `uncompressed_locales_java_list` |
| A string holding a Java source fragment that gives the list of locales stored |
| uncompressed in the `//assets/stored-locales/` directory. These are used for |
| the System WebView feature only. Note that the files will be stored with the |
| `.pak` extension (e.g. `//assets/stored-locales/en-US.apk`). |
| |
| * `extra_android_manifests` |
| A list of `deps_configs['android_manifest]` entries, for all resource |
| dependencies for this target. I.e. a list of paths to manifest files for |
| all the resources in this APK. These will be merged with the root manifest |
| file to generate the final one used to build the APK. |
| |
| * `java_resources_jars` |
| This is a list of `.jar` files whose *Java* resources should be included in |
| the final APK. For example, this is used to copy the `.res` files from the |
| EMMA Coverage tool. The copy will omit any `.class` file and the top-level |
| `//meta-inf/` directory from the input jars. Everything else will be copied |
| into the final APK as-is. |
| |
| NOTE: This has nothing to do with *Android* resources. |
| |
| * `jni['all_source']` |
| The list of all `deps_info['java_sources_file']` entries for all library |
| dependencies for this APK. Note: this is a list of files, where each file |
| contains a list of Java source files. This is used for JNI registration. |
| |
| * `deps_info['proguard_all_configs']`: |
| The collection of all 'deps_info['proguard_configs']` values from this target |
| and all its dependencies. |
| |
| * `deps_info['proguard_classpath_jars']`: |
| The collection of all 'deps_info['extra_classpath_jars']` values from all |
| dependencies. |
| |
| * `deps_info['proguard_under_test_mapping']`: |
| Applicable to apks with proguard enabled that have an apk_under_test. This is |
| the path to the apk_under_test's output proguard .mapping file. |
| |
| ## <a name="target_android_app_bundle_module">Target type \ |
| `android_app_bundle_module`</a>: |
| |
| Corresponds to an Android app bundle module. Very similar to an APK and |
| inherits the same fields, except that this does not generate an installable |
| file (see `android_app_bundle`), and for the following omitted fields: |
| |
| * `deps_info['apk_path']`, `deps_info['incremental_apk_path']` and |
| `deps_info['incremental_install_json_path']` are omitted. |
| |
| * top-level `dist_jar` is omitted as well. |
| |
| In addition to `android_apk` targets though come these new fields: |
| |
| * `deps_info['proto_resources_path']`: |
| The path of an zip archive containing the APK's resources compiled to the |
| protocol buffer format (instead of regular binary xml + resources.arsc). |
| |
| * `deps_info['module_rtxt_path']`: |
| The path of the R.txt file generated when compiling the resources for the bundle |
| module. |
| |
| * `deps_info['module_pathmap_path']`: |
| The path of the pathmap file generated when compiling the resources for the |
| bundle module, if resource path shortening is enabled. |
| |
| * `deps_info['base_whitelist_rtxt_path']`: |
| Optional path to an R.txt file used as a whitelist for base string resources. |
| This means that any string resource listed in this file *and* in |
| `deps_info['module_rtxt_path']` will end up in the base split APK of any |
| `android_app_bundle` target that uses this target as its base module. |
| |
| This ensures that such localized strings are available to all bundle installs, |
| even when language based splits are enabled (e.g. required for WebView strings |
| inside the Monochrome bundle). |
| |
| |
| ## <a name="target_android_app_bundle">Target type `android_app_bundle`</a> |
| |
| This target type corresponds to an Android app bundle, and is built from one |
| or more `android_app_bundle_module` targets listed as dependencies. |
| |
| |
| ## <a name="target_dist_aar">Target type `dist_aar`</a>: |
| |
| This type corresponds to a target used to generate an `.aar` archive for |
| distribution. The archive's content is determined by the target's dependencies. |
| |
| This always has the following entries: |
| |
| * `deps_info['supports_android']` (always True). |
| * `deps_info['requires_android']` (always True). |
| * `deps_info['proguard_configs']` (optional). |
| |
| |
| ## <a name="target_dist_jar">Target type `dist_jar`</a>: |
| |
| This type is similar to [`dist_aar`](#target_dist_aar) but is not |
| Android-specific, and used to create a `.jar` file that can be later |
| redistributed. |
| |
| This always has the following entries: |
| |
| * `deps_info['proguard_enabled']` (False by default). |
| * `deps_info['proguard_configs']` (optional). |
| * `deps_info['supports_android']` (True by default). |
| * `deps_info['requires_android']` (False by default). |
| |
| |
| |
| ## <a name="target_resource_rewriter">Target type `resource_rewriter`</a>: |
| |
| The ResourceRewriter Java class is in charge of rewriting resource IDs at |
| runtime, for the benefit of the System WebView feature. This is a special |
| target type for it. |
| |
| Its `.build_config` only keeps a list of dependencies in its |
| `deps_info['deps_configs']` key. |
| |
| ## <a name="dict_javac">The `deps_info['javac']` dictionary</a>: |
| |
| This dictionary appears in Java-related targets (e.g. `java_library`, |
| `android_apk` and others), and contains information related to the compilation |
| of Java sources, class files, and jars. |
| |
| * `javac['resource_packages']` |
| For `java_library` targets, this is the list of package names for all resource |
| dependencies for the current target. Order must match the one from |
| `javac['srcjars']`. For other target types, this key does not exist. |
| |
| * `javac['classpath']` |
| The classpath used to compile this target when annotation processors are |
| present. |
| |
| * `javac['interface_classpath']` |
| The classpath used to compile this target when annotation processors are |
| not present. These are also always used to known when a target needs to be |
| rebuilt. |
| |
| * `javac['processor_classpath']` |
| The classpath listing the jars used for annotation processors. I.e. sent as |
| `-processorpath` when invoking `javac`. |
| |
| * `javac['processor_classes']` |
| The list of annotation processor main classes. I.e. sent as `-processor' when |
| invoking `javac`. |
| |
| ## <a name="android_app_bundle">Target type `android_app_bundle`</a>: |
| |
| This type corresponds to an Android app bundle (`.aab` file). |
| |
| --------------- END_MARKDOWN --------------------------------------------------- |
| """ |
| |
| from __future__ import print_function |
| |
| import collections |
| import itertools |
| import json |
| import optparse |
| import os |
| import sys |
| import xml.dom.minidom |
| |
| from util import build_utils |
| |
| # Types that should never be used as a dependency of another build config. |
| _ROOT_TYPES = ('android_apk', 'java_binary', 'java_annotation_processor', |
| 'junit_binary', 'resource_rewriter', 'android_app_bundle') |
| # Types that should not allow code deps to pass through. |
| _RESOURCE_TYPES = ('android_assets', 'android_resources', 'system_java_library') |
| |
| |
| def _ExtractMarkdownDocumentation(input_text): |
| """Extract Markdown documentation from a list of input strings lines. |
| |
| This generates a list of strings extracted from |input_text|, by looking |
| for '-- BEGIN_MARKDOWN --' and '-- END_MARKDOWN --' line markers.""" |
| in_markdown = False |
| result = [] |
| for line in input_text.splitlines(): |
| if in_markdown: |
| if '-- END_MARKDOWN --' in line: |
| in_markdown = False |
| else: |
| result.append(line) |
| else: |
| if '-- BEGIN_MARKDOWN --' in line: |
| in_markdown = True |
| |
| return result |
| |
| class AndroidManifest(object): |
| def __init__(self, path): |
| self.path = path |
| dom = xml.dom.minidom.parse(path) |
| manifests = dom.getElementsByTagName('manifest') |
| assert len(manifests) == 1 |
| self.manifest = manifests[0] |
| |
| def GetInstrumentationElements(self): |
| instrumentation_els = self.manifest.getElementsByTagName('instrumentation') |
| if len(instrumentation_els) == 0: |
| return None |
| return instrumentation_els |
| |
| def CheckInstrumentationElements(self, expected_package): |
| instrs = self.GetInstrumentationElements() |
| if not instrs: |
| raise Exception('No <instrumentation> elements found in %s' % self.path) |
| for instr in instrs: |
| instrumented_package = instr.getAttributeNS( |
| 'http://schemas.android.com/apk/res/android', 'targetPackage') |
| if instrumented_package != expected_package: |
| raise Exception( |
| 'Wrong instrumented package. Expected %s, got %s' |
| % (expected_package, instrumented_package)) |
| |
| def GetPackageName(self): |
| return self.manifest.getAttribute('package') |
| |
| |
| dep_config_cache = {} |
| def GetDepConfig(path): |
| if not path in dep_config_cache: |
| with open(path) as jsonfile: |
| dep_config_cache[path] = json.load(jsonfile)['deps_info'] |
| return dep_config_cache[path] |
| |
| |
| def DepsOfType(wanted_type, configs): |
| return [c for c in configs if c['type'] == wanted_type] |
| |
| |
| def GetAllDepsConfigsInOrder(deps_config_paths): |
| def GetDeps(path): |
| return GetDepConfig(path)['deps_configs'] |
| return build_utils.GetSortedTransitiveDependencies(deps_config_paths, GetDeps) |
| |
| |
| class Deps(object): |
| def __init__(self, direct_deps_config_paths): |
| self.all_deps_config_paths = GetAllDepsConfigsInOrder( |
| direct_deps_config_paths) |
| self.direct_deps_configs = [ |
| GetDepConfig(p) for p in direct_deps_config_paths] |
| self.all_deps_configs = [ |
| GetDepConfig(p) for p in self.all_deps_config_paths] |
| self.direct_deps_config_paths = direct_deps_config_paths |
| |
| def All(self, wanted_type=None): |
| if type is None: |
| return self.all_deps_configs |
| return DepsOfType(wanted_type, self.all_deps_configs) |
| |
| def Direct(self, wanted_type=None): |
| if wanted_type is None: |
| return self.direct_deps_configs |
| return DepsOfType(wanted_type, self.direct_deps_configs) |
| |
| def AllConfigPaths(self): |
| return self.all_deps_config_paths |
| |
| def RemoveNonDirectDep(self, path): |
| if path in self.direct_deps_config_paths: |
| raise Exception('Cannot remove direct dep.') |
| self.all_deps_config_paths.remove(path) |
| self.all_deps_configs.remove(GetDepConfig(path)) |
| |
| def GradlePrebuiltJarPaths(self): |
| ret = [] |
| |
| def helper(cur): |
| for config in cur.Direct('java_library'): |
| if config['is_prebuilt'] or config['gradle_treat_as_prebuilt']: |
| if config['jar_path'] not in ret: |
| ret.append(config['jar_path']) |
| |
| helper(self) |
| return ret |
| |
| def GradleLibraryProjectDeps(self): |
| ret = [] |
| |
| def helper(cur): |
| for config in cur.Direct('java_library'): |
| if config['is_prebuilt']: |
| pass |
| elif config['gradle_treat_as_prebuilt']: |
| helper(Deps(config['deps_configs'])) |
| elif config not in ret: |
| ret.append(config) |
| |
| helper(self) |
| return ret |
| |
| |
| def _MergeAssets(all_assets): |
| """Merges all assets from the given deps. |
| |
| Returns: |
| A tuple of: (compressed, uncompressed, locale_paks) |
| |compressed| and |uncompressed| are lists of "srcPath:zipPath". srcPath is |
| the path of the asset to add, and zipPath is the location within the zip |
| (excluding assets/ prefix). |
| |locale_paks| is a set of all zipPaths that have been marked as |
| treat_as_locale_paks=true. |
| """ |
| compressed = {} |
| uncompressed = {} |
| locale_paks = set() |
| for asset_dep in all_assets: |
| entry = asset_dep['assets'] |
| disable_compression = entry.get('disable_compression') |
| treat_as_locale_paks = entry.get('treat_as_locale_paks') |
| dest_map = uncompressed if disable_compression else compressed |
| other_map = compressed if disable_compression else uncompressed |
| outputs = entry.get('outputs', []) |
| for src, dest in itertools.izip_longest(entry['sources'], outputs): |
| if not dest: |
| dest = os.path.basename(src) |
| # Merge so that each path shows up in only one of the lists, and that |
| # deps of the same target override previous ones. |
| other_map.pop(dest, 0) |
| dest_map[dest] = src |
| if treat_as_locale_paks: |
| locale_paks.add(dest) |
| |
| def create_list(asset_map): |
| ret = ['%s:%s' % (src, dest) for dest, src in asset_map.iteritems()] |
| # Sort to ensure deterministic ordering. |
| ret.sort() |
| return ret |
| |
| return create_list(compressed), create_list(uncompressed), locale_paks |
| |
| |
| def _ResolveGroups(configs): |
| """Returns a list of configs with all groups inlined.""" |
| ret = list(configs) |
| while True: |
| groups = DepsOfType('group', ret) |
| if not groups: |
| return ret |
| for config in groups: |
| index = ret.index(config) |
| expanded_configs = [GetDepConfig(p) for p in config['deps_configs']] |
| ret[index:index + 1] = expanded_configs |
| |
| |
| def _DepsFromPaths(dep_paths, target_type, filter_root_targets=True): |
| """Resolves all groups and trims dependency branches that we never want. |
| |
| E.g. When a resource or asset depends on an apk target, the intent is to |
| include the .apk as a resource/asset, not to have the apk's classpath added. |
| """ |
| configs = [GetDepConfig(p) for p in dep_paths] |
| groups = DepsOfType('group', configs) |
| configs = _ResolveGroups(configs) |
| configs += groups |
| # Don't allow root targets to be considered as a dep. |
| if filter_root_targets: |
| configs = [c for c in configs if c['type'] not in _ROOT_TYPES] |
| |
| # Don't allow java libraries to cross through assets/resources. |
| if target_type in _RESOURCE_TYPES: |
| configs = [c for c in configs if c['type'] in _RESOURCE_TYPES] |
| |
| return Deps([c['path'] for c in configs]) |
| |
| |
| def _ExtractSharedLibsFromRuntimeDeps(runtime_deps_file): |
| ret = [] |
| with open(runtime_deps_file) as f: |
| for line in f: |
| line = line.rstrip() |
| if not line.endswith('.so'): |
| continue |
| # Only unstripped .so files are listed in runtime deps. |
| # Convert to the stripped .so by going up one directory. |
| ret.append(os.path.normpath(line.replace('lib.unstripped/', ''))) |
| ret.reverse() |
| return ret |
| |
| |
| def _CreateJavaLibrariesList(library_paths): |
| """Returns a java literal array with the "base" library names: |
| e.g. libfoo.so -> foo |
| """ |
| return ('{%s}' % ','.join(['"%s"' % s[3:-3] for s in library_paths])) |
| |
| |
| def _CreateJavaLocaleListFromAssets(assets, locale_paks): |
| """Returns a java literal array from a list of locale assets. |
| |
| Args: |
| assets: A list of all APK asset paths in the form 'src:dst' |
| locale_paks: A list of asset paths that correponds to the locale pak |
| files of interest. Each |assets| entry will have its 'dst' part matched |
| against it to determine if they are part of the result. |
| Returns: |
| A string that is a Java source literal array listing the locale names |
| of the corresponding asset files, without directory or .pak suffix. |
| E.g. '{"en-GB", "en-US", "es-ES", "fr", ... }' |
| """ |
| assets_paths = [a.split(':')[1] for a in assets] |
| locales = [os.path.basename(a)[:-4] for a in assets_paths if a in locale_paks] |
| return '{%s}' % ','.join(['"%s"' % l for l in sorted(locales)]) |
| |
| |
| def main(argv): |
| parser = optparse.OptionParser() |
| build_utils.AddDepfileOption(parser) |
| parser.add_option('--build-config', help='Path to build_config output.') |
| parser.add_option( |
| '--type', |
| help='Type of this target (e.g. android_library).') |
| parser.add_option( |
| '--deps-configs', |
| help='GN-list of dependent build_config files.') |
| parser.add_option( |
| '--annotation-processor-configs', |
| help='GN-list of build_config files for annotation processors.') |
| parser.add_option( |
| '--classpath-deps-configs', |
| help='GN-list of build_config files for libraries to include as ' |
| 'build-time-only classpath.') |
| |
| # android_resources options |
| parser.add_option('--srcjar', help='Path to target\'s resources srcjar.') |
| parser.add_option('--resources-zip', help='Path to target\'s resources zip.') |
| parser.add_option('--r-text', help='Path to target\'s R.txt file.') |
| parser.add_option('--package-name', |
| help='Java package name for these resources.') |
| parser.add_option('--android-manifest', help='Path to android manifest.') |
| parser.add_option('--resource-dirs', action='append', default=[], |
| help='GYP-list of resource dirs') |
| |
| # android_assets options |
| parser.add_option('--asset-sources', help='List of asset sources.') |
| parser.add_option('--asset-renaming-sources', |
| help='List of asset sources with custom destinations.') |
| parser.add_option('--asset-renaming-destinations', |
| help='List of asset custom destinations.') |
| parser.add_option('--disable-asset-compression', action='store_true', |
| help='Whether to disable asset compression.') |
| parser.add_option('--treat-as-locale-paks', action='store_true', |
| help='Consider the assets as locale paks in BuildConfig.java') |
| |
| # java library options |
| parser.add_option('--jar-path', help='Path to target\'s jar output.') |
| parser.add_option('--unprocessed-jar-path', |
| help='Path to the .jar to use for javac classpath purposes.') |
| parser.add_option('--interface-jar-path', |
| help='Path to the .interface.jar to use for javac classpath purposes.') |
| parser.add_option('--is-prebuilt', action='store_true', |
| help='Whether the jar was compiled or pre-compiled.') |
| parser.add_option('--java-sources-file', help='Path to .sources file') |
| parser.add_option('--bundled-srcjars', |
| help='GYP-list of .srcjars that have been included in this java_library.') |
| parser.add_option('--supports-android', action='store_true', |
| help='Whether this library supports running on the Android platform.') |
| parser.add_option('--requires-android', action='store_true', |
| help='Whether this library requires running on the Android platform.') |
| parser.add_option('--bypass-platform-checks', action='store_true', |
| help='Bypass checks for support/require Android platform.') |
| parser.add_option('--extra-classpath-jars', |
| help='GYP-list of .jar files to include on the classpath when compiling, ' |
| 'but not to include in the final binary.') |
| parser.add_option('--gradle-treat-as-prebuilt', action='store_true', |
| help='Whether this library should be treated as a prebuilt library by ' |
| 'generate_gradle.py.') |
| parser.add_option('--main-class', |
| help='Main class for java_binary or java_annotation_processor targets.') |
| parser.add_option('--java-resources-jar-path', |
| help='Path to JAR that contains java resources. Everything ' |
| 'from this JAR except meta-inf/ content and .class files ' |
| 'will be added to the final APK.') |
| |
| # android library options |
| parser.add_option('--dex-path', help='Path to target\'s dex output.') |
| |
| # native library options |
| parser.add_option('--shared-libraries-runtime-deps', |
| help='Path to file containing runtime deps for shared ' |
| 'libraries.') |
| parser.add_option('--native-libs', |
| action='append', |
| help='GN-list of native libraries for primary ' |
| 'android-abi. Can be specified multiple times.', |
| default=[]) |
| parser.add_option('--secondary-abi-shared-libraries-runtime-deps', |
| help='Path to file containing runtime deps for secondary ' |
| 'abi shared libraries.') |
| parser.add_option('--secondary-native-libs', |
| action='append', |
| help='GN-list of native libraries for secondary ' |
| 'android-abi. Can be specified multiple times.', |
| default=[]) |
| parser.add_option( |
| '--native-lib-placeholders', |
| action='append', |
| help='GN-list of native library placeholders to add.', |
| default=[]) |
| parser.add_option( |
| '--secondary-native-lib-placeholders', |
| action='append', |
| help='GN-list of native library placeholders to add ' |
| 'for the secondary android-abi.', |
| default=[]) |
| parser.add_option('--uncompress-shared-libraries', default=False, |
| action='store_true', |
| help='Whether to store native libraries uncompressed') |
| # apk options |
| parser.add_option('--apk-path', help='Path to the target\'s apk output.') |
| parser.add_option('--incremental-apk-path', |
| help="Path to the target's incremental apk output.") |
| parser.add_option('--incremental-install-json-path', |
| help="Path to the target's generated incremental install " |
| "json.") |
| |
| # apk options that are static library specific |
| parser.add_option( |
| '--static-library-dependent-configs', |
| help='GN list of .build_configs of targets that use this target as a ' |
| 'static library.') |
| parser.add_option( |
| '--static-library-jar-path', |
| help='Equivalent to what normally would be the jar_path for the APK.') |
| parser.add_option( |
| '--resource-ids-provider', |
| help='Path to the .build_config for the APK that this static library ' |
| 'target uses to generate stable resource IDs.') |
| parser.add_option( |
| '--compressed-locales-provider', |
| help='Path to the .build_config that contains the compressed locales ' |
| 'Java list for this static library target.') |
| parser.add_option( |
| '--uncompressed-locales-provider', |
| help='Path to the .build_config that contains the uncompressed locales ' |
| 'Java list for this static library target.') |
| |
| parser.add_option('--tested-apk-config', |
| help='Path to the build config of the tested apk (for an instrumentation ' |
| 'test apk).') |
| parser.add_option('--proguard-enabled', action='store_true', |
| help='Whether proguard is enabled for this apk or bundle module.') |
| parser.add_option('--proguard-configs', |
| help='GN-list of proguard flag files to use in final apk.') |
| parser.add_option('--proguard-mapping-path', |
| help='Path to jar created by ProGuard step') |
| parser.add_option('--fail', |
| help='GN-list of error message lines to fail with.') |
| |
| parser.add_option('--final-dex-path', |
| help='Path to final input classes.dex (or classes.zip) to ' |
| 'use in final apk.') |
| parser.add_option('--res-size-info', help='Path to .ap_.info') |
| parser.add_option('--apk-proto-resources', |
| help='Path to resources compiled in protocol buffer format ' |
| ' for this apk.') |
| parser.add_option( |
| '--module-pathmap-path', |
| help='Path to pathmap file for resource paths in a bundle module.') |
| parser.add_option( |
| '--module-rtxt-path', |
| help='Path to R.txt file for resources in a bundle module.') |
| parser.add_option( |
| '--base-whitelist-rtxt-path', |
| help='Path to R.txt file for the base resources whitelist.') |
| parser.add_option( |
| '--is-base-module', |
| help='Specifies that this module is a base module for some app bundle.') |
| |
| parser.add_option('--generate-markdown-format-doc', action='store_true', |
| help='Dump the Markdown .build_config format documentation ' |
| 'then exit immediately.') |
| |
| parser.add_option( |
| '--base-module-build-config', |
| help='Path to the base module\'s build config ' |
| 'if this is a feature module.') |
| |
| parser.add_option( |
| '--module-build-configs', |
| help='For bundles, the paths of all non-async module .build_configs ' |
| 'for modules that are part of the bundle.') |
| |
| options, args = parser.parse_args(argv) |
| |
| if args: |
| parser.error('No positional arguments should be given.') |
| |
| if options.generate_markdown_format_doc: |
| doc_lines = _ExtractMarkdownDocumentation(__doc__) |
| for line in doc_lines: |
| print(line) |
| return 0 |
| |
| if options.fail: |
| parser.error('\n'.join(build_utils.ParseGnList(options.fail))) |
| |
| jar_path_options = ['jar_path', 'unprocessed_jar_path', 'interface_jar_path'] |
| required_options_map = { |
| 'android_apk': ['build_config', 'dex_path', 'final_dex_path'] + \ |
| jar_path_options, |
| 'android_app_bundle_module': ['build_config', 'dex_path', |
| 'final_dex_path', 'res_size_info'] + jar_path_options, |
| 'android_assets': ['build_config'], |
| 'android_resources': ['build_config', 'resources_zip'], |
| 'dist_aar': ['build_config'], |
| 'dist_jar': ['build_config'], |
| 'group': ['build_config'], |
| 'java_annotation_processor': ['build_config', 'main_class'], |
| 'java_binary': ['build_config'], |
| 'java_library': ['build_config'] + jar_path_options, |
| 'junit_binary': ['build_config'], |
| 'resource_rewriter': ['build_config'], |
| 'system_java_library': ['build_config'], |
| 'android_app_bundle': ['build_config', 'module_build_configs'], |
| } |
| required_options = required_options_map.get(options.type) |
| if not required_options: |
| raise Exception('Unknown type: <%s>' % options.type) |
| |
| build_utils.CheckOptions(options, parser, required_options) |
| |
| if options.type != 'android_app_bundle_module': |
| if options.apk_proto_resources: |
| raise Exception('--apk-proto-resources can only be used with ' |
| '--type=android_app_bundle_module') |
| if options.module_rtxt_path: |
| raise Exception('--module-rxt-path can only be used with ' |
| '--type=android_app_bundle_module') |
| if options.module_pathmap_path: |
| raise Exception('--module-pathmap-path can only be used with ' |
| '--type=android_app_bundle_module') |
| if options.base_whitelist_rtxt_path: |
| raise Exception('--base-whitelist-rtxt-path can only be used with ' |
| '--type=android_app_bundle_module') |
| if options.is_base_module: |
| raise Exception('--is-base-module can only be used with ' |
| '--type=android_app_bundle_module') |
| |
| is_apk_or_module_target = options.type in ('android_apk', |
| 'android_app_bundle_module') |
| |
| if options.uncompress_shared_libraries: |
| if not is_apk_or_module_target: |
| raise Exception('--uncompressed-shared-libraries can only be used ' |
| 'with --type=android_apk or ' |
| '--type=android_app_bundle_module') |
| |
| if options.jar_path and options.supports_android and not options.dex_path: |
| raise Exception('java_library that supports Android requires a dex path.') |
| if any(getattr(options, x) for x in jar_path_options): |
| for attr in jar_path_options: |
| if not getattr(options, attr): |
| raise('Expected %s to be set.' % attr) |
| |
| if options.requires_android and not options.supports_android: |
| raise Exception( |
| '--supports-android is required when using --requires-android') |
| |
| is_java_target = options.type in ( |
| 'java_binary', 'junit_binary', 'java_annotation_processor', |
| 'java_library', 'android_apk', 'dist_aar', 'dist_jar', |
| 'system_java_library', 'android_app_bundle_module') |
| |
| is_static_library_dex_provider_target = ( |
| options.static_library_dependent_configs and options.proguard_enabled) |
| if is_static_library_dex_provider_target: |
| if options.type != 'android_apk': |
| raise Exception( |
| '--static-library-dependent-configs only supports --type=android_apk') |
| if not options.static_library_jar_path: |
| raise Exception('Can\'t have --static-library-dependent-configs without ' |
| '--static-library-jar-path') |
| options.static_library_dependent_configs = build_utils.ParseGnList( |
| options.static_library_dependent_configs) |
| static_library_dependent_configs_by_path = { |
| p: GetDepConfig(p) |
| for p in options.static_library_dependent_configs |
| } |
| |
| deps = _DepsFromPaths( |
| build_utils.ParseGnList(options.deps_configs), options.type) |
| processor_deps = _DepsFromPaths( |
| build_utils.ParseGnList(options.annotation_processor_configs or ''), |
| options.type, filter_root_targets=False) |
| classpath_deps = _DepsFromPaths( |
| build_utils.ParseGnList(options.classpath_deps_configs or ''), |
| options.type) |
| |
| all_inputs = sorted( |
| set(deps.AllConfigPaths() + processor_deps.AllConfigPaths() + |
| classpath_deps.AllConfigPaths() + |
| list(static_library_dependent_configs_by_path))) |
| |
| system_library_deps = deps.Direct('system_java_library') |
| direct_library_deps = deps.Direct('java_library') |
| direct_group_deps = deps.Direct('group') |
| all_group_deps = deps.All('group') |
| all_library_deps = deps.All('java_library') |
| all_resources_deps = deps.All('android_resources') |
| all_classpath_library_deps = classpath_deps.All('java_library') |
| |
| base_module_build_config = None |
| if options.base_module_build_config: |
| with open(options.base_module_build_config, 'r') as f: |
| base_module_build_config = json.load(f) |
| |
| # Initialize some common config. |
| # Any value that needs to be queryable by dependents must go within deps_info. |
| config = { |
| 'deps_info': { |
| 'name': os.path.basename(options.build_config), |
| 'path': options.build_config, |
| 'type': options.type, |
| 'deps_configs': deps.direct_deps_config_paths |
| }, |
| # Info needed only by generate_gradle.py. |
| 'gradle': {} |
| } |
| deps_info = config['deps_info'] |
| gradle = config['gradle'] |
| |
| if options.type == 'android_apk' and options.tested_apk_config: |
| tested_apk_deps = Deps([options.tested_apk_config]) |
| tested_apk_config = tested_apk_deps.Direct()[0] |
| tested_apk_resources_deps = tested_apk_deps.All('android_resources') |
| gradle['apk_under_test'] = tested_apk_config['name'] |
| all_resources_deps = [ |
| d for d in all_resources_deps if not d in tested_apk_resources_deps] |
| |
| if options.type == 'android_app_bundle_module': |
| deps_info['is_base_module'] = bool(options.is_base_module) |
| |
| # Required for generating gradle files. |
| if options.type == 'java_library': |
| deps_info['is_prebuilt'] = bool(options.is_prebuilt) |
| deps_info['gradle_treat_as_prebuilt'] = options.gradle_treat_as_prebuilt |
| |
| if options.android_manifest: |
| deps_info['android_manifest'] = options.android_manifest |
| |
| if is_java_target: |
| if options.java_sources_file: |
| deps_info['java_sources_file'] = options.java_sources_file |
| if options.bundled_srcjars: |
| gradle['bundled_srcjars'] = ( |
| build_utils.ParseGnList(options.bundled_srcjars)) |
| |
| gradle['dependent_android_projects'] = [] |
| gradle['dependent_java_projects'] = [] |
| gradle['dependent_prebuilt_jars'] = deps.GradlePrebuiltJarPaths() |
| |
| if options.main_class: |
| deps_info['main_class'] = options.main_class |
| |
| for c in deps.GradleLibraryProjectDeps(): |
| if c['requires_android']: |
| gradle['dependent_android_projects'].append(c['path']) |
| else: |
| gradle['dependent_java_projects'].append(c['path']) |
| |
| # TODO(tiborg): Remove creation of JNI info for type group and java_library |
| # once we can generate the JNI registration based on APK / module targets as |
| # opposed to groups and libraries. |
| if is_apk_or_module_target or options.type in ( |
| 'group', 'java_library', 'junit_binary'): |
| deps_info['jni'] = {} |
| all_java_sources = [c['java_sources_file'] for c in all_library_deps |
| if 'java_sources_file' in c] |
| if options.java_sources_file: |
| all_java_sources.append(options.java_sources_file) |
| |
| if options.apk_proto_resources: |
| deps_info['proto_resources_path'] = options.apk_proto_resources |
| |
| if options.module_rtxt_path: |
| deps_info['module_rtxt_path'] = options.module_rtxt_path |
| |
| if options.module_pathmap_path: |
| deps_info['module_pathmap_path'] = options.module_pathmap_path |
| else: |
| # Ensure there is an entry, even if it is empty, for modules |
| # that have not enabled resource path shortening. Otherwise |
| # build_utils.ExpandFileArgs fails. |
| deps_info['module_pathmap_path'] = '' |
| |
| if options.base_whitelist_rtxt_path: |
| deps_info['base_whitelist_rtxt_path'] = options.base_whitelist_rtxt_path |
| else: |
| # Ensure there is an entry, even if it is empty, for modules |
| # that don't need such a whitelist. |
| deps_info['base_whitelist_rtxt_path'] = '' |
| |
| if is_java_target: |
| deps_info['requires_android'] = bool(options.requires_android) |
| deps_info['supports_android'] = bool(options.supports_android) |
| |
| if not options.bypass_platform_checks: |
| deps_require_android = (all_resources_deps + |
| [d['name'] for d in all_library_deps if d['requires_android']]) |
| deps_not_support_android = ( |
| [d['name'] for d in all_library_deps if not d['supports_android']]) |
| |
| if deps_require_android and not options.requires_android: |
| raise Exception('Some deps require building for the Android platform: ' |
| + str(deps_require_android)) |
| |
| if deps_not_support_android and options.supports_android: |
| raise Exception('Not all deps support the Android platform: ' |
| + str(deps_not_support_android)) |
| |
| if is_java_target: |
| # Classpath values filled in below (after applying tested_apk_config). |
| config['javac'] = {} |
| if options.jar_path: |
| deps_info['jar_path'] = options.jar_path |
| deps_info['unprocessed_jar_path'] = options.unprocessed_jar_path |
| deps_info['interface_jar_path'] = options.interface_jar_path |
| if options.dex_path: |
| deps_info['dex_path'] = options.dex_path |
| if options.type == 'android_apk': |
| deps_info['apk_path'] = options.apk_path |
| deps_info['incremental_apk_path'] = options.incremental_apk_path |
| deps_info['incremental_install_json_path'] = ( |
| options.incremental_install_json_path) |
| |
| if options.type == 'android_assets': |
| all_asset_sources = [] |
| if options.asset_renaming_sources: |
| all_asset_sources.extend( |
| build_utils.ParseGnList(options.asset_renaming_sources)) |
| if options.asset_sources: |
| all_asset_sources.extend(build_utils.ParseGnList(options.asset_sources)) |
| |
| deps_info['assets'] = { |
| 'sources': all_asset_sources |
| } |
| if options.asset_renaming_destinations: |
| deps_info['assets']['outputs'] = ( |
| build_utils.ParseGnList(options.asset_renaming_destinations)) |
| if options.disable_asset_compression: |
| deps_info['assets']['disable_compression'] = True |
| if options.treat_as_locale_paks: |
| deps_info['assets']['treat_as_locale_paks'] = True |
| |
| if options.type == 'android_resources': |
| deps_info['resources_zip'] = options.resources_zip |
| if options.srcjar: |
| deps_info['srcjar'] = options.srcjar |
| if options.android_manifest: |
| manifest = AndroidManifest(options.android_manifest) |
| deps_info['package_name'] = manifest.GetPackageName() |
| if options.package_name: |
| deps_info['package_name'] = options.package_name |
| if options.r_text: |
| deps_info['r_text'] = options.r_text |
| |
| deps_info['resources_dirs'] = [] |
| if options.resource_dirs: |
| for gyp_list in options.resource_dirs: |
| deps_info['resources_dirs'].extend(build_utils.ParseGnList(gyp_list)) |
| |
| if options.requires_android and is_java_target: |
| # Lint all resources that are not already linted by a dependent library. |
| owned_resource_dirs = set() |
| owned_resource_zips = set() |
| owned_resource_srcjars = set() |
| for c in all_resources_deps: |
| # Always use resources_dirs in favour of resources_zips so that lint error |
| # messages have paths that are closer to reality (and to avoid needing to |
| # extract during lint). |
| if c['resources_dirs']: |
| owned_resource_dirs.update(c['resources_dirs']) |
| else: |
| owned_resource_zips.add(c['resources_zip']) |
| srcjar = c.get('srcjar') |
| if srcjar: |
| owned_resource_srcjars.add(srcjar) |
| |
| for c in itertools.chain(all_library_deps, all_classpath_library_deps): |
| if c['requires_android']: |
| owned_resource_dirs.difference_update(c['owned_resources_dirs']) |
| owned_resource_zips.difference_update(c['owned_resources_zips']) |
| # Many .aar files include R.class files in them, as it makes it easier |
| # for IDEs to resolve symbols. However, including them is not required |
| # and not all prebuilts do. Rather than try to detect their presense, |
| # just assume they are not there. The only consequence is redundant |
| # compilation of the R.class. |
| if not c['is_prebuilt']: |
| owned_resource_srcjars.difference_update(c['owned_resource_srcjars']) |
| deps_info['owned_resources_dirs'] = sorted(owned_resource_dirs) |
| deps_info['owned_resources_zips'] = sorted(owned_resource_zips) |
| deps_info['owned_resource_srcjars'] = sorted(owned_resource_srcjars) |
| |
| if options.type == 'java_library': |
| # Used to strip out R.class for android_prebuilt()s. |
| config['javac']['resource_packages'] = [ |
| c['package_name'] for c in all_resources_deps if 'package_name' in c] |
| |
| if options.type in ( |
| 'android_resources', 'android_apk', 'junit_binary', 'resource_rewriter', |
| 'dist_aar', 'android_app_bundle_module'): |
| config['resources'] = {} |
| |
| dependency_zips = [ |
| c['resources_zip'] for c in all_resources_deps if c['resources_zip'] |
| ] |
| extra_package_names = [] |
| extra_r_text_files = [] |
| |
| if options.type != 'android_resources': |
| extra_package_names = [ |
| c['package_name'] for c in all_resources_deps if 'package_name' in c] |
| extra_r_text_files = [ |
| c['r_text'] for c in all_resources_deps if 'r_text' in c] |
| |
| # For feature modules, remove any resources that already exist in the base |
| # module. |
| if base_module_build_config: |
| dependency_zips = [ |
| c for c in dependency_zips |
| if c not in base_module_build_config['resources']['dependency_zips'] |
| ] |
| extra_package_names = [ |
| c for c in extra_package_names if c not in |
| base_module_build_config['resources']['extra_package_names'] |
| ] |
| extra_r_text_files = [ |
| c for c in extra_r_text_files if c not in |
| base_module_build_config['resources']['extra_r_text_files'] |
| ] |
| |
| config['resources']['dependency_zips'] = dependency_zips |
| config['resources']['extra_package_names'] = extra_package_names |
| config['resources']['extra_r_text_files'] = extra_r_text_files |
| if options.type == 'android_apk' and options.tested_apk_config: |
| config['resources']['arsc_package_name'] = ( |
| tested_apk_config['package_name']) |
| if options.res_size_info: |
| config['resources']['size_info'] = options.res_size_info |
| |
| if is_apk_or_module_target: |
| deps_dex_files = [c['dex_path'] for c in all_library_deps] |
| |
| if options.type == 'group': |
| if options.extra_classpath_jars: |
| # These are .jars to add to javac classpath but not to runtime classpath. |
| extra_jars = build_utils.ParseGnList(options.extra_classpath_jars) |
| deps_info['extra_classpath_jars'] = extra_jars |
| deps_info['extra_classpath_interface_jars'] = extra_jars |
| |
| if is_java_target: |
| # The classpath used to compile this target when annotation processors are |
| # present. |
| javac_classpath = [ |
| c['unprocessed_jar_path'] for c in direct_library_deps] |
| # The classpath used to compile this target when annotation processors are |
| # not present. These are also always used to know when a target needs to be |
| # rebuilt. |
| javac_interface_classpath = [ |
| c['interface_jar_path'] for c in direct_library_deps] |
| # The classpath used for error prone. |
| javac_full_interface_classpath = [ |
| c['interface_jar_path'] for c in all_library_deps] |
| # The classpath used for bytecode-rewritting. |
| javac_full_classpath = [ |
| c['unprocessed_jar_path'] for c in all_library_deps] |
| |
| for dep in direct_group_deps: |
| javac_classpath.extend(dep.get('extra_classpath_jars', [])) |
| javac_interface_classpath.extend( |
| dep.get('extra_classpath_interface_jars', [])) |
| for dep in all_group_deps: |
| javac_full_classpath.extend(dep.get('extra_classpath_jars', [])) |
| javac_full_interface_classpath.extend( |
| dep.get('extra_classpath_interface_jars', [])) |
| |
| # Deps to add to the compile-time classpath (but not the runtime classpath). |
| # TODO(agrieve): Might be less confusing to fold these into bootclasspath. |
| javac_extra_jars = [ |
| c['unprocessed_jar_path'] for c in classpath_deps.All('java_library') |
| ] |
| extra_jars = [c['jar_path'] for c in classpath_deps.All('java_library')] |
| interface_extra_jars = [ |
| c['interface_jar_path'] for c in classpath_deps.All('java_library') |
| ] |
| |
| # These are jars specified by input_jars_paths that almost never change. |
| # Just add them directly to all the *extra_jars. |
| if options.extra_classpath_jars: |
| # These are .jars to add to javac classpath but not to runtime classpath. |
| javac_extra_jars.extend( |
| build_utils.ParseGnList(options.extra_classpath_jars)) |
| extra_jars.extend(build_utils.ParseGnList(options.extra_classpath_jars)) |
| interface_extra_jars.extend( |
| build_utils.ParseGnList(options.extra_classpath_jars)) |
| |
| if extra_jars: |
| deps_info['extra_classpath_jars'] = extra_jars |
| |
| if interface_extra_jars: |
| deps_info['extra_classpath_interface_jars'] = interface_extra_jars |
| |
| javac_extra_jars = [p for p in javac_extra_jars if p not in javac_classpath] |
| javac_classpath.extend(javac_extra_jars) |
| javac_full_classpath.extend( |
| p for p in javac_extra_jars if p not in javac_full_classpath) |
| |
| interface_extra_jars = [ |
| p for p in interface_extra_jars if p not in javac_interface_classpath |
| ] |
| javac_interface_classpath.extend(interface_extra_jars) |
| javac_full_interface_classpath.extend( |
| p for p in interface_extra_jars |
| if p not in javac_full_interface_classpath) |
| |
| if is_java_target or options.type == 'android_app_bundle': |
| # The classpath to use to run this target (or as an input to ProGuard). |
| java_full_classpath = [] |
| if is_java_target and options.jar_path: |
| java_full_classpath.append(options.jar_path) |
| java_full_classpath.extend(c['jar_path'] for c in all_library_deps) |
| if options.type == 'android_app_bundle': |
| for d in deps.Direct('android_app_bundle_module'): |
| java_full_classpath.extend( |
| c for c in d.get('java_runtime_classpath', []) |
| if c not in java_full_classpath) |
| |
| all_configs = build_utils.ParseGnList(options.proguard_configs) |
| deps_info['proguard_configs'] = list(all_configs) |
| extra_proguard_classpath_jars = [] |
| |
| if options.type == 'android_app_bundle': |
| module_configs = [ |
| GetDepConfig(c) |
| for c in build_utils.ParseGnList(options.module_build_configs) |
| ] |
| base_module_configs = [c for c in module_configs if c['is_base_module']] |
| assert len(base_module_configs) == 1, 'Must have exactly 1 base module!' |
| deps_info['base_module_config'] = base_module_configs[0]['path'] |
| |
| if is_static_library_dex_provider_target: |
| # Map classpath entries to configs that include them in their classpath. |
| configs_by_classpath_entry = collections.defaultdict(list) |
| static_lib_jar_paths = {} |
| for config_path, dep_config in ( |
| static_library_dependent_configs_by_path.iteritems()): |
| # For bundles, only the jar path and jni sources of the base module |
| # are relevant for proguard. Should be updated when bundle feature |
| # modules support JNI. |
| base_config = dep_config |
| if dep_config['type'] == 'android_app_bundle': |
| base_config = GetDepConfig(dep_config['base_module_config']) |
| static_lib_jar_paths[config_path] = base_config['jar_path'] |
| all_configs.extend(dep_config['proguard_all_configs']) |
| extra_proguard_classpath_jars.extend( |
| dep_config['proguard_classpath_jars']) |
| all_java_sources.extend(base_config['jni']['all_source']) |
| for cp_entry in dep_config['java_runtime_classpath']: |
| # The APK Java targets for the static library dependent targets will |
| # have some of the same classes (R.java) due to shared resource |
| # dependencies. To avoid Proguard failures due to duplicate classes, we |
| # merge the APK jars into the static library's jar_path as a |
| # preprocessing build step. |
| if cp_entry != base_config['jar_path']: |
| configs_by_classpath_entry[cp_entry].append(config_path) |
| |
| for cp_entry in java_full_classpath: |
| configs_by_classpath_entry[cp_entry].append(options.build_config) |
| |
| # Map configs to classpath entries that should be included in their final |
| # dex. |
| classpath_entries_by_owning_config = collections.defaultdict(list) |
| for cp_entry, candidate_configs in configs_by_classpath_entry.iteritems(): |
| config_path = (candidate_configs[0] |
| if len(candidate_configs) == 1 else options.build_config) |
| classpath_entries_by_owning_config[config_path].append(cp_entry) |
| java_full_classpath.append(cp_entry) |
| |
| java_full_classpath = sorted(set(java_full_classpath)) |
| deps_info['static_library_dependent_classpath_configs'] = { |
| path: sorted(set(classpath)) |
| for path, classpath in classpath_entries_by_owning_config.iteritems() |
| } |
| |
| # resource_ids_provider's jar must go first to ensure the correct R.java |
| # IDs are used. |
| ordered_static_lib_jar_paths = [] |
| if options.resource_ids_provider: |
| assert (options.resource_ids_provider in options. |
| static_library_dependent_configs), ( |
| '--resource-ids-provider must be in ' |
| '--static-library-dependent-configs') |
| ordered_static_lib_jar_paths.append( |
| static_lib_jar_paths[options.resource_ids_provider]) |
| |
| ordered_static_lib_jar_paths.extend( |
| x for x in static_lib_jar_paths.itervalues() |
| if x not in ordered_static_lib_jar_paths) |
| ordered_static_lib_jar_paths.append(options.static_library_jar_path) |
| deps_info[ |
| 'static_library_dependent_apk_jars'] = ordered_static_lib_jar_paths |
| deps_info['static_library_proguard_mapping_output_paths'] = [ |
| d['proguard_mapping_path'] |
| for d in static_library_dependent_configs_by_path.itervalues() |
| ] |
| |
| if is_apk_or_module_target or options.type in ('group', 'java_library', |
| 'junit_binary'): |
| deps_info['jni']['all_source'] = sorted(set(all_java_sources)) |
| |
| system_jars = [c['jar_path'] for c in system_library_deps] |
| system_interface_jars = [c['interface_jar_path'] for c in system_library_deps] |
| if system_library_deps: |
| config['android'] = {} |
| config['android']['sdk_interface_jars'] = system_interface_jars |
| config['android']['sdk_jars'] = system_jars |
| |
| if options.type in ('android_apk', 'dist_aar', |
| 'dist_jar', 'android_app_bundle_module', 'android_app_bundle'): |
| for c in all_library_deps: |
| all_configs.extend(p for p in c.get('proguard_configs', [])) |
| extra_proguard_classpath_jars.extend( |
| p for p in c.get('extra_classpath_jars', [])) |
| for c in all_group_deps: |
| extra_proguard_classpath_jars.extend( |
| p for p in c.get('extra_classpath_jars', [])) |
| if options.type == 'android_app_bundle': |
| for c in deps.Direct('android_app_bundle_module'): |
| all_configs.extend(p for p in c.get('proguard_configs', [])) |
| if options.type == 'android_app_bundle': |
| for d in deps.Direct('android_app_bundle_module'): |
| extra_proguard_classpath_jars.extend( |
| c for c in d.get('proguard_classpath_jars', []) |
| if c not in extra_proguard_classpath_jars) |
| |
| if options.type == 'android_app_bundle': |
| deps_proguard_enabled = [] |
| deps_proguard_disabled = [] |
| for d in deps.Direct('android_app_bundle_module'): |
| if not d['java_runtime_classpath']: |
| # We don't care about modules that have no Java code for proguarding. |
| continue |
| if d['proguard_enabled']: |
| deps_proguard_enabled.append(d['name']) |
| else: |
| deps_proguard_disabled.append(d['name']) |
| if deps_proguard_enabled and deps_proguard_disabled: |
| raise Exception('Deps %s have proguard enabled while deps %s have ' |
| 'proguard disabled' % (deps_proguard_enabled, |
| deps_proguard_disabled)) |
| else: |
| deps_info['proguard_enabled'] = bool(options.proguard_enabled) |
| |
| if options.proguard_mapping_path: |
| deps_info['proguard_mapping_path'] = options.proguard_mapping_path |
| |
| # The java code for an instrumentation test apk is assembled differently for |
| # ProGuard vs. non-ProGuard. |
| # |
| # Without ProGuard: Each library's jar is dexed separately and then combined |
| # into a single classes.dex. A test apk will include all dex files not already |
| # present in the apk-under-test. At runtime all test code lives in the test |
| # apk, and the program code lives in the apk-under-test. |
| # |
| # With ProGuard: Each library's .jar file is fed into ProGuard, which outputs |
| # a single .jar, which is then dexed into a classes.dex. A test apk includes |
| # all jar files from the program and the tests because having them separate |
| # doesn't work with ProGuard's whole-program optimizations. Although the |
| # apk-under-test still has all of its code in its classes.dex, none of it is |
| # used at runtime because the copy of it within the test apk takes precidence. |
| |
| if options.type == 'android_apk' and options.tested_apk_config: |
| if tested_apk_config['proguard_enabled']: |
| assert options.proguard_enabled, ('proguard must be enabled for ' |
| 'instrumentation apks if it\'s enabled for the tested apk.') |
| # Mutating lists, so no need to explicitly re-assign to dict. |
| all_configs.extend(p for p in tested_apk_config['proguard_all_configs']) |
| extra_proguard_classpath_jars.extend( |
| p for p in tested_apk_config['proguard_classpath_jars']) |
| tested_apk_config = GetDepConfig(options.tested_apk_config) |
| deps_info['proguard_under_test_mapping'] = ( |
| tested_apk_config['proguard_mapping_path']) |
| elif options.proguard_enabled: |
| # Not sure why you'd want to proguard the test apk when the under-test apk |
| # is not proguarded, but it's easy enough to support. |
| deps_info['proguard_under_test_mapping'] = '' |
| |
| expected_tested_package = tested_apk_config['package_name'] |
| AndroidManifest(options.android_manifest).CheckInstrumentationElements( |
| expected_tested_package) |
| |
| # Add all tested classes to the test's classpath to ensure that the test's |
| # java code is a superset of the tested apk's java code |
| java_full_classpath.extend( |
| p for p in tested_apk_config['java_runtime_classpath'] |
| if p not in java_full_classpath) |
| # Include in the classpath classes that are added directly to the apk under |
| # test (those that are not a part of a java_library). |
| javac_classpath.append(tested_apk_config['unprocessed_jar_path']) |
| javac_full_classpath.append(tested_apk_config['unprocessed_jar_path']) |
| javac_interface_classpath.append(tested_apk_config['interface_jar_path']) |
| javac_full_interface_classpath.append( |
| tested_apk_config['interface_jar_path']) |
| javac_full_interface_classpath.extend( |
| p for p in tested_apk_config['javac_full_interface_classpath'] |
| if p not in javac_full_interface_classpath) |
| javac_full_classpath.extend( |
| p for p in tested_apk_config['javac_full_classpath'] |
| if p not in javac_full_classpath) |
| |
| # Exclude dex files from the test apk that exist within the apk under test. |
| # TODO(agrieve): When proguard is enabled, this filtering logic happens |
| # within proguard_util.py. Move the logic for the proguard case into |
| # here as well. |
| tested_apk_library_deps = tested_apk_deps.All('java_library') |
| tested_apk_deps_dex_files = [c['dex_path'] for c in tested_apk_library_deps] |
| deps_dex_files = [ |
| p for p in deps_dex_files if not p in tested_apk_deps_dex_files] |
| |
| if options.type in ('android_apk', 'dist_aar', 'dist_jar', |
| 'android_app_bundle_module', 'android_app_bundle'): |
| deps_info['proguard_all_configs'] = sorted(set(all_configs)) |
| deps_info['proguard_classpath_jars'] = sorted( |
| set(extra_proguard_classpath_jars)) |
| |
| # Dependencies for the final dex file of an apk. |
| if is_apk_or_module_target or options.final_dex_path: |
| config['final_dex'] = {} |
| dex_config = config['final_dex'] |
| dex_config['path'] = options.final_dex_path |
| if is_apk_or_module_target: |
| dex_config['dependency_dex_files'] = deps_dex_files |
| |
| if is_java_target: |
| config['javac']['classpath'] = javac_classpath |
| config['javac']['interface_classpath'] = javac_interface_classpath |
| # Direct() will be of type 'java_annotation_processor'. |
| config['javac']['processor_classpath'] = [ |
| c['jar_path'] for c in processor_deps.Direct() if c.get('jar_path')] + [ |
| c['jar_path'] for c in processor_deps.All('java_library')] |
| config['javac']['processor_classes'] = [ |
| c['main_class'] for c in processor_deps.Direct()] |
| deps_info['javac_full_classpath'] = javac_full_classpath |
| deps_info['javac_full_interface_classpath'] = javac_full_interface_classpath |
| elif options.type == 'android_app_bundle': |
| # bundles require javac_full_classpath to create .aab.jar.info. |
| javac_full_classpath = set() |
| for d in deps.Direct('android_app_bundle_module'): |
| javac_full_classpath.update(p for p in d['javac_full_classpath']) |
| javac_full_classpath.add(d['jar_path']) |
| deps_info['javac_full_classpath'] = sorted(javac_full_classpath) |
| |
| if options.type in ('android_apk', 'dist_jar', 'java_binary', 'junit_binary', |
| 'android_app_bundle_module', 'android_app_bundle'): |
| deps_info['java_runtime_classpath'] = java_full_classpath |
| |
| if options.type in ('android_apk', 'dist_jar'): |
| all_interface_jars = [] |
| if options.jar_path: |
| all_interface_jars.append(options.interface_jar_path) |
| all_interface_jars.extend(c['interface_jar_path'] for c in all_library_deps) |
| |
| config['dist_jar'] = { |
| 'all_interface_jars': all_interface_jars, |
| } |
| |
| if is_apk_or_module_target: |
| manifest = AndroidManifest(options.android_manifest) |
| deps_info['package_name'] = manifest.GetPackageName() |
| if not options.tested_apk_config and manifest.GetInstrumentationElements(): |
| # This must then have instrumentation only for itself. |
| manifest.CheckInstrumentationElements(manifest.GetPackageName()) |
| |
| library_paths = [] |
| java_libraries_list = None |
| if options.shared_libraries_runtime_deps: |
| library_paths = _ExtractSharedLibsFromRuntimeDeps( |
| options.shared_libraries_runtime_deps) |
| java_libraries_list = _CreateJavaLibrariesList(library_paths) |
| all_inputs.append(options.shared_libraries_runtime_deps) |
| |
| secondary_abi_library_paths = [] |
| if options.secondary_abi_shared_libraries_runtime_deps: |
| secondary_abi_library_paths = _ExtractSharedLibsFromRuntimeDeps( |
| options.secondary_abi_shared_libraries_runtime_deps) |
| all_inputs.append(options.secondary_abi_shared_libraries_runtime_deps) |
| |
| secondary_abi_library_paths.extend( |
| build_utils.ParseGnList(options.secondary_native_libs)) |
| |
| native_library_placeholder_paths = build_utils.ParseGnList( |
| options.native_lib_placeholders) |
| |
| secondary_native_library_placeholder_paths = build_utils.ParseGnList( |
| options.secondary_native_lib_placeholders) |
| |
| extra_shared_libraries = build_utils.ParseGnList(options.native_libs) |
| |
| config['native'] = { |
| 'libraries': |
| library_paths, |
| 'native_library_placeholders': |
| native_library_placeholder_paths, |
| 'secondary_abi_libraries': |
| secondary_abi_library_paths, |
| 'secondary_native_library_placeholders': |
| secondary_native_library_placeholder_paths, |
| 'java_libraries_list': |
| java_libraries_list, |
| 'uncompress_shared_libraries': |
| options.uncompress_shared_libraries, |
| 'extra_shared_libraries': |
| extra_shared_libraries, |
| } |
| config['assets'], config['uncompressed_assets'], locale_paks = ( |
| _MergeAssets(deps.All('android_assets'))) |
| |
| if options.compressed_locales_provider: |
| dep_config = GetDepConfig(options.compressed_locales_provider) |
| if dep_config['type'] == 'android_app_bundle': |
| dep_config = GetDepConfig(dep_config['base_module_config']) |
| deps_info['compressed_locales_java_list'] = dep_config[ |
| 'compressed_locales_java_list'] |
| else: |
| deps_info[ |
| 'compressed_locales_java_list'] = _CreateJavaLocaleListFromAssets( |
| config['assets'], locale_paks) |
| |
| if options.uncompressed_locales_provider: |
| dep_config = GetDepConfig(options.uncompressed_locales_provider) |
| if dep_config['type'] == 'android_app_bundle': |
| dep_config = GetDepConfig(dep_config['base_module_config']) |
| deps_info['uncompressed_locales_java_list'] = dep_config[ |
| 'uncompressed_locales_java_list'] |
| else: |
| deps_info[ |
| 'uncompressed_locales_java_list'] = _CreateJavaLocaleListFromAssets( |
| config['uncompressed_assets'], locale_paks) |
| |
| config['extra_android_manifests'] = filter(None, ( |
| d.get('android_manifest') for d in all_resources_deps)) |
| |
| # Collect java resources |
| java_resources_jars = [d['java_resources_jar'] for d in all_library_deps |
| if 'java_resources_jar' in d] |
| if options.tested_apk_config: |
| tested_apk_resource_jars = [d['java_resources_jar'] |
| for d in tested_apk_library_deps |
| if 'java_resources_jar' in d] |
| java_resources_jars = [jar for jar in java_resources_jars |
| if jar not in tested_apk_resource_jars] |
| config['java_resources_jars'] = java_resources_jars |
| |
| if options.java_resources_jar_path: |
| deps_info['java_resources_jar'] = options.java_resources_jar_path |
| |
| build_utils.WriteJson(config, options.build_config, only_if_changed=True) |
| |
| if options.depfile: |
| build_utils.WriteDepfile(options.depfile, options.build_config, all_inputs, |
| add_pydeps=False) # pydeps listed in GN. |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main(sys.argv[1:])) |