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