aboutsummaryrefslogtreecommitdiff
path: root/rules/android_application/android_feature_module_rule.bzl
diff options
context:
space:
mode:
Diffstat (limited to 'rules/android_application/android_feature_module_rule.bzl')
-rw-r--r--rules/android_application/android_feature_module_rule.bzl196
1 files changed, 196 insertions, 0 deletions
diff --git a/rules/android_application/android_feature_module_rule.bzl b/rules/android_application/android_feature_module_rule.bzl
new file mode 100644
index 0000000..87c57b5
--- /dev/null
+++ b/rules/android_application/android_feature_module_rule.bzl
@@ -0,0 +1,196 @@
+# Copyright 2021 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""android_feature_module rule."""
+
+load(":attrs.bzl", "ANDROID_FEATURE_MODULE_ATTRS")
+load("@rules_android//rules:java.bzl", _java = "java")
+load(
+ "@rules_android//rules:providers.bzl",
+ "AndroidFeatureModuleInfo",
+)
+load("@rules_android//rules:acls.bzl", "acls")
+load(
+ "@rules_android//rules:utils.bzl",
+ "get_android_toolchain",
+)
+
+def _impl(ctx):
+ validation = ctx.actions.declare_file(ctx.label.name + "_validation")
+ inputs = [ctx.attr.binary[ApkInfo].unsigned_apk]
+ args = ctx.actions.args()
+ args.add(validation.path)
+ if ctx.file.manifest:
+ args.add(ctx.file.manifest.path)
+ inputs.append(ctx.file.manifest)
+ else:
+ args.add("")
+ args.add(ctx.attr.binary[ApkInfo].unsigned_apk.path)
+ args.add(ctx.configuration.coverage_enabled)
+ args.add(ctx.fragments.android.desugar_java8_libs)
+ args.add(ctx.attr.library.label)
+ args.add(get_android_toolchain(ctx).xmllint_tool.files_to_run.executable)
+ args.add(get_android_toolchain(ctx).unzip_tool.files_to_run.executable)
+
+ ctx.actions.run(
+ executable = ctx.executable._feature_module_validation_script,
+ inputs = inputs,
+ outputs = [validation],
+ arguments = [args],
+ tools = [
+ get_android_toolchain(ctx).xmllint_tool.files_to_run.executable,
+ get_android_toolchain(ctx).unzip_tool.files_to_run.executable,
+ ],
+ mnemonic = "ValidateFeatureModule",
+ progress_message = "Validating feature module %s" % str(ctx.label),
+ )
+
+ return [
+ AndroidFeatureModuleInfo(
+ binary = ctx.attr.binary,
+ library = ctx.attr.library,
+ title_id = ctx.attr.title_id,
+ title_lib = ctx.attr.title_lib,
+ feature_name = ctx.attr.feature_name,
+ fused = ctx.attr.fused,
+ manifest = ctx.file.manifest,
+ ),
+ OutputGroupInfo(_validation = depset([validation])),
+ ]
+
+android_feature_module = rule(
+ attrs = ANDROID_FEATURE_MODULE_ATTRS,
+ fragments = [
+ "android",
+ "java",
+ ],
+ implementation = _impl,
+ provides = [AndroidFeatureModuleInfo],
+ toolchains = ["@rules_android//toolchains/android:toolchain_type"],
+ _skylark_testable = True,
+)
+
+def get_feature_module_paths(fqn):
+ # Given a fqn to an android_feature_module, returns the absolute paths to
+ # all implicitly generated targets
+ return struct(
+ binary = Label("%s_bin" % fqn),
+ manifest_lib = Label("%s_AndroidManifest" % fqn),
+ title_strings_xml = Label("%s_title_strings_xml" % fqn),
+ title_lib = Label("%s_title_lib" % fqn),
+ )
+
+def android_feature_module_macro(_android_binary, _android_library, **attrs):
+ """android_feature_module_macro.
+
+ Args:
+ _android_binary: The android_binary rule to use.
+ _android_library: The android_library rule to use.
+ **attrs: android_feature_module attributes.
+ """
+
+ # Enable dot syntax
+ attrs = struct(**attrs)
+ fqn = "//%s:%s" % (native.package_name(), attrs.name)
+
+ required_attrs = ["name", "library", "title"]
+ if not acls.in_android_feature_splits_dogfood(fqn):
+ required_attrs.append("manifest")
+
+ # Check for required macro attributes
+ for attr in required_attrs:
+ if not getattr(attrs, attr, None):
+ fail("%s missing required attr <%s>" % (fqn, attr))
+
+ if hasattr(attrs, "fused") and hasattr(attrs, "manifest"):
+ fail("%s cannot specify <fused> and <manifest>. Prefer <manifest>")
+
+ targets = get_feature_module_paths(fqn)
+
+ tags = getattr(attrs, "tags", [])
+ transitive_configs = getattr(attrs, "transitive_configs", [])
+ visibility = getattr(attrs, "visibility", None)
+
+ # Create strings.xml containing split title
+ title_id = "split_" + str(hash(fqn)).replace("-", "N")
+ native.genrule(
+ name = targets.title_strings_xml.name,
+ outs = [attrs.name + "/res/values/strings.xml"],
+ cmd = """cat > $@ <<EOF
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"
+xmlns:tools="http://schemas.android.com/tools"
+tools:keep="@string/{title_id}">
+ <string name="{title_id}">{title}</string>
+</resources>
+EOF
+""".format(title = attrs.title, title_id = title_id),
+ )
+
+ # Create AndroidManifest.xml
+ min_sdk_version = getattr(attrs, "min_sdk_version", "14") or "14"
+ package = _java.resolve_package_from_label(Label(fqn), getattr(attrs, "custom_package", None))
+ native.genrule(
+ name = targets.manifest_lib.name,
+ outs = [attrs.name + "/AndroidManifest.xml"],
+ cmd = """cat > $@ <<EOF
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="{package}">
+ <uses-sdk
+ android:minSdkVersion="{min_sdk_version}"/>
+</manifest>
+EOF
+""".format(package = package, min_sdk_version = min_sdk_version),
+ )
+
+ # Resource processing requires an android_library target
+ _android_library(
+ name = targets.title_lib.name,
+ custom_package = getattr(attrs, "custom_package", None),
+ manifest = str(targets.manifest_lib),
+ resource_files = [str(targets.title_strings_xml)],
+ tags = tags,
+ transitive_configs = transitive_configs,
+ visibility = visibility,
+ )
+
+ # Wrap any deps in an android_binary. Will be validated to ensure does not contain any dexes
+ binary_attrs = {
+ "name": targets.binary.name,
+ "custom_package": getattr(attrs, "custom_package", None),
+ "manifest": str(targets.manifest_lib),
+ "deps": [attrs.library],
+ "multidex": "native",
+ "tags": tags,
+ "transitive_configs": transitive_configs,
+ "visibility": visibility,
+ "feature_flags": getattr(attrs, "feature_flags", None),
+ "$enable_manifest_merging": False,
+ }
+ _android_binary(**binary_attrs)
+
+ android_feature_module(
+ name = attrs.name,
+ library = attrs.library,
+ binary = str(targets.binary),
+ title_id = title_id,
+ title_lib = str(targets.title_lib),
+ feature_name = getattr(attrs, "feature_name", attrs.name),
+ fused = getattr(attrs, "fused", True),
+ manifest = getattr(attrs, "manifest", None),
+ tags = tags,
+ transitive_configs = transitive_configs,
+ visibility = visibility,
+ )