1# Copyright 2018 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"""Implementation.""" 16 17load( 18 "@rules_android//rules:acls.bzl", 19 _acls = "acls", 20) 21load( 22 "@rules_android//rules:common.bzl", 23 _common = "common", 24) 25load("@rules_android//rules:intellij.bzl", "intellij") 26load( 27 "@rules_android//rules:java.bzl", 28 _java = "java", 29) 30load("@rules_android//rules:providers.bzl", "AndroidLintRulesInfo") 31load( 32 "@rules_android//rules:resources.bzl", 33 _resources = "resources", 34) 35load( 36 "@rules_android//rules:utils.bzl", 37 _get_android_toolchain = "get_android_toolchain", 38 _utils = "utils", 39) 40 41RULE_PREFIX = "_aar" 42ANDROID_MANIFEST = "AndroidManifest.xml" 43LINT_JAR = "lint.jar" 44_UNEXPECTED_LINT_JAR_ERROR = ( 45 "In target %s, has_lint_jar attribute is required when the aar contains " + 46 "a lint.jar file." 47) 48 49def _create_aar_artifact(ctx, name): 50 return ctx.actions.declare_file("%s/%s/%s" % (RULE_PREFIX, ctx.label.name, name)) 51 52def _create_aar_tree_artifact(ctx, name): 53 return ctx.actions.declare_directory("%s/unzipped/%s/%s" % (RULE_PREFIX, name, ctx.label.name)) 54 55# Create an action to extract a file (specified by the parameter filename) from an AAR file. 56def _extract_single_file( 57 ctx, 58 out_file, 59 aar, 60 filename, 61 unzip_tool): 62 args = ctx.actions.args() 63 args.add(aar) 64 args.add(filename) 65 args.add("-d", out_file.dirname) 66 67 ctx.actions.run( 68 executable = unzip_tool, 69 arguments = [args], 70 inputs = [aar], 71 outputs = [out_file], 72 mnemonic = "AarFileExtractor", 73 progress_message = "Extracting %s from %s" % (filename, aar.basename), 74 ) 75 76def _extract_resources( 77 ctx, 78 out_resources_dir, 79 out_assets_dir, 80 aar, 81 aar_resources_extractor_tool): 82 args = ctx.actions.args() 83 args.add("--input_aar", aar) 84 args.add("--output_res_dir", out_resources_dir.path) 85 args.add("--output_assets_dir", out_assets_dir.path) 86 ctx.actions.run( 87 executable = aar_resources_extractor_tool, 88 arguments = [args], 89 inputs = [aar], 90 outputs = [out_resources_dir, out_assets_dir], 91 mnemonic = "AarResourcesExtractor", 92 progress_message = "Extracting resources and assets from %s" % aar.basename, 93 ) 94 95def _extract_native_libs( 96 ctx, 97 output_zip, 98 aar, 99 android_cpu, 100 aar_native_libs_zip_creator_tool): 101 args = ctx.actions.args() 102 args.add("--input_aar", aar) 103 args.add("--cpu", android_cpu) 104 args.add("--output_zip", output_zip) 105 ctx.actions.run( 106 executable = aar_native_libs_zip_creator_tool, 107 arguments = [args], 108 inputs = [aar], 109 outputs = [output_zip], 110 mnemonic = "AarNativeLibsFilter", 111 progress_message = "Filtering AAR native libs by architecture", 112 ) 113 114def _process_resources( 115 ctx, 116 aar, 117 manifest, 118 deps, 119 aar_resources_extractor_tool, 120 unzip_tool): 121 # Extract resources and assets, if they exist. 122 resources = _create_aar_tree_artifact(ctx, "resources") 123 assets = _create_aar_tree_artifact(ctx, "assets") 124 _extract_resources( 125 ctx, 126 resources, 127 assets, 128 aar, 129 aar_resources_extractor_tool, 130 ) 131 132 resources_ctx = _resources.process_starlark( 133 ctx, 134 manifest = manifest, 135 assets = [assets], 136 assets_dir = assets.path, 137 resource_files = [resources], 138 stamp_manifest = False, 139 deps = ctx.attr.deps, 140 exports = ctx.attr.exports, 141 exports_manifest = getattr(ctx.attr, "exports_manifest", True), 142 143 # Tool and Processing related inputs 144 aapt = _get_android_toolchain(ctx).aapt2.files_to_run, 145 android_jar = ctx.attr._android_sdk[AndroidSdkInfo].android_jar, 146 android_kit = _get_android_toolchain(ctx).android_kit.files_to_run, 147 busybox = _get_android_toolchain(ctx).android_resources_busybox.files_to_run, 148 java_toolchain = _common.get_java_toolchain(ctx), 149 host_javabase = _common.get_host_javabase(ctx), 150 instrument_xslt = _utils.only(_get_android_toolchain(ctx).add_g3itr_xslt.files.to_list()), 151 xsltproc = _get_android_toolchain(ctx).xsltproc_tool.files_to_run, 152 ) 153 154 # TODO: replace android_data 155 # data_ctx = android_data.make_context(ctx.actions, ctx.attr) 156 # resource_apk = android_data.process_aar_import_data( 157 # data_ctx, 158 # resources, 159 # assets, 160 # manifest, 161 # deps = deps, 162 # ) 163 # resources_ctx["validation_results"].append( 164 # _utils.only(resource_apk[AndroidResourcesInfo].direct_android_resources.to_list()).java_class_jar, 165 # ) 166 # resources_ctx["providers"].append(resource_apk[AndroidResourcesInfo]) 167 # resources_ctx["providers"].append(resource_apk[AndroidAssetsInfo]) 168 169 if not _acls.in_aar_propagate_resources(str(ctx.label)): 170 resources_ctx["providers"] = [] 171 172 return struct(**resources_ctx) 173 174def _extract_jars( 175 ctx, 176 out_jars_tree_artifact, 177 out_jars_params_file, 178 aar, 179 aar_embedded_jars_extractor_tool): 180 args = ctx.actions.args() 181 args.add("--input_aar", aar) 182 args.add("--output_dir", out_jars_tree_artifact.path) 183 args.add("--output_singlejar_param_file", out_jars_params_file) 184 ctx.actions.run( 185 executable = aar_embedded_jars_extractor_tool, 186 arguments = [args], 187 inputs = [aar], 188 outputs = [out_jars_tree_artifact, out_jars_params_file], 189 mnemonic = "AarEmbeddedJarsExtractor", 190 progress_message = "Extracting classes.jar and libs/*.jar from %s" % aar.basename, 191 ) 192 193def _merge_jars( 194 ctx, 195 out_jar, 196 jars_tree_artifact, 197 jars_param_file, 198 single_jar_tool): 199 args = ctx.actions.args() 200 args.add("--output", out_jar) 201 args.add("--dont_change_compression") 202 args.add("--normalize") 203 args.add("@" + jars_param_file.path) 204 ctx.actions.run( 205 executable = single_jar_tool, 206 arguments = [args], 207 inputs = [jars_tree_artifact, jars_param_file], 208 outputs = [out_jar], 209 mnemonic = "AarJarsMerger", 210 progress_message = "Merging AAR embedded jars", 211 ) 212 213def _extract_and_merge_jars( 214 ctx, 215 out_jar, 216 aar, 217 aar_embedded_jars_extractor_tool, 218 single_jar_tool): 219 """Extracts all the Jars within the AAR and produces a single jar. 220 221 An AAR may have multiple Jar files embedded within it. This method 222 extracts and merges all Jars. 223 """ 224 jars_tree_artifact = _create_aar_tree_artifact(ctx, "jars") 225 jars_params_file = _create_aar_artifact(ctx, "jar_merging_params") 226 _extract_jars( 227 ctx, 228 jars_tree_artifact, 229 jars_params_file, 230 aar, 231 aar_embedded_jars_extractor_tool, 232 ) 233 _merge_jars( 234 ctx, 235 out_jar, 236 jars_tree_artifact, 237 jars_params_file, 238 single_jar_tool, 239 ) 240 241def _create_import_deps_check( 242 ctx, 243 jars_to_check, 244 declared_deps, 245 transitive_deps, 246 bootclasspath, 247 jdeps_output, 248 import_deps_checker_tool, 249 host_javabase): 250 args = ctx.actions.args() 251 args.add_all(jars_to_check, before_each = "--input") 252 args.add_all(declared_deps, before_each = "--directdep") 253 args.add_all(transitive_deps, before_each = "--classpath_entry") 254 args.add_all(bootclasspath, before_each = "--bootclasspath_entry") 255 args.add("--checking_mode=error") 256 args.add("--jdeps_output", jdeps_output) 257 args.add("--rule_label", ctx.label) 258 259 _java.run( 260 ctx = ctx, 261 host_javabase = host_javabase, 262 executable = import_deps_checker_tool, 263 arguments = [args], 264 inputs = depset( 265 jars_to_check, 266 transitive = [ 267 declared_deps, 268 transitive_deps, 269 bootclasspath, 270 ], 271 ), 272 outputs = [jdeps_output], 273 mnemonic = "ImportDepsChecker", 274 progress_message = "Checking the completeness of the deps for %s" % jars_to_check, 275 ) 276 277def _process_jars( 278 ctx, 279 out_jar, 280 aar, 281 source_jar, 282 r_java, 283 deps, 284 exports, 285 enable_desugar_java8, 286 enable_imports_deps_check, 287 bootclasspath, 288 desugar_java8_extra_bootclasspath, 289 aar_embedded_jars_extractor_tool, 290 import_deps_checker_tool, 291 single_jar_tool, 292 java_toolchain, 293 host_javabase): 294 providers = [] 295 validation_results = [] 296 r_java_info = [r_java] if r_java else [] 297 298 # An aar may have multple Jar files, extract and merge into a single jar. 299 _extract_and_merge_jars( 300 ctx, 301 out_jar, 302 aar, 303 aar_embedded_jars_extractor_tool, 304 single_jar_tool, 305 ) 306 307 java_infos = deps + exports 308 309 if enable_desugar_java8: 310 bootclasspath = depset(transitive = [ 311 desugar_java8_extra_bootclasspath, 312 bootclasspath, 313 ]) 314 315 merged_java_info = java_common.merge(java_infos + r_java_info) 316 jdeps_artifact = _create_aar_artifact(ctx, "jdeps.proto") 317 _create_import_deps_check( 318 ctx, 319 [out_jar], 320 merged_java_info.compile_jars, 321 merged_java_info.transitive_compile_time_jars, 322 bootclasspath, 323 jdeps_artifact, 324 import_deps_checker_tool, 325 host_javabase, 326 ) 327 if enable_imports_deps_check: 328 validation_results.append(jdeps_artifact) 329 330 java_info = JavaInfo( 331 out_jar, 332 compile_jar = java_common.stamp_jar( 333 actions = ctx.actions, 334 jar = out_jar, 335 target_label = ctx.label, 336 java_toolchain = java_toolchain, 337 ), 338 source_jar = source_jar, 339 neverlink = False, 340 deps = r_java_info + java_infos, # TODO(djwhang): Exports are not deps. 341 exports = 342 (r_java_info if _acls.in_aar_import_exports_r_java(str(ctx.label)) else []) + 343 java_infos, # TODO(djwhang): Deps are not exports. 344 # TODO(djwhang): AarImportTest is not expecting jdeps, enable or remove it completely 345 # jdeps = jdeps_artifact, 346 ) 347 providers.append(java_info) 348 349 return struct( 350 java_info = java_info, 351 providers = providers, 352 validation_results = validation_results, 353 ) 354 355def _validate_rule( 356 ctx, 357 aar, 358 manifest, 359 checks): 360 package = _java.resolve_package_from_label(ctx.label, ctx.attr.package) 361 validation_output = ctx.actions.declare_file("%s_validation_output" % ctx.label.name) 362 363 args = ctx.actions.args() 364 args.add("-aar", aar) 365 inputs = [aar] 366 args.add("-label", str(ctx.label)) 367 if _acls.in_aar_import_pkg_check(str(ctx.label)): 368 args.add("-pkg", package) 369 args.add("-manifest", manifest) 370 inputs.append(manifest) 371 if ctx.attr.has_lint_jar: 372 args.add("-has_lint_jar") 373 args.add("-output", validation_output) 374 375 ctx.actions.run( 376 executable = checks, 377 arguments = [args], 378 inputs = inputs, 379 outputs = [validation_output], 380 mnemonic = "ValidateAAR", 381 progress_message = "Validating aar_import %s" % str(ctx.label), 382 ) 383 return validation_output 384 385def _process_lint_rules( 386 ctx, 387 aar, 388 unzip_tool): 389 providers = [] 390 391 if ctx.attr.has_lint_jar: 392 lint_jar = _create_aar_artifact(ctx, LINT_JAR) 393 _extract_single_file( 394 ctx, 395 lint_jar, 396 aar, 397 LINT_JAR, 398 unzip_tool, 399 ) 400 providers.append(AndroidLintRulesInfo( 401 lint_jar = lint_jar, 402 )) 403 404 providers.extend(_utils.collect_providers( 405 AndroidLintRulesInfo, 406 ctx.attr.exports, 407 )) 408 return providers 409 410def impl(ctx): 411 """The rule implementation. 412 413 Args: 414 ctx: The context. 415 416 Returns: 417 A list of providers. 418 """ 419 providers = [] 420 validation_outputs = [] 421 422 aar = _utils.only(ctx.files.aar) 423 unzip_tool = _get_android_toolchain(ctx).unzip_tool.files_to_run 424 425 # Extract the AndroidManifest.xml from the AAR. 426 android_manifest = _create_aar_artifact(ctx, ANDROID_MANIFEST) 427 _extract_single_file( 428 ctx, 429 android_manifest, 430 aar, 431 ANDROID_MANIFEST, 432 unzip_tool, 433 ) 434 435 resources_ctx = _process_resources( 436 ctx, 437 aar = aar, 438 manifest = android_manifest, 439 deps = ctx.attr.deps, 440 aar_resources_extractor_tool = 441 _get_android_toolchain(ctx).aar_resources_extractor.files_to_run, 442 unzip_tool = unzip_tool, 443 ) 444 providers.extend(resources_ctx.providers) 445 446 merged_jar = _create_aar_artifact(ctx, "classes_and_libs_merged.jar") 447 jvm_ctx = _process_jars( 448 ctx, 449 out_jar = merged_jar, 450 aar = aar, 451 source_jar = ctx.file.srcjar, 452 deps = _utils.collect_providers(JavaInfo, ctx.attr.deps), 453 r_java = resources_ctx.r_java, 454 exports = _utils.collect_providers(JavaInfo, ctx.attr.exports), 455 enable_desugar_java8 = ctx.fragments.android.desugar_java8, 456 enable_imports_deps_check = 457 _acls.in_aar_import_deps_checker(str(ctx.label)), 458 aar_embedded_jars_extractor_tool = 459 _get_android_toolchain(ctx).aar_embedded_jars_extractor.files_to_run, 460 bootclasspath = 461 ctx.attr._java_toolchain[java_common.JavaToolchainInfo].bootclasspath, 462 desugar_java8_extra_bootclasspath = 463 _get_android_toolchain(ctx).desugar_java8_extra_bootclasspath.files, 464 import_deps_checker_tool = 465 _get_android_toolchain(ctx).import_deps_checker.files_to_run, 466 single_jar_tool = 467 ctx.attr._java_toolchain[java_common.JavaToolchainInfo].single_jar, 468 java_toolchain = 469 ctx.attr._java_toolchain[java_common.JavaToolchainInfo], 470 host_javabase = ctx.attr._host_javabase, 471 ) 472 providers.extend(jvm_ctx.providers) 473 validation_outputs.extend(jvm_ctx.validation_results) 474 475 native_libs = _create_aar_artifact(ctx, "native_libs.zip") 476 _extract_native_libs( 477 ctx, 478 native_libs, 479 aar = aar, 480 android_cpu = ctx.fragments.android.android_cpu, 481 aar_native_libs_zip_creator_tool = 482 _get_android_toolchain(ctx).aar_native_libs_zip_creator.files_to_run, 483 ) 484 native_libs_infos = _utils.collect_providers( 485 AndroidNativeLibsInfo, 486 ctx.attr.deps, 487 ctx.attr.exports, 488 ) 489 providers.append( 490 AndroidNativeLibsInfo( 491 depset( 492 [native_libs], 493 transitive = [info.native_libs for info in native_libs_infos], 494 ), 495 ), 496 ) 497 498 lint_providers = _process_lint_rules( 499 ctx, 500 aar = aar, 501 unzip_tool = unzip_tool, 502 ) 503 providers.extend(lint_providers) 504 505 validation_outputs.append(_validate_rule( 506 ctx, 507 aar = aar, 508 manifest = android_manifest, 509 checks = _get_android_toolchain(ctx).aar_import_checks.files_to_run, 510 )) 511 512 providers.append( 513 intellij.make_android_ide_info( 514 ctx, 515 java_package = _java.resolve_package_from_label(ctx.label, ctx.attr.package), 516 manifest = resources_ctx.merged_manifest, 517 defines_resources = resources_ctx.defines_resources, 518 merged_manifest = resources_ctx.merged_manifest, 519 resources_apk = resources_ctx.resources_apk, 520 r_jar = _utils.only(resources_ctx.r_java.outputs.jars) if resources_ctx.r_java else None, 521 java_info = jvm_ctx.java_info, 522 signed_apk = None, # signed_apk, always empty for aar_import 523 apks_under_test = [], # apks_under_test, always empty for aar_import 524 native_libs = dict(), # nativelibs, always empty for aar_import 525 idlclass = _get_android_toolchain(ctx).idlclass.files_to_run, 526 host_javabase = _common.get_host_javabase(ctx), 527 ), 528 ) 529 530 providers.append(OutputGroupInfo(_validation = depset(validation_outputs))) 531 532 # There isn't really any use case for building an aar_import target on its own, so the files to 533 # build could be empty. The R class JAR and merged JARs are added here as a sanity check for 534 # Bazel developers so that `bazel build java/com/my_aar_import` will fail if the resource 535 # processing or JAR merging steps fail. 536 files_to_build = [] 537 files_to_build.extend(resources_ctx.validation_results) # TODO(djwhang): This should be validation. 538 files_to_build.append(merged_jar) 539 540 providers.append( 541 DefaultInfo( 542 files = depset(files_to_build), 543 runfiles = ctx.runfiles(), 544 ), 545 ) 546 547 return providers 548