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