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