1# Copyright 2016 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"""Helpers for the build file used in android_sdk_repository.""" 16 17load("@local_config_platform//:constraints.bzl", "HOST_CONSTRAINTS") 18load("@rules_java//java:defs.bzl", "java_binary", "java_import") 19 20def _bool_flag_impl(_unused_ctx): 21 pass 22 23_bool_flag = rule( 24 implementation = _bool_flag_impl, 25 build_setting = config.bool(flag = True), 26) 27 28def _int_flag_impl(ctx): 29 allowed_values = ctx.attr.values 30 value = ctx.build_setting_value 31 if len(allowed_values) != 0 and value not in ctx.attr.values: 32 fail( 33 "Error setting %s: invalid value '%d'. Allowed values are %s" % ( 34 ctx.label, 35 value, 36 ",".join([str(val) for val in allowed_values]), 37 ), 38 ) 39 40int_flag = rule( 41 implementation = _int_flag_impl, 42 build_setting = config.int(flag = True), 43 attrs = { 44 "values": attr.int_list( 45 doc = "The list of allowed values for this setting. An error is raised if any other value is given.", 46 ), 47 }, 48 doc = "An int-typed build setting that can be set on the command line", 49) 50 51def _create_config_setting_rule(): 52 """Create config_setting rule for windows. 53 54 These represent the matching --host_cpu values. 55 """ 56 name = "windows" 57 if not native.existing_rule(name): 58 native.config_setting( 59 name = name, 60 values = {"host_cpu": "x64_" + name}, 61 ) 62 63 native.config_setting( 64 name = "d8_standalone_dexer", 65 values = {"define": "android_standalone_dexing_tool=d8_compat_dx"}, 66 ) 67 68 native.config_setting( 69 name = "dx_standalone_dexer", 70 values = {"define": "android_standalone_dexing_tool=dx_compat_dx"}, 71 ) 72 73 _bool_flag( 74 name = "allow_proguard", 75 build_setting_default = True, 76 ) 77 78 native.config_setting( 79 name = "disallow_proguard", 80 flag_values = {":allow_proguard": "false"}, 81 ) 82 83def create_android_sdk_rules( 84 name, 85 build_tools_version, 86 build_tools_directory, 87 api_levels, 88 default_api_level): 89 """Generate android_sdk rules for the API levels in the Android SDK. 90 91 Args: 92 name: string, the name of the repository being generated. 93 build_tools_version: string, the version of Android's build tools to use. 94 build_tools_directory: string, the directory name of the build tools in 95 sdk's build-tools directory. 96 api_levels: list of ints, the API levels from which to get android.jar 97 et al. and create android_sdk rules. 98 default_api_level: int, the API level to alias the default sdk to if 99 --android_sdk is not specified on the command line. 100 """ 101 102 _create_config_setting_rule() 103 104 int_flag( 105 name = "api_level", 106 build_setting_default = default_api_level, 107 values = api_levels, 108 visibility = ["//visibility:public"], 109 ) 110 111 windows_only_files = [ 112 "build-tools/%s/aapt.exe" % build_tools_directory, 113 "build-tools/%s/aidl.exe" % build_tools_directory, 114 "build-tools/%s/zipalign.exe" % build_tools_directory, 115 "platform-tools/adb.exe", 116 ] + native.glob( 117 ["build-tools/%s/aapt2.exe" % build_tools_directory], 118 allow_empty = True, 119 ) 120 121 linux_only_files = [ 122 "build-tools/%s/aapt" % build_tools_directory, 123 "build-tools/%s/aidl" % build_tools_directory, 124 "build-tools/%s/zipalign" % build_tools_directory, 125 "platform-tools/adb", 126 ] + native.glob( 127 ["extras", "build-tools/%s/aapt2" % build_tools_directory], 128 allow_empty = True, 129 exclude_directories = 0, 130 ) 131 132 # This filegroup is used to pass the minimal contents of the SDK to the 133 # Android integration tests. Note that in order to work on Windows, we cannot 134 # include directories and must keep the size small. 135 native.filegroup( 136 name = "files", 137 srcs = [ 138 "build-tools/%s/lib/apksigner.jar" % build_tools_directory, 139 "build-tools/%s/lib/d8.jar" % build_tools_directory, 140 "build-tools/%s/lib/dx.jar" % build_tools_directory, 141 "build-tools/%s/mainDexClasses.rules" % build_tools_directory, 142 ":build_tools_libs", 143 ] + [ 144 "platforms/android-%d/%s" % (api_level, filename) 145 for api_level in api_levels 146 for filename in ["android.jar", "framework.aidl"] 147 ] + select({ 148 ":windows": windows_only_files, 149 "//conditions:default": linux_only_files, 150 }), 151 ) 152 153 for api_level in api_levels: 154 if api_level >= 23: 155 # Android 23 removed most of org.apache.http from android.jar and moved it 156 # to a separate jar. 157 java_import( 158 name = "org_apache_http_legacy-%d" % api_level, 159 jars = ["platforms/android-%d/optional/org.apache.http.legacy.jar" % api_level], 160 ) 161 162 if api_level >= 28: 163 # Android 28 removed most of android.test from android.jar and moved it 164 # to separate jars. 165 java_import( 166 name = "legacy_test-%d" % api_level, 167 jars = [ 168 "platforms/android-%d/optional/android.test.base.jar" % api_level, 169 "platforms/android-%d/optional/android.test.mock.jar" % api_level, 170 "platforms/android-%d/optional/android.test.runner.jar" % api_level, 171 ], 172 neverlink = 1, 173 ) 174 175 native.config_setting( 176 name = "api_%d_enabled" % api_level, 177 flag_values = { 178 ":api_level": str(api_level), 179 }, 180 ) 181 182 # TODO(katre): Use the Starlark android_sdk 183 native.android_sdk( 184 name = "sdk-%d" % api_level, 185 aapt = select({ 186 ":windows": "build-tools/%s/aapt.exe" % build_tools_directory, 187 "//conditions:default": ":aapt_binary", 188 }), 189 aapt2 = select({ 190 ":windows": "build-tools/%s/aapt2.exe" % build_tools_directory, 191 "//conditions:default": ":aapt2_binary", 192 }), 193 adb = select({ 194 ":windows": "platform-tools/adb.exe", 195 "//conditions:default": "platform-tools/adb", 196 }), 197 aidl = select({ 198 ":windows": "build-tools/%s/aidl.exe" % build_tools_directory, 199 "//conditions:default": ":aidl_binary", 200 }), 201 android_jar = "platforms/android-%d/android.jar" % api_level, 202 apksigner = ":apksigner", 203 build_tools_version = build_tools_version, 204 dx = select({ 205 "d8_standalone_dexer": ":d8_compat_dx", 206 "dx_standalone_dexer": ":dx_binary", 207 "//conditions:default": ":d8_compat_dx", 208 }), 209 framework_aidl = "platforms/android-%d/framework.aidl" % api_level, 210 legacy_main_dex_list_generator = ":generate_main_dex_list", 211 main_dex_classes = "build-tools/%s/mainDexClasses.rules" % build_tools_directory, 212 proguard = select({ 213 ":disallow_proguard": ":fail", 214 "//conditions:default": "@bazel_tools//tools/jdk:proguard", 215 }), 216 shrinked_android_jar = "platforms/android-%d/android.jar" % api_level, 217 # See https://github.com/bazelbuild/bazel/issues/8757 218 tags = ["__ANDROID_RULES_MIGRATION__"], 219 zipalign = select({ 220 ":windows": "build-tools/%s/zipalign.exe" % build_tools_directory, 221 "//conditions:default": ":zipalign_binary", 222 }), 223 ) 224 225 native.toolchain( 226 name = "sdk-%d-toolchain" % api_level, 227 exec_compatible_with = HOST_CONSTRAINTS, 228 target_compatible_with = [ 229 "@platforms//os:android", 230 ], 231 toolchain = ":sdk-%d" % api_level, 232 toolchain_type = "@bazel_tools//tools/android:sdk_toolchain_type", 233 target_settings = [ 234 ":api_%d_enabled" % api_level, 235 ], 236 ) 237 238 create_dummy_sdk_toolchain() 239 240 native.alias( 241 name = "org_apache_http_legacy", 242 actual = ":org_apache_http_legacy-%d" % default_api_level, 243 ) 244 245 sdk_alias_dict = { 246 "//conditions:default": "sdk-%d" % default_api_level, 247 } 248 249 for api_level in api_levels: 250 sdk_alias_dict[":api_%d_enabled" % api_level] = "sdk-%d" % api_level 251 252 native.alias( 253 name = "sdk", 254 actual = select( 255 sdk_alias_dict, 256 no_match_error = "Unknown Android SDK level, valid levels are %s" % ",".join([str(level) for level in api_levels]), 257 ), 258 ) 259 260 java_binary( 261 name = "apksigner", 262 main_class = "com.android.apksigner.ApkSignerTool", 263 runtime_deps = ["build-tools/%s/lib/apksigner.jar" % build_tools_directory], 264 ) 265 266 native.filegroup( 267 name = "build_tools_libs", 268 srcs = native.glob([ 269 "build-tools/%s/lib/**" % build_tools_directory, 270 # Build tools version 24.0.0 added a lib64 folder. 271 "build-tools/%s/lib64/**" % build_tools_directory, 272 ], allow_empty = True), 273 ) 274 275 for tool in ["aapt", "aapt2", "aidl", "zipalign"]: 276 native.genrule( 277 name = tool + "_runner", 278 srcs = [], 279 outs = [tool + "_runner.sh"], 280 cmd = "\n".join([ 281 "cat > $@ << 'EOF'", 282 "#!/bin/bash", 283 "set -eu", 284 # The tools under build-tools/VERSION require the libraries under 285 # build-tools/VERSION/lib, so we can't simply depend on them as a 286 # file like we do with aapt. 287 # On Windows however we can use these binaries directly because 288 # there's no runfiles support so Bazel just creates a junction to 289 # {SDK}/build-tools. 290 "SDK=$${0}.runfiles/%s" % name, 291 # If $${SDK} is not a directory, it means that this tool is running 292 # from a runfiles directory, in the case of 293 # android_instrumentation_test. Hence, use the androidsdk 294 # that's already present in the runfiles of the current context. 295 "if [[ ! -d $${SDK} ]] ; then", 296 " SDK=$$(pwd)/../%s" % name, 297 "fi", 298 "tool=$${SDK}/build-tools/%s/%s" % (build_tools_directory, tool), 299 "exec env LD_LIBRARY_PATH=$${SDK}/build-tools/%s/lib64 $$tool $$*" % build_tools_directory, 300 "EOF\n", 301 ]), 302 ) 303 304 native.sh_binary( 305 name = tool + "_binary", 306 srcs = [tool + "_runner.sh"], 307 data = [ 308 ":build_tools_libs", 309 "build-tools/%s/%s" % (build_tools_directory, tool), 310 ], 311 ) 312 313 native.sh_binary( 314 name = "fail", 315 srcs = select({ 316 ":windows": [":generate_fail_cmd"], 317 "//conditions:default": [":generate_fail_sh"], 318 }), 319 ) 320 321 native.genrule( 322 name = "generate_fail_sh", 323 outs = ["fail.sh"], 324 cmd = "echo -e '#!/bin/bash\\nexit 1' >> $@; chmod +x $@", 325 executable = 1, 326 ) 327 328 native.genrule( 329 name = "generate_fail_cmd", 330 outs = ["fail.cmd"], 331 cmd = "echo @exit /b 1 > $@", 332 executable = 1, 333 ) 334 335 native.genrule( 336 name = "main_dex_list_creator_source", 337 srcs = [], 338 outs = ["main_dex_list_creator.sh"], 339 cmd = "\n".join([ 340 "cat > $@ <<'EOF'", 341 "#!/bin/bash", 342 "", 343 "MAIN_DEX_LIST=$$1", 344 "STRIPPED_JAR=$$2", 345 "JAR=$$3", 346 "" + 347 "JAVA_BINARY=$$0.runfiles/%s/main_dex_list_creator_java" % name, 348 "$$JAVA_BINARY $$STRIPPED_JAR $$JAR > $$MAIN_DEX_LIST", 349 "exit $$?", 350 "", 351 "EOF\n", 352 ]), 353 ) 354 355 native.sh_binary( 356 name = "main_dex_list_creator", 357 srcs = ["main_dex_list_creator.sh"], 358 data = [":main_dex_list_creator_java"], 359 ) 360 java_binary( 361 name = "main_dex_list_creator_java", 362 main_class = "com.android.multidex.ClassReferenceListBuilder", 363 runtime_deps = [":dx_jar_import"], 364 ) 365 java_binary( 366 name = "dx_binary", 367 main_class = "com.android.dx.command.Main", 368 runtime_deps = [":dx_jar_import"], 369 ) 370 java_import( 371 name = "dx_jar_import", 372 jars = ["build-tools/%s/lib/dx.jar" % build_tools_directory], 373 ) 374 java_binary( 375 name = "generate_main_dex_list", 376 jvm_flags = [ 377 "-XX:+TieredCompilation", 378 "-XX:TieredStopAtLevel=1", 379 # Consistent with what we use for desugar. 380 "-Xms8g", 381 "-Xmx8g", 382 ], 383 main_class = "com.android.tools.r8.GenerateMainDexList", 384 runtime_deps = ["@bazel_tools//src/tools/android/java/com/google/devtools/build/android/r8"], 385 ) 386 java_binary( 387 name = "d8_compat_dx", 388 main_class = "com.google.devtools.build.android.r8.CompatDx", 389 runtime_deps = [ 390 "@bazel_tools//src/tools/android/java/com/google/devtools/build/android/r8", 391 ], 392 ) 393 native.alias( 394 name = "d8_jar_import", 395 actual = "@android_gmaven_r8//jar", 396 ) 397 398TAGDIR_TO_TAG_MAP = { 399 "google_apis_playstore": "playstore", 400 "google_apis": "google", 401 "default": "android", 402 "android-tv": "tv", 403 "android-wear": "wear", 404} 405 406ARCHDIR_TO_ARCH_MAP = { 407 "x86": "x86", 408 "armeabi-v7a": "arm", 409} 410 411# buildifier: disable=unnamed-macro 412def create_system_images_filegroups(system_image_dirs): 413 """Generate filegroups for the system images in the Android SDK. 414 415 Args: 416 system_image_dirs: list of strings, the directories containing system image 417 files to be used to create android_device rules. 418 """ 419 420 # These images will need to be updated as Android releases new system images. 421 # We are intentionally not adding future releases because there is no 422 # guarantee that they will work out of the box. Supported system images should 423 # be added here once they have been confirmed to work with the Bazel Android 424 # testing infrastructure. 425 system_images = [ 426 (tag, str(api), arch) 427 for tag in ["android", "google"] 428 for api in [10] + list(range(15, 20)) + list(range(21, 30)) 429 for arch in ("x86", "arm") 430 ] + [ 431 ("playstore", str(api), "x86") 432 for api in list(range(24, 30)) 433 ] 434 tv_images = [ 435 ("tv", str(api), "x86") 436 for api in range(21, 30) 437 ] + [ 438 ("tv", "21", "arm"), 439 ("tv", "23", "arm"), 440 ] 441 wear_images = [ 442 ("wear", str(api), "x86") 443 for api in [23, 25, 26, 28] 444 ] + [ 445 ("wear", str(api), "arm") 446 for api in [23, 25] 447 ] 448 supported_system_images = system_images + tv_images + wear_images 449 450 installed_system_images_dirs = {} 451 for system_image_dir in system_image_dirs: 452 apidir, tagdir, archdir = system_image_dir.split("/")[1:] 453 if "-" not in apidir: 454 continue 455 api = apidir.split("-")[1] # "android-24" --> "24", "android-O" --> "O" 456 if tagdir not in TAGDIR_TO_TAG_MAP: 457 continue 458 tag = TAGDIR_TO_TAG_MAP[tagdir] 459 if archdir not in ARCHDIR_TO_ARCH_MAP: 460 continue 461 arch = ARCHDIR_TO_ARCH_MAP[archdir] 462 if (tag, api, arch) in supported_system_images: 463 name = "emulator_images_%s_%s_%s" % (tag, api, arch) 464 installed_system_images_dirs[name] = system_image_dir 465 else: 466 # TODO(bazel-team): If the user has an unsupported system image installed, 467 # should we print a warning? This includes all 64-bit system-images. 468 pass 469 470 for (tag, api, arch) in supported_system_images: 471 name = "emulator_images_%s_%s_%s" % (tag, api, arch) 472 if name in installed_system_images_dirs: 473 system_image_dir = installed_system_images_dirs[name] 474 475 # For supported system images that exist in /sdk/system-images/, we 476 # create a filegroup with their contents. 477 native.filegroup( 478 name = name, 479 srcs = native.glob([ 480 "%s/**" % system_image_dir, 481 ]), 482 ) 483 native.filegroup( 484 name = "%s_qemu2_extra" % name, 485 srcs = native.glob(["%s/kernel-ranchu" % system_image_dir], allow_empty = True), 486 ) 487 else: 488 # For supported system images that are not installed in the SDK, we 489 # create a "poison pill" genrule to display a helpful error message to 490 # a user who attempts to run a test against an android_device that 491 # they don't have the system image for installed. 492 native.genrule( 493 name = name, 494 outs = [ 495 # Necessary so that the build doesn't fail in analysis because 496 # android_device expects a file named source.properties. 497 "poison_pill_for_%s/source.properties" % name, 498 ], 499 cmd = """echo \ 500 This rule requires that the Android SDK used by Bazel has the \ 501 following system image installed: %s. Please install this system \ 502 image through the Android SDK Manager and try again. ; \ 503 exit 1 504 """ % name, 505 ) 506 native.filegroup( 507 name = "%s_qemu2_extra" % name, 508 srcs = [], 509 ) # buildifier: disable=unnamed-macro 510 511# This is a dummy sdk toolchain that matches any platform. It will 512# fail if actually resolved to and used. 513# buildifier: disable=unnamed-macro 514def create_dummy_sdk_toolchain(): 515 "Create a dummy SDK for fallback builds" 516 517 native.toolchain( 518 name = "sdk-dummy-toolchain", 519 toolchain = ":sdk-dummy", 520 toolchain_type = "@bazel_tools//tools/android:sdk_toolchain_type", 521 ) 522 523 native.filegroup(name = "jar-filegroup", srcs = ["dummy.jar"]) 524 525 native.genrule( 526 name = "genrule", 527 srcs = [], 528 outs = ["empty.sh"], 529 cmd = "echo '' >> \"$@\"", 530 executable = 1, 531 ) 532 533 native.sh_binary(name = "empty-binary", srcs = [":genrule"]) 534 535 native.android_sdk( 536 name = "sdk-dummy", 537 aapt = ":empty-binary", 538 adb = ":empty-binary", 539 aidl = ":empty-binary", 540 android_jar = ":jar-filegroup", 541 apksigner = ":empty-binary", 542 dx = ":empty-binary", 543 framework_aidl = "dummy.jar", 544 main_dex_classes = "dummy.jar", 545 main_dex_list_creator = ":empty-binary", 546 proguard = ":empty-binary", 547 shrinked_android_jar = "dummy.jar", 548 tags = ["__ANDROID_RULES_MIGRATION__"], 549 zipalign = ":empty-binary", 550 ) 551