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