• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2021 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#    http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""android_feature_module rule."""
16
17load(":attrs.bzl", "ANDROID_FEATURE_MODULE_ATTRS")
18load("//rules:java.bzl", _java = "java")
19load(
20    "//rules:providers.bzl",
21    "AndroidFeatureModuleInfo",
22)
23load("//rules:acls.bzl", "acls")
24load(
25    "//rules:utils.bzl",
26    "get_android_toolchain",
27    "utils",
28)
29
30def _impl(ctx):
31    validation = ctx.actions.declare_file(ctx.label.name + "_validation")
32    inputs = [ctx.attr.binary[ApkInfo].unsigned_apk]
33    args = ctx.actions.args()
34    args.add(validation.path)
35    if ctx.file.manifest:
36        args.add(ctx.file.manifest.path)
37        inputs.append(ctx.file.manifest)
38    else:
39        args.add("")
40    args.add(ctx.attr.binary[ApkInfo].unsigned_apk.path)
41    args.add(ctx.configuration.coverage_enabled)
42    args.add(ctx.fragments.android.desugar_java8_libs)
43    args.add(utils.dedupe_split_attr(ctx.split_attr.library).label)
44    args.add(get_android_toolchain(ctx).xmllint_tool.files_to_run.executable)
45    args.add(get_android_toolchain(ctx).unzip_tool.files_to_run.executable)
46
47    ctx.actions.run(
48        executable = ctx.executable._feature_module_validation_script,
49        inputs = inputs,
50        outputs = [validation],
51        arguments = [args],
52        tools = [
53            get_android_toolchain(ctx).xmllint_tool.files_to_run.executable,
54            get_android_toolchain(ctx).unzip_tool.files_to_run.executable,
55        ],
56        mnemonic = "ValidateFeatureModule",
57        progress_message = "Validating feature module %s" % str(ctx.label),
58    )
59
60    return [
61        AndroidFeatureModuleInfo(
62            binary = ctx.attr.binary,
63            library = utils.dedupe_split_attr(ctx.split_attr.library),
64            title_id = ctx.attr.title_id,
65            title_lib = ctx.attr.title_lib,
66            feature_name = ctx.attr.feature_name,
67            fused = ctx.attr.fused,
68            manifest = ctx.file.manifest,
69        ),
70        OutputGroupInfo(_validation = depset([validation])),
71    ]
72
73android_feature_module = rule(
74    attrs = ANDROID_FEATURE_MODULE_ATTRS,
75    fragments = [
76        "android",
77        "java",
78    ],
79    implementation = _impl,
80    provides = [AndroidFeatureModuleInfo],
81    toolchains = ["//toolchains/android:toolchain_type"],
82    _skylark_testable = True,
83)
84
85def get_feature_module_paths(fqn):
86    # Given a fqn to an android_feature_module, returns the absolute paths to
87    # all implicitly generated targets
88    return struct(
89        binary = Label("%s_bin" % fqn),
90        manifest_lib = Label("%s_AndroidManifest" % fqn),
91        title_strings_xml = Label("%s_title_strings_xml" % fqn),
92        title_lib = Label("%s_title_lib" % fqn),
93    )
94
95def android_feature_module_macro(_android_binary, _android_library, **attrs):
96    """android_feature_module_macro.
97
98    Args:
99      _android_binary: The android_binary rule to use.
100      _android_library: The android_library rule to use.
101      **attrs: android_feature_module attributes.
102    """
103
104    # Enable dot syntax
105    attrs = struct(**attrs)
106    fqn = "//%s:%s" % (native.package_name(), attrs.name)
107
108    required_attrs = ["name", "library", "title"]
109    if not acls.in_android_feature_splits_dogfood(fqn):
110        required_attrs.append("manifest")
111
112    # Check for required macro attributes
113    for attr in required_attrs:
114        if not getattr(attrs, attr, None):
115            fail("%s missing required attr <%s>" % (fqn, attr))
116
117    if hasattr(attrs, "fused") and hasattr(attrs, "manifest"):
118        fail("%s cannot specify <fused> and <manifest>. Prefer <manifest>")
119
120    targets = get_feature_module_paths(fqn)
121
122    tags = getattr(attrs, "tags", [])
123    transitive_configs = getattr(attrs, "transitive_configs", [])
124    visibility = getattr(attrs, "visibility", None)
125    testonly = getattr(attrs, "testonly", None)
126
127    # Create strings.xml containing split title
128    title_id = "split_" + str(hash(fqn)).replace("-", "N")
129    native.genrule(
130        name = targets.title_strings_xml.name,
131        outs = [attrs.name + "/res/values/strings.xml"],
132        cmd = """cat > $@ <<EOF
133<?xml version="1.0" encoding="utf-8"?>
134<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"
135xmlns:tools="http://schemas.android.com/tools"
136tools:keep="@string/{title_id}">
137    <string name="{title_id}">{title}</string>
138</resources>
139EOF
140""".format(title = attrs.title, title_id = title_id),
141    )
142
143    # Create AndroidManifest.xml
144    min_sdk_version = getattr(attrs, "min_sdk_version", "21") or "21"
145    package = _java.resolve_package_from_label(Label(fqn), getattr(attrs, "custom_package", None))
146    native.genrule(
147        name = targets.manifest_lib.name,
148        outs = [attrs.name + "/AndroidManifest.xml"],
149        cmd = """cat > $@ <<EOF
150<?xml version="1.0" encoding="utf-8"?>
151<manifest xmlns:android="http://schemas.android.com/apk/res/android"
152    package="{package}">
153    <uses-sdk
154      android:minSdkVersion="{min_sdk_version}"/>
155</manifest>
156EOF
157""".format(package = package, min_sdk_version = min_sdk_version),
158    )
159
160    # Resource processing requires an android_library target
161    _android_library(
162        name = targets.title_lib.name,
163        custom_package = getattr(attrs, "custom_package", None),
164        manifest = str(targets.manifest_lib),
165        resource_files = [str(targets.title_strings_xml)],
166        tags = tags,
167        transitive_configs = transitive_configs,
168        visibility = visibility,
169        testonly = testonly,
170    )
171
172    # Wrap any deps in an android_binary. Will be validated to ensure does not contain any dexes
173    binary_attrs = {
174        "name": targets.binary.name,
175        "custom_package": getattr(attrs, "custom_package", None),
176        "manifest": str(targets.manifest_lib),
177        "deps": [attrs.library],
178        "multidex": "native",
179        "tags": tags,
180        "transitive_configs": transitive_configs,
181        "visibility": visibility,
182        "feature_flags": getattr(attrs, "feature_flags", None),
183        "$enable_manifest_merging": False,
184        "testonly": testonly,
185    }
186    _android_binary(**binary_attrs)
187
188    android_feature_module(
189        name = attrs.name,
190        library = attrs.library,
191        binary = str(targets.binary),
192        title_id = title_id,
193        title_lib = str(targets.title_lib),
194        feature_name = getattr(attrs, "feature_name", attrs.name),
195        fused = getattr(attrs, "fused", True),
196        manifest = getattr(attrs, "manifest", None),
197        tags = tags,
198        transitive_configs = transitive_configs,
199        visibility = visibility,
200        testonly = testonly,
201    )
202