1# Copyright (C) 2022 The Android Open Source Project 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"""This file defines the rule that builds android partitions.""" 16 17load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") 18load("//build/bazel/rules:build_fingerprint.bzl", "BuildFingerprintInfo") 19 20_IMAGE_TYPES = [ 21 "system", 22 "system_other", 23 "userdata", 24 "cache", 25 "vendor", 26 "product", 27 "system_ext", 28 "odm", 29 "vendor_dlkm", 30 "system_dlkm", 31 "oem", 32] 33 34def _get_python3(ctx): 35 python_interpreter = ctx.toolchains["@bazel_tools//tools/python:toolchain_type"].py3_runtime.interpreter 36 if python_interpreter.basename == "python3": 37 return python_interpreter 38 39 renamed = ctx.actions.declare_file(ctx.attr.name + "/python3") 40 ctx.actions.symlink( 41 output = renamed, 42 target_file = python_interpreter, 43 is_executable = True, 44 ) 45 return renamed 46 47def _partition_impl(ctx): 48 if ctx.attr.type != "system": 49 fail("currently only system images are supported") 50 51 toolchain = ctx.toolchains[":partition_toolchain_type"].toolchain_info 52 python_interpreter = _get_python3(ctx) 53 54 du = ctx.actions.declare_file(ctx.attr.name + "/du") 55 ctx.actions.symlink( 56 output = du, 57 target_file = toolchain.toybox[DefaultInfo].files_to_run.executable, 58 is_executable = True, 59 ) 60 find = ctx.actions.declare_file(ctx.attr.name + "/find") 61 ctx.actions.symlink( 62 output = find, 63 target_file = toolchain.toybox[DefaultInfo].files_to_run.executable, 64 is_executable = True, 65 ) 66 67 # build_image requires that the output file be named specifically <type>.img, so 68 # put all the outputs under a name-qualified folder. 69 output_image = ctx.actions.declare_file(ctx.attr.name + "/" + ctx.attr.type + ".img") 70 71 # TODO(b/297269187) Fill this out with the contents of ctx.attr.deps 72 files = {} 73 74 staging_dir_builder_options = { 75 "file_mapping": {k: v.path for k, v in files.items()}, 76 } 77 78 extra_inputs = [] 79 if ctx.attr.base_staging_dir: 80 staging_dir_builder_options["base_staging_dir"] = ctx.file.base_staging_dir.path 81 extra_inputs.append(ctx.file.base_staging_dir) 82 bbipi = ctx.attr._build_broken_incorrect_partition_images[BuildSettingInfo].value 83 if ctx.attr.base_staging_dir_file_list and not bbipi: 84 staging_dir_builder_options["base_staging_dir_file_list"] = ctx.file.base_staging_dir_file_list.path 85 extra_inputs.append(ctx.file.base_staging_dir_file_list) 86 87 if "{BUILD_NUMBER}" in ctx.attr.image_properties: 88 fail("Can't have {BUILD_NUMBER} in image_properties") 89 for line in ctx.attr.image_properties.splitlines(): 90 if line.startswith("avb_"): 91 fail("avb properties should be managed by their bespoke attributes: " + line) 92 93 image_info_contents = ctx.attr.image_properties + "\n\n" 94 image_info_contents += "ext_mkuserimg=mkuserimg_mke2fs\n" 95 if ctx.attr.root_dir: 96 extra_inputs.append(ctx.file.root_dir) 97 image_info_contents += "root_dir=" + ctx.file.root_dir.path + "\n" 98 if ctx.attr.selinux_file_contexts: 99 extra_inputs.append(ctx.file.selinux_file_contexts) 100 image_info_contents += ctx.attr.type + "_selinux_fc=" + ctx.file.selinux_file_contexts.path + "\n" 101 102 if not ctx.attr.avb_enable: 103 if ctx.attr.avb_add_hashtree_footer_args: 104 fail("Must specify avb_enable = True to use avb_add_hashtree_footer_args") 105 if ctx.attr.avb_key: 106 fail("Must specify avb_enable = True to use avb_key") 107 if ctx.attr.avb_algorithm: 108 fail("Must specify avb_enable = True to use avb_key") 109 if ctx.attr.avb_rollback_index >= 0: 110 fail("Must specify avb_enable = True to use avb_rollback_index") 111 if ctx.attr.avb_rollback_index_location >= 0: 112 fail("Must specify avb_enable = True to use avb_rollback_index_location") 113 else: 114 image_info_contents += "avb_avbtool=avbtool\n" 115 image_info_contents += "avb_" + ctx.attr.type + "_hashtree_enable=true" + "\n" 116 footer_args = ctx.attr.avb_add_hashtree_footer_args 117 if footer_args: 118 footer_args += " " 119 footer_args += "--prop com.android.build.system.os_version:" + ctx.attr._platform_version_last_stable[BuildSettingInfo].value 120 footer_args += " --prop com.android.build.system.fingerprint:" + ctx.attr._build_fingerprint[BuildFingerprintInfo].fingerprint_placeholder_build_number 121 footer_args += " --prop com.android.build.system.security_patch:" + ctx.attr._platform_security_patch[BuildSettingInfo].value 122 if not ctx.attr.type.startswith("vbmeta_") and ctx.attr.avb_rollback_index >= 0: 123 footer_args += " --rollback_index " + str(ctx.attr.avb_rollback_index) 124 image_info_contents += "avb_" + ctx.attr.type + "_add_hashtree_footer_args=" + footer_args + "\n" 125 if ctx.attr.avb_key: 126 image_info_contents += "avb_" + ctx.attr.type + "_key_path=" + ctx.file.avb_key.path + "\n" 127 extra_inputs.append(ctx.file.avb_key) 128 image_info_contents += "avb_" + ctx.attr.type + "_algorithm=" + ctx.attr.avb_algorithm + "\n" 129 if ctx.attr.avb_rollback_index_location >= 0: 130 image_info_contents += "avb_" + ctx.attr.type + "_rollback_index_location=" + str(ctx.attr.avb_rollback_index_location) + "\n" 131 132 image_info_without_build_number = ctx.actions.declare_file(ctx.attr.name + "/image_info_without_build_number.txt") 133 ctx.actions.write(image_info_without_build_number, image_info_contents) 134 image_info = ctx.actions.declare_file(ctx.attr.name + "/image_info.txt") 135 ctx.actions.run( 136 inputs = [ 137 ctx.version_file, 138 image_info_without_build_number, 139 ], 140 outputs = [image_info], 141 executable = ctx.executable._status_file_reader, 142 arguments = [ 143 "replace", 144 ctx.version_file.path, 145 image_info_without_build_number.path, 146 image_info.path, 147 "--var", 148 "BUILD_NUMBER", 149 ], 150 ) 151 152 staging_dir_builder_options_file = ctx.actions.declare_file(ctx.attr.name + "/staging_dir_builder_options.json") 153 ctx.actions.write(staging_dir_builder_options_file, json.encode(staging_dir_builder_options)) 154 155 build_image_files = toolchain.build_image[DefaultInfo].files_to_run 156 157 # These are tools that are run from build_image or another tool that build_image runs. 158 # They are all expected to be available in the PATH. 159 extra_tools = [ 160 toolchain.avbtool[DefaultInfo].files_to_run, 161 toolchain.e2fsdroid[DefaultInfo].files_to_run, 162 toolchain.fec[DefaultInfo].files_to_run, 163 toolchain.mke2fs[DefaultInfo].files_to_run, 164 toolchain.mkfs_erofs[DefaultInfo].files_to_run, 165 toolchain.mkuserimg_mke2fs[DefaultInfo].files_to_run, 166 toolchain.simg2img[DefaultInfo].files_to_run, 167 toolchain.tune2fs[DefaultInfo].files_to_run, 168 ] 169 170 ctx.actions.run( 171 inputs = [ 172 image_info, 173 staging_dir_builder_options_file, 174 toolchain.openssl, 175 ] + files.values() + extra_inputs, 176 tools = extra_tools + [ 177 build_image_files, 178 du, 179 find, 180 python_interpreter, 181 toolchain.toybox[DefaultInfo].files_to_run, 182 ], 183 outputs = [output_image], 184 executable = ctx.executable._staging_dir_builder, 185 arguments = [ 186 staging_dir_builder_options_file.path, 187 build_image_files.executable.path, 188 "STAGING_DIR_PLACEHOLDER", 189 image_info.path, 190 output_image.path, 191 "STAGING_DIR_PLACEHOLDER", 192 ], 193 mnemonic = "BuildPartition", 194 env = { 195 # The dict + .keys() is to dedup the path elements, as some tools are in the same folder 196 "PATH": ":".join(({t.executable.dirname: True for t in extra_tools} | { 197 python_interpreter.dirname: True, 198 } | { 199 du.dirname: True, 200 } | { 201 find.dirname: True, 202 } | { 203 toolchain.openssl.dirname: True, 204 }).keys()), 205 }, 206 ) 207 208 return DefaultInfo(files = depset([output_image])) 209 210_partition = rule( 211 implementation = _partition_impl, 212 attrs = { 213 "type": attr.string( 214 mandatory = True, 215 values = _IMAGE_TYPES, 216 ), 217 "image_properties": attr.string( 218 doc = "The image property dictionary in key=value format. TODO: consider replacing this with explicit bazel properties for each property in this file.", 219 ), 220 "avb_enable": attr.bool(), 221 "avb_add_hashtree_footer_args": attr.string(), 222 "avb_key": attr.label(allow_single_file = True), 223 "avb_algorithm": attr.string(), 224 "avb_rollback_index": attr.int(default = -1), 225 "avb_rollback_index_location": attr.int(default = -1), 226 "base_staging_dir": attr.label( 227 allow_single_file = True, 228 doc = "A staging dir that the deps will be added to. This is intended to be used to import a make-built staging directory when building the partition with bazel.", 229 ), 230 "base_staging_dir_file_list": attr.label( 231 allow_single_file = True, 232 doc = "A file list that will be used to filter the base_staging_dir.", 233 ), 234 "deps": attr.label_list(), 235 "root_dir": attr.label( 236 allow_single_file = True, 237 doc = "A folder to add as the root_dir property in the property file", 238 ), 239 "selinux_file_contexts": attr.label( 240 allow_single_file = True, 241 doc = "The file specifying the selinux rules for all the files in this partition.", 242 ), 243 "_build_broken_incorrect_partition_images": attr.label( 244 default = "//build/bazel/product_config:build_broken_incorrect_partition_images", 245 ), 246 "_build_fingerprint": attr.label( 247 default = "//build/bazel/rules:build_fingerprint", 248 ), 249 "_platform_version_last_stable": attr.label( 250 default = "//build/bazel/product_config:platform_version_last_stable", 251 ), 252 "_platform_security_patch": attr.label( 253 default = "//build/bazel/product_config:platform_security_patch", 254 ), 255 "_staging_dir_builder": attr.label( 256 cfg = "exec", 257 doc = "The tool used to build a staging directory, because if bazel were to build it it would be entirely symlinks.", 258 executable = True, 259 default = "//build/bazel/rules:staging_dir_builder", 260 ), 261 "_status_file_reader": attr.label( 262 cfg = "exec", 263 executable = True, 264 default = "//build/bazel/rules:status_file_reader", 265 ), 266 }, 267 toolchains = [ 268 ":partition_toolchain_type", 269 "@bazel_tools//tools/python:toolchain_type", 270 ], 271) 272 273def partition(target_compatible_with = [], **kwargs): 274 target_compatible_with = select({ 275 "//build/bazel_common_rules/platforms/os:android": [], 276 "//conditions:default": ["@platforms//:incompatible"], 277 }) + target_compatible_with 278 _partition( 279 target_compatible_with = target_compatible_with, 280 **kwargs 281 ) 282