From 8c58d5ca98f7510f427a3d971351d013933e978e Mon Sep 17 00:00:00 2001 From: Tim Peut Date: Tue, 28 Sep 2021 18:45:05 +0000 Subject: Created project --- groups | 3 +++ project.config | 6 ++++++ 2 files changed, 9 insertions(+) create mode 100644 groups create mode 100644 project.config diff --git a/groups b/groups new file mode 100644 index 0000000..52ca179 --- /dev/null +++ b/groups @@ -0,0 +1,3 @@ +# UUID Group Name +# +mdb:mobile-ninjas-releaser mdb/mobile-ninjas-releaser diff --git a/project.config b/project.config new file mode 100644 index 0000000..cb8250b --- /dev/null +++ b/project.config @@ -0,0 +1,6 @@ +[access] + inheritFrom = mobile-ninjas-releaser +[submit] + action = inherit +[access "refs/*"] + owner = group mdb/mobile-ninjas-releaser -- cgit v1.2.3 From 793451188a75d563ecebb737b798bb2fa9839dd5 Mon Sep 17 00:00:00 2001 From: Tim Peut Date: Tue, 28 Sep 2021 18:48:47 +0000 Subject: Modify access rules --- groups | 1 + project.config | 1 + 2 files changed, 2 insertions(+) diff --git a/groups b/groups index 52ca179..27a76d8 100644 --- a/groups +++ b/groups @@ -1,3 +1,4 @@ # UUID Group Name # +mdb:copybara-git-writers mdb/copybara-git-writers mdb:mobile-ninjas-releaser mdb/mobile-ninjas-releaser diff --git a/project.config b/project.config index cb8250b..db40d49 100644 --- a/project.config +++ b/project.config @@ -3,4 +3,5 @@ [submit] action = inherit [access "refs/*"] + owner = group mdb/copybara-git-writers owner = group mdb/mobile-ninjas-releaser -- cgit v1.2.3 From ac60cc068ed234e830c7d7ffe170c50ff2ee1f1c Mon Sep 17 00:00:00 2001 From: A Googler Date: Tue, 28 Sep 2021 15:10:33 -0400 Subject: \nInitial commit\n PiperOrigin-RevId: 399498130 --- .bazelci/postsubmit.yml | 22 + AUTHORS | 9 + BUILD | 0 CONTRIBUTING.md | 39 + CONTRIBUTORS | 16 + LICENSE | 202 +++ README.md | 49 + WORKSPACE | 3 + defs.bzl | 31 + rules/BUILD | 12 + rules/aapt.bzl | 200 +++ rules/aar_import/BUILD | 21 + rules/aar_import/attrs.bzl | 64 + rules/aar_import/impl.bzl | 578 +++++++ rules/aar_import/rule.bzl | 31 + rules/acls.bzl | 333 ++++ rules/acls/BUILD | 6 + rules/acls/aar_import_deps_checker.bzl | 27 + .../acls/aar_import_explicit_exports_manifest.bzl | 19 + rules/acls/aar_import_exports_r_java.bzl | 19 + rules/acls/aar_propagate_resources.bzl | 24 + rules/acls/ait_install_snapshots.bzl | 23 + rules/acls/ait_virtual_device.bzl | 21 + rules/acls/allow_resource_conflicts.bzl | 20 + rules/acls/android_archive_dogfood.bzl | 20 + .../android_archive_excluded_deps_denylist.bzl | 21 + rules/acls/android_binary_starlark_resources.bzl | 22 + rules/acls/android_build_stamping_rollout.bzl | 23 + rules/acls/android_device_plugin_rollout.bzl | 26 + rules/acls/android_feature_splits_dogfood.bzl | 19 + ...d_instrumentation_binary_starlark_resources.bzl | 22 + ...instrumentation_test_manifest_check_rollout.bzl | 22 + rules/acls/android_library_implicit_exports.bzl | 23 + .../android_library_resources_without_srcs.bzl | 19 + .../android_library_starlark_resource_outputs.bzl | 26 + rules/acls/android_library_starlark_resources.bzl | 23 + rules/acls/android_lint_checks_rollout.bzl | 22 + rules/acls/android_lint_rollout.bzl | 21 + rules/acls/android_test_lockdown.bzl | 21 + rules/acls/android_test_platform_rollout.bzl | 26 + rules/acls/android_test_starlark_resources.bzl | 17 + rules/acls/b122039567.bzl | 18 + rules/acls/b123854163.bzl | 18 + rules/acls/dex2oat_opts.bzl | 19 + rules/acls/fix_export_exporting_rollout.bzl | 24 + rules/acls/fix_resource_transitivity_rollout.bzl | 22 + rules/acls/host_dex2oat_rollout.bzl | 27 + rules/acls/install_apps_in_data.bzl | 18 + rules/acls/kt_android_library_rollout.bzl | 24 + rules/acls/library_rollout.bzl | 23 + rules/acls/library_rollout_override.bzl | 17 + rules/acls/lint_registry_rollout.bzl | 21 + rules/acls/local_test_multi_proto.bzl | 23 + rules/acls/local_test_rollout.bzl | 22 + rules/acls/local_test_starlark_resources.bzl | 22 + rules/acls/nitrogen_test_runner_rollout.bzl | 28 + rules/acls/partial_jetification_targets.bzl | 20 + rules/acls/sourceless_binary_rollout.bzl | 21 + rules/acls/starlark_resources_diff.bzl | 18 + rules/acls/test_to_instrument_test_rollout.bzl | 21 + rules/acls/use_classic_desugar.bzl | 18 + rules/android_application/BUILD | 27 + rules/android_application/android_application.bzl | 51 + .../android_application_rule.bzl | 385 +++++ .../android_application/android_feature_module.bzl | 58 + .../android_feature_module_rule.bzl | 196 +++ rules/android_application/attrs.bzl | 89 ++ .../android_application/bundle_deploy.sh_template | 26 + .../feature_module_validation.sh | 78 + .../gen_android_feature_manifest.sh | 51 + .../gen_priority_android_feature_manifest.sh | 49 + rules/android_binary.bzl | 42 + rules/android_binary_internal/BUILD | 18 + rules/android_binary_internal/attrs.bzl | 69 + rules/android_binary_internal/impl.bzl | 111 ++ rules/android_binary_internal/rule.bzl | 90 ++ rules/android_library/BUILD | 21 + rules/android_library/attrs.bzl | 73 + rules/android_library/impl.bzl | 534 +++++++ rules/android_library/rule.bzl | 146 ++ rules/android_local_test.bzl | 27 + rules/android_ndk_repository.bzl | 25 + rules/android_sdk.bzl | 54 + rules/android_sdk_repository.bzl | 25 + rules/android_tools_defaults_jar.bzl | 32 + rules/attrs.bzl | 290 ++++ rules/bundletool.bzl | 263 +++ rules/busybox.bzl | 1042 ++++++++++++ rules/common.bzl | 111 ++ rules/data_binding.bzl | 299 ++++ rules/data_binding_annotation_template.txt | 15 + rules/flags/BUILD | 22 + rules/flags/flag_defs.bzl | 93 ++ rules/flags/flags.bzl | 255 +++ rules/idl.bzl | 264 +++ rules/intellij.bzl | 165 ++ rules/java.bzl | 454 ++++++ rules/migration_tag_DONOTUSE.bzl | 25 + rules/path.bzl | 102 ++ rules/processing_pipeline.bzl | 161 ++ rules/proguard.bzl | 110 ++ rules/providers.bzl | 149 ++ rules/res_v3_dummy_AndroidManifest.xml | 2 + rules/res_v3_dummy_R.txt | 1 + rules/resources.bzl | 1687 ++++++++++++++++++++ rules/robolectric_properties_template.txt | 5 + rules/rules.bzl | 60 + rules/toolchains/emulator/toolchain.bzl | 59 + rules/utils.bzl | 467 ++++++ src/validations/aar_import_checks/BUILD | 27 + test/rules/resources/BUILD | 4 + toolchains/android/BUILD | 57 + toolchains/android/toolchain.bzl | 203 +++ toolchains/android/zip.sh | 25 + toolchains/android_sdk/BUILD | 30 + toolchains/emulator/BUILD | 27 + tools/android/BUILD | 32 + tools/android/defs.bzl | 40 + tools/android/xslt/BUILD | 7 + tools/android/xslt/add_g3itr.xslt | 7 + tools/android/xslt/xslt.sh | 17 + tools/jdk/BUILD | 17 + 122 files changed, 11437 insertions(+) create mode 100644 .bazelci/postsubmit.yml create mode 100644 AUTHORS create mode 100644 BUILD create mode 100644 CONTRIBUTING.md create mode 100644 CONTRIBUTORS create mode 100644 LICENSE create mode 100644 README.md create mode 100644 WORKSPACE create mode 100644 defs.bzl create mode 100644 rules/BUILD create mode 100644 rules/aapt.bzl create mode 100644 rules/aar_import/BUILD create mode 100644 rules/aar_import/attrs.bzl create mode 100644 rules/aar_import/impl.bzl create mode 100644 rules/aar_import/rule.bzl create mode 100644 rules/acls.bzl create mode 100644 rules/acls/BUILD create mode 100644 rules/acls/aar_import_deps_checker.bzl create mode 100644 rules/acls/aar_import_explicit_exports_manifest.bzl create mode 100644 rules/acls/aar_import_exports_r_java.bzl create mode 100644 rules/acls/aar_propagate_resources.bzl create mode 100644 rules/acls/ait_install_snapshots.bzl create mode 100644 rules/acls/ait_virtual_device.bzl create mode 100644 rules/acls/allow_resource_conflicts.bzl create mode 100644 rules/acls/android_archive_dogfood.bzl create mode 100644 rules/acls/android_archive_excluded_deps_denylist.bzl create mode 100644 rules/acls/android_binary_starlark_resources.bzl create mode 100644 rules/acls/android_build_stamping_rollout.bzl create mode 100644 rules/acls/android_device_plugin_rollout.bzl create mode 100644 rules/acls/android_feature_splits_dogfood.bzl create mode 100644 rules/acls/android_instrumentation_binary_starlark_resources.bzl create mode 100644 rules/acls/android_instrumentation_test_manifest_check_rollout.bzl create mode 100644 rules/acls/android_library_implicit_exports.bzl create mode 100644 rules/acls/android_library_resources_without_srcs.bzl create mode 100644 rules/acls/android_library_starlark_resource_outputs.bzl create mode 100644 rules/acls/android_library_starlark_resources.bzl create mode 100644 rules/acls/android_lint_checks_rollout.bzl create mode 100644 rules/acls/android_lint_rollout.bzl create mode 100644 rules/acls/android_test_lockdown.bzl create mode 100644 rules/acls/android_test_platform_rollout.bzl create mode 100644 rules/acls/android_test_starlark_resources.bzl create mode 100644 rules/acls/b122039567.bzl create mode 100644 rules/acls/b123854163.bzl create mode 100644 rules/acls/dex2oat_opts.bzl create mode 100644 rules/acls/fix_export_exporting_rollout.bzl create mode 100644 rules/acls/fix_resource_transitivity_rollout.bzl create mode 100644 rules/acls/host_dex2oat_rollout.bzl create mode 100644 rules/acls/install_apps_in_data.bzl create mode 100644 rules/acls/kt_android_library_rollout.bzl create mode 100644 rules/acls/library_rollout.bzl create mode 100644 rules/acls/library_rollout_override.bzl create mode 100644 rules/acls/lint_registry_rollout.bzl create mode 100644 rules/acls/local_test_multi_proto.bzl create mode 100644 rules/acls/local_test_rollout.bzl create mode 100644 rules/acls/local_test_starlark_resources.bzl create mode 100644 rules/acls/nitrogen_test_runner_rollout.bzl create mode 100644 rules/acls/partial_jetification_targets.bzl create mode 100644 rules/acls/sourceless_binary_rollout.bzl create mode 100644 rules/acls/starlark_resources_diff.bzl create mode 100644 rules/acls/test_to_instrument_test_rollout.bzl create mode 100644 rules/acls/use_classic_desugar.bzl create mode 100644 rules/android_application/BUILD create mode 100644 rules/android_application/android_application.bzl create mode 100644 rules/android_application/android_application_rule.bzl create mode 100644 rules/android_application/android_feature_module.bzl create mode 100644 rules/android_application/android_feature_module_rule.bzl create mode 100644 rules/android_application/attrs.bzl create mode 100644 rules/android_application/bundle_deploy.sh_template create mode 100644 rules/android_application/feature_module_validation.sh create mode 100644 rules/android_application/gen_android_feature_manifest.sh create mode 100644 rules/android_application/gen_priority_android_feature_manifest.sh create mode 100644 rules/android_binary.bzl create mode 100644 rules/android_binary_internal/BUILD create mode 100644 rules/android_binary_internal/attrs.bzl create mode 100644 rules/android_binary_internal/impl.bzl create mode 100644 rules/android_binary_internal/rule.bzl create mode 100644 rules/android_library/BUILD create mode 100644 rules/android_library/attrs.bzl create mode 100644 rules/android_library/impl.bzl create mode 100644 rules/android_library/rule.bzl create mode 100644 rules/android_local_test.bzl create mode 100644 rules/android_ndk_repository.bzl create mode 100644 rules/android_sdk.bzl create mode 100644 rules/android_sdk_repository.bzl create mode 100644 rules/android_tools_defaults_jar.bzl create mode 100644 rules/attrs.bzl create mode 100644 rules/bundletool.bzl create mode 100644 rules/busybox.bzl create mode 100644 rules/common.bzl create mode 100644 rules/data_binding.bzl create mode 100644 rules/data_binding_annotation_template.txt create mode 100644 rules/flags/BUILD create mode 100644 rules/flags/flag_defs.bzl create mode 100644 rules/flags/flags.bzl create mode 100644 rules/idl.bzl create mode 100644 rules/intellij.bzl create mode 100644 rules/java.bzl create mode 100644 rules/migration_tag_DONOTUSE.bzl create mode 100644 rules/path.bzl create mode 100644 rules/processing_pipeline.bzl create mode 100644 rules/proguard.bzl create mode 100644 rules/providers.bzl create mode 100644 rules/res_v3_dummy_AndroidManifest.xml create mode 100644 rules/res_v3_dummy_R.txt create mode 100644 rules/resources.bzl create mode 100644 rules/robolectric_properties_template.txt create mode 100644 rules/rules.bzl create mode 100644 rules/toolchains/emulator/toolchain.bzl create mode 100644 rules/utils.bzl create mode 100644 src/validations/aar_import_checks/BUILD create mode 100644 test/rules/resources/BUILD create mode 100644 toolchains/android/BUILD create mode 100644 toolchains/android/toolchain.bzl create mode 100755 toolchains/android/zip.sh create mode 100644 toolchains/android_sdk/BUILD create mode 100644 toolchains/emulator/BUILD create mode 100644 tools/android/BUILD create mode 100644 tools/android/defs.bzl create mode 100644 tools/android/xslt/BUILD create mode 100644 tools/android/xslt/add_g3itr.xslt create mode 100755 tools/android/xslt/xslt.sh create mode 100644 tools/jdk/BUILD diff --git a/.bazelci/postsubmit.yml b/.bazelci/postsubmit.yml new file mode 100644 index 0000000..c1218a6 --- /dev/null +++ b/.bazelci/postsubmit.yml @@ -0,0 +1,22 @@ +--- +platforms: + ubuntu1604: + build_targets: + - "//..." + # test_targets: + # - "//..." + ubuntu1804: + build_targets: + - "//..." + # test_targets: + # - "//..." + macos: + build_targets: + - "//..." + # test_targets: + # - "//..." + windows: + build_targets: + - "//..." + # test_targets: + # - "//..." diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..dc35af2 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,9 @@ +# This the official list of authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as: +# Name or Organization +# The email address is not required for organizations. + +Google LLC diff --git a/BUILD b/BUILD new file mode 100644 index 0000000..e69de29 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..99da6f8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,39 @@ +Want to contribute? Great! First, read this page (including the small print at +the end). + +### Before you contribute +**Before we can use your code, you must sign the +[Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1) +(CLA)**, which you can do online. + +The CLA is necessary mainly because you own the copyright to your changes, +even after your contribution becomes part of our codebase, so we need your +permission to use and distribute your code. We also need to be sure of +various other things — for instance that you'll tell us if you know that +your code infringes on other people's patents. You don't have to sign +the CLA until after you've submitted your code for review and a member has +approved it, but you must do it before we can put your code into our codebase. + +### The small print +Contributions made by corporations are covered by a different agreement than +the one above, the +[Software Grant and Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate). + +### Contribution process + +1. Explain your idea and discuss your plan with members of the team. The best + way to do this is to create + an [issue](https://github.com/bazelbuild/rules_android/issues) or comment on + an existing issue. +1. Prepare a git commit with your change. Don't forget to + add [tests](https://github.com/bazelbuild/rules_android/tree/master/tests). + Run the existing tests with `bazel test //...`. Update + [README.md](https://github.com/bazelbuild/rules_android/blob/master/README.md) + if appropriate. +1. [Create a pull request](https://help.github.com/articles/creating-a-pull-request/). + This will start the code review process. **All submissions, including + submissions by project members, require review.** +1. You may be asked to make some changes. You'll also need to sign the CLA at + this point, if you haven't done so already. Our continuous integration bots + will test your change automatically on supported platforms. Once everything + looks good, your change will be merged. diff --git a/CONTRIBUTORS b/CONTRIBUTORS new file mode 100644 index 0000000..8fc0e51 --- /dev/null +++ b/CONTRIBUTORS @@ -0,0 +1,16 @@ +# People who have agreed to one of the CLAs and can contribute patches. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# https://developers.google.com/open-source/cla/individual +# https://developers.google.com/open-source/cla/corporate +# +# Names should be added to this file as: +# Name +Daniel Whang +Mauricio Galindo +Stefan Ramsauer +Tim Peut +Alex Humesky +Jingwen Chen diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1d3ffbf --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# Android support in Bazel + +## Disclaimer + +NOTE: This branch contains a development preview of the Starlark implementation of Android rules for Bazel. This code is incomplete and may not function as-is. + +A version of Bazel built at or near head and the following flags are necessary to use these rules: +``` +--experimental_enable_android_migration_apis +--experimental_google_legacy_api +--incompatible_java_common_parameters +--android_databinding_use_v3_4_args +--experimental_android_databinding_v2 +``` + +## Overview + +This repository contains the Starlark implementation of Android rules in Bazel. + +The rules are being incrementally converted from their native implementations +in the [Bazel source +tree](https://source.bazel.build/bazel/+/master:src/main/java/com/google/devtools/build/lib/rules/android/). + +For the list of Android rules, see the Bazel [documentation](https://docs.bazel.build/versions/master/be/android.html). + +## Getting Started +To use the new Bazel Android rules, add the following to your WORKSPACE file: + + load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + http_archive( + name = "build_bazel_rules_android", + urls = ["https://github.com/bazelbuild/rules_android/archive/refs/heads/pre-alpha.zip"], + strip_prefix = "rules_android-pre-alpha", + ) + load("@build_bazel_rules_android//:defs.bzl", "rules_android_workspace") + rules_android_workspace() + + register_toolchains( + "@build_bazel_rules_android//toolchains/android:android_default_toolchain", + "@build_bazel_rules_android//toolchains/android_sdk:android_sdk_tools", + ) + + +Then, in your BUILD files, import and use the rules: + + load("@build_bazel_rules_android//rules:rules.bzl", "android_library") + android_library( + ... + ) diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 0000000..9c8339a --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,3 @@ +workspace(name = "build_bazel_rules_android") + +register_toolchains("//android/toolchains/emulator:all") diff --git a/defs.bzl b/defs.bzl new file mode 100644 index 0000000..4d08708 --- /dev/null +++ b/defs.bzl @@ -0,0 +1,31 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Workspace setup macro for rules_android.""" + +load("@rules_jvm_external//:defs.bzl", "maven_install") + +def rules_android_workspace(): + """ Sets up workspace dependencies for rules_android.""" + + maven_install( + name = "rules_android_maven", + artifacts = [ + "com.android.tools.build:bundletool:1.6.1", + ], + repositories = [ + "https://maven.google.com", + "https://repo1.maven.org/maven2", + ], + ) diff --git a/rules/BUILD b/rules/BUILD new file mode 100644 index 0000000..ac89362 --- /dev/null +++ b/rules/BUILD @@ -0,0 +1,12 @@ +exports_files([ + "data_binding_annotation_template.txt", + "res_v3_dummy_AndroidManifest.xml", + "res_v3_dummy_R.txt", + "robolectric_properties_template.txt", +]) + +alias( + name = "ResourceProcessorBusyBox", + actual = "@bazel_tools//tools/android:busybox", + visibility = ["//visibility:public"], +) diff --git a/rules/aapt.bzl b/rules/aapt.bzl new file mode 100644 index 0000000..284d89a --- /dev/null +++ b/rules/aapt.bzl @@ -0,0 +1,200 @@ +# Copyright 2019 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel AAPT Commands.""" + +def _link( + ctx, + out_r_java, + out_resource_apk, + manifest = None, + java_package = None, + assets = depset([]), + assets_dirs = [], + compiled_resources = depset([]), + config_filters = [], + make_r_java_ids_non_final = False, + compatible_with_resource_shrinking = True, + enable_debug = False, + enable_static_lib = False, + android_jar = None, + aapt = None): + """Links compiled Android Resources with AAPT. + + Args: + ctx: The context. + out_r_java: A File. The R.java outputted by linking resources. + out_resource_apk: A File. The Resource APK outputted by linking resources. + manifest: A File. The AndroidManifest.xml. + java_package: A string. The Java package for the generated R.java. + assets: A list of Files. The list of assets from the transitive closure of + the project. + assets_dirs: A list of strings. The list of asset directories in the + transitive closure of the project. + compiled_resources: List of intermediate compiled android resource files. + config_filters: A list of Strings. The configuration filters. + make_r_java_ids_non_final: A bool. Makes the R.java produced from linkin + have non-final values. + compatible_with_resource_shrinking: A bool. When enabled produces the + output in proto format which is a requirement for resource shrinking. + enable_debug: A bool. Enable debugging + enable_static_lib: A bool. Enable static lib. + android_jar: A File. The Android Jar. + aapt: A FilesToRunProvider. The AAPT executable. + """ + + # Output the list of resources in reverse topological order. + resources_param = ctx.actions.declare_file( + out_r_java.basename + ".params", + sibling = out_r_java, + ) + args = ctx.actions.args() + args.use_param_file("%s", use_always = True) + args.set_param_file_format("multiline") + args.add_all(compiled_resources, expand_directories = True) + ctx.actions.run_shell( + command = """ +# Reverses the set of inputs that have been topologically ordered to utilize the +# overlay/override semantics of aapt2. +set -e + +echo $(tac $1) > $2 +""", + arguments = [args, resources_param.path], + outputs = [resources_param], + inputs = compiled_resources, + ) + + args = ctx.actions.args() + args.add("link") + if enable_static_lib: + args.add("--static-lib") + args.add("--no-version-vectors") + args.add("--no-static-lib-packages") # Turn off namespaced resource + + args.add("--manifest", manifest) + args.add("--auto-add-overlay") # Enables resource redefinition and merging + args.add("--override-styles-instead-of-overlaying") # mimic AAPT1. + if make_r_java_ids_non_final: + args.add("--non-final-ids") + if compatible_with_resource_shrinking: + args.add("--proto-format") + if enable_debug: + args.add("--debug-mode") + args.add("--custom-package", java_package) + args.add("-I", android_jar) + args.add_all(assets_dirs, before_each = "-A") + args.add("-R", resources_param, format = "@%s") + args.add("-0", ".apk") + args.add_joined("-c", config_filters, join_with = ",", omit_if_empty = True) + args.add("--java", out_r_java.path.rpartition(java_package.replace(".", "/"))[0]) + args.add("-o", out_resource_apk) + + ctx.actions.run( + executable = aapt, + arguments = [args], + inputs = depset( + [android_jar, resources_param] + + ([manifest] if manifest else []), + transitive = [assets, compiled_resources], + ), + outputs = [out_resource_apk, out_r_java], + mnemonic = "LinkAndroidResources", + progress_message = "ResV3 Linking Android Resources to %s" % out_resource_apk.short_path, + ) + +def _compile( + ctx, + out_dir, + resource_files, + aapt): + """Compile and store resources in a single archive. + + Args: + ctx: The context. + out_dir: File. A file to store the output. + resource_files: A list of Files. The list of resource files or directories + to process. + aapt: AAPT. Tool for compiling resources. + """ + if not out_dir: + fail("No output directory specified.") + if not out_dir.is_directory: + fail("Output directory is not a directory artifact.") + if not resource_files: + fail("No resource files given.") + + # Retrieves the list of files at runtime when a directory is passed. + args = ctx.actions.args() + args.add_all(resource_files, expand_directories = True) + + ctx.actions.run_shell( + command = """ +set -e + +AAPT=%s +OUT_DIR=%s +RESOURCE_FILES=$@ + +i=0 +declare -A out_dir_map +for f in ${RESOURCE_FILES}; do + res_dir="$(dirname $(dirname ${f}))" + if [ -z "${out_dir_map[${res_dir}]}" ]; then + out_dir="${OUT_DIR}/$((++i))" + mkdir -p ${out_dir} + out_dir_map[${res_dir}]="${out_dir}" + fi + # Outputs from multiple directories can overwrite the outputs. As we do not + # control the outputs for now store each in its own sub directory which will be + # captured by the over_dir. + # TODO(b/139757260): Re-evaluate this one compile per file or multiple and zip + # merge. + "${AAPT}" compile --legacy "${f}" -o "${out_dir_map[${res_dir}]}" +done +""" % (aapt.executable.path, out_dir.path), + tools = [aapt], + arguments = [args], + inputs = resource_files, + outputs = [out_dir], + mnemonic = "CompileAndroidResources", + progress_message = "ResV3 Compiling Android Resources in %s" % out_dir, + ) + +def _convert( + ctx, + out = None, + input = None, + to_proto = False, + aapt = None): + args = ctx.actions.args() + args.add("convert") + args.add("--output-format", ("proto" if to_proto else "binary")) + args.add("-o", out) + args.add(input) + + ctx.actions.run( + executable = aapt, + arguments = [args], + inputs = [input], + outputs = [out], + mnemonic = "AaptConvert", + progress_message = "ResV3 Convert to %s" % out.short_path, + ) + +aapt = struct( + link = _link, + compile = _compile, + convert = _convert, +) diff --git a/rules/aar_import/BUILD b/rules/aar_import/BUILD new file mode 100644 index 0000000..50d41fe --- /dev/null +++ b/rules/aar_import/BUILD @@ -0,0 +1,21 @@ +# The aar_import rule. + +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") + +licenses(["notice"]) + +exports_files(["rule.bzl"]) + +filegroup( + name = "all_files", + srcs = glob(["**"]), +) + +bzl_library( + name = "bzl", + srcs = glob(["*.bzl"]), + deps = [ + "@rules_android//rules:common_bzl", + "@rules_android//rules/flags:bzl", + ], +) diff --git a/rules/aar_import/attrs.bzl b/rules/aar_import/attrs.bzl new file mode 100644 index 0000000..5ac9c7a --- /dev/null +++ b/rules/aar_import/attrs.bzl @@ -0,0 +1,64 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Attributes.""" + +load( + "@rules_android//rules:attrs.bzl", + _attrs = "attrs", +) + +ATTRS = _attrs.add( + dict( + aar = attr.label( + allow_single_file = [".aar"], + mandatory = True, + ), + data = attr.label_list(allow_files = True), + deps = attr.label_list( + allow_files = False, + providers = [JavaInfo], + ), + exports = attr.label_list( + allow_files = False, + allow_rules = ["aar_import", "java_import"], + ), + has_lint_jar = attr.bool( + default = False, + doc = "Whether the aar contains a lint.jar. This is required to " + + "know at analysis time if a lint jar is included in the aar.", + ), + package = attr.string( + doc = "Package to use while processing the aar at analysis time. " + + "This needs to be the same value as the manifest's package.", + ), + srcjar = attr.label( + allow_single_file = [".srcjar"], + doc = + "A srcjar file that contains the source code for the JVM " + + "artifacts stored within the AAR.", + ), + _flags = attr.label( + default = "@rules_android//rules/flags", + ), + _java_toolchain = attr.label( + default = Label("//tools/jdk:toolchain_android_only"), + ), + _host_javabase = attr.label( + cfg = "host", + default = Label("//tools/jdk:current_java_runtime"), + ), + ), + _attrs.DATA_CONTEXT, +) diff --git a/rules/aar_import/impl.bzl b/rules/aar_import/impl.bzl new file mode 100644 index 0000000..280060e --- /dev/null +++ b/rules/aar_import/impl.bzl @@ -0,0 +1,578 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Implementation.""" + +load( + "@rules_android//rules:acls.bzl", + _acls = "acls", +) +load( + "@rules_android//rules:common.bzl", + _common = "common", +) +load("@rules_android//rules:intellij.bzl", "intellij") +load( + "@rules_android//rules:java.bzl", + _java = "java", +) +load("@rules_android//rules:providers.bzl", "AndroidLintRulesInfo") +load( + "@rules_android//rules:resources.bzl", + _resources = "resources", +) +load( + "@rules_android//rules:utils.bzl", + _get_android_toolchain = "get_android_toolchain", + _utils = "utils", +) + +RULE_PREFIX = "_aar" +ANDROID_MANIFEST = "AndroidManifest.xml" +LINT_JAR = "lint.jar" +_UNEXPECTED_LINT_JAR_ERROR = ( + "In target %s, has_lint_jar attribute is required when the aar contains " + + "a lint.jar file." +) + +# Resources context dict fields. +_PROVIDERS = "providers" +_VALIDATION_RESULTS = "validation_results" + +def _create_aar_artifact(ctx, name): + return ctx.actions.declare_file("%s/%s/%s" % (RULE_PREFIX, ctx.label.name, name)) + +def _create_aar_tree_artifact(ctx, name): + return ctx.actions.declare_directory("%s/unzipped/%s/%s" % (RULE_PREFIX, name, ctx.label.name)) + +# Create an action to extract a file (specified by the parameter filename) from an AAR file. +def _extract_single_file( + ctx, + out_file, + aar, + filename, + unzip_tool): + args = ctx.actions.args() + args.add(aar) + args.add(filename) + args.add("-d", out_file.dirname) + + ctx.actions.run( + executable = unzip_tool, + arguments = [args], + inputs = [aar], + outputs = [out_file], + mnemonic = "AarFileExtractor", + progress_message = "Extracting %s from %s" % (filename, aar.basename), + ) + +def _extract_resources( + ctx, + out_resources_dir, + out_assets_dir, + aar, + aar_resources_extractor_tool): + args = ctx.actions.args() + args.add("--input_aar", aar) + args.add("--output_res_dir", out_resources_dir.path) + args.add("--output_assets_dir", out_assets_dir.path) + ctx.actions.run( + executable = aar_resources_extractor_tool, + arguments = [args], + inputs = [aar], + outputs = [out_resources_dir, out_assets_dir], + mnemonic = "AarResourcesExtractor", + progress_message = "Extracting resources and assets from %s" % aar.basename, + ) + +def _extract_native_libs( + ctx, + output_zip, + aar, + android_cpu, + aar_native_libs_zip_creator_tool): + args = ctx.actions.args() + args.add("--input_aar", aar) + args.add("--cpu", android_cpu) + args.add("--output_zip", output_zip) + ctx.actions.run( + executable = aar_native_libs_zip_creator_tool, + arguments = [args], + inputs = [aar], + outputs = [output_zip], + mnemonic = "AarNativeLibsFilter", + progress_message = "Filtering AAR native libs by architecture", + ) + +def _process_resources( + ctx, + aar, + package, + manifest, + deps, + aar_resources_extractor_tool, + unzip_tool): + # Extract resources and assets, if they exist. + resources = _create_aar_tree_artifact(ctx, "resources") + assets = _create_aar_tree_artifact(ctx, "assets") + _extract_resources( + ctx, + resources, + assets, + aar, + aar_resources_extractor_tool, + ) + + resources_ctx = _resources.process_starlark( + ctx, + manifest = manifest, + assets = [assets], + assets_dir = assets.path, + resource_files = [resources], + stamp_manifest = False, + deps = ctx.attr.deps, + exports = ctx.attr.exports, + exports_manifest = getattr(ctx.attr, "exports_manifest", True), + propagate_resources = _acls.in_aar_propagate_resources(str(ctx.label)), + + # Tool and Processing related inputs + aapt = _get_android_toolchain(ctx).aapt2.files_to_run, + android_jar = ctx.attr._android_sdk[AndroidSdkInfo].android_jar, + android_kit = _get_android_toolchain(ctx).android_kit.files_to_run, + busybox = _get_android_toolchain(ctx).android_resources_busybox.files_to_run, + java_toolchain = _common.get_java_toolchain(ctx), + host_javabase = _common.get_host_javabase(ctx), + instrument_xslt = _utils.only(_get_android_toolchain(ctx).add_g3itr_xslt.files.to_list()), + xsltproc = _get_android_toolchain(ctx).xsltproc_tool.files_to_run, + ) + + native_android_manifest = manifest + if not getattr(ctx.attr, "exports_manifest", True): + # Write an empty manifest, for the native pipeline. + native_android_manifest = ctx.actions.declare_file(ctx.label.name + "_aar/AndroidManifest.xml") + ctx.actions.write(native_android_manifest, content = """ + + +""" % package) + + + return struct(**resources_ctx) + +def _extract_jars( + ctx, + out_jars_tree_artifact, + out_jars_params_file, + aar, + aar_embedded_jars_extractor_tool): + args = ctx.actions.args() + args.add("--input_aar", aar) + args.add("--output_dir", out_jars_tree_artifact.path) + args.add("--output_singlejar_param_file", out_jars_params_file) + ctx.actions.run( + executable = aar_embedded_jars_extractor_tool, + arguments = [args], + inputs = [aar], + outputs = [out_jars_tree_artifact, out_jars_params_file], + mnemonic = "AarEmbeddedJarsExtractor", + progress_message = "Extracting classes.jar and libs/*.jar from %s" % aar.basename, + ) + +def _merge_jars( + ctx, + out_jar, + jars_tree_artifact, + jars_param_file, + single_jar_tool): + args = ctx.actions.args() + args.add("--output", out_jar) + args.add("--dont_change_compression") + args.add("--normalize") + args.add("@" + jars_param_file.path) + ctx.actions.run( + executable = single_jar_tool, + arguments = [args], + inputs = [jars_tree_artifact, jars_param_file], + outputs = [out_jar], + mnemonic = "AarJarsMerger", + progress_message = "Merging AAR embedded jars", + ) + +def _extract_and_merge_jars( + ctx, + out_jar, + aar, + aar_embedded_jars_extractor_tool, + single_jar_tool): + """Extracts all the Jars within the AAR and produces a single jar. + + An AAR may have multiple Jar files embedded within it. This method + extracts and merges all Jars. + """ + jars_tree_artifact = _create_aar_tree_artifact(ctx, "jars") + jars_params_file = _create_aar_artifact(ctx, "jar_merging_params") + _extract_jars( + ctx, + jars_tree_artifact, + jars_params_file, + aar, + aar_embedded_jars_extractor_tool, + ) + _merge_jars( + ctx, + out_jar, + jars_tree_artifact, + jars_params_file, + single_jar_tool, + ) + +def _create_import_deps_check( + ctx, + jars_to_check, + declared_deps, + transitive_deps, + bootclasspath, + jdeps_output, + import_deps_checker_tool, + host_javabase): + args = ctx.actions.args() + args.add_all(jars_to_check, before_each = "--input") + args.add_all(declared_deps, before_each = "--directdep") + args.add_all( + depset(order = "preorder", transitive = [declared_deps, transitive_deps]), + before_each = "--classpath_entry", + ) + args.add_all(bootclasspath, before_each = "--bootclasspath_entry") + args.add("--checking_mode=error") + args.add("--jdeps_output", jdeps_output) + args.add("--rule_label", ctx.label) + + _java.run( + ctx = ctx, + host_javabase = host_javabase, + executable = import_deps_checker_tool, + arguments = [args], + inputs = depset( + jars_to_check, + transitive = [ + declared_deps, + transitive_deps, + bootclasspath, + ], + ), + outputs = [jdeps_output], + mnemonic = "ImportDepsChecker", + progress_message = "Checking the completeness of the deps for %s" % jars_to_check, + ) + +def _process_jars( + ctx, + out_jar, + aar, + source_jar, + r_java, + deps, + exports, + enable_desugar_java8, + enable_imports_deps_check, + bootclasspath, + desugar_java8_extra_bootclasspath, + aar_embedded_jars_extractor_tool, + import_deps_checker_tool, + single_jar_tool, + java_toolchain, + host_javabase): + providers = [] + validation_results = [] + r_java_info = [r_java] if r_java else [] + + # An aar may have multple Jar files, extract and merge into a single jar. + _extract_and_merge_jars( + ctx, + out_jar, + aar, + aar_embedded_jars_extractor_tool, + single_jar_tool, + ) + + java_infos = deps + exports + + if enable_desugar_java8: + bootclasspath = depset(transitive = [ + desugar_java8_extra_bootclasspath, + bootclasspath, + ]) + + merged_java_info = java_common.merge(java_infos + r_java_info) + jdeps_artifact = _create_aar_artifact(ctx, "jdeps.proto") + _create_import_deps_check( + ctx, + [out_jar], + merged_java_info.compile_jars, + merged_java_info.transitive_compile_time_jars, + bootclasspath, + jdeps_artifact, + import_deps_checker_tool, + host_javabase, + ) + if enable_imports_deps_check: + validation_results.append(jdeps_artifact) + + java_info = JavaInfo( + out_jar, + compile_jar = java_common.stamp_jar( + actions = ctx.actions, + jar = out_jar, + target_label = ctx.label, + java_toolchain = java_toolchain, + ), + source_jar = source_jar, + neverlink = False, + deps = r_java_info + java_infos, # TODO(djwhang): Exports are not deps. + exports = + (r_java_info if _acls.in_aar_import_exports_r_java(str(ctx.label)) else []) + + java_infos, # TODO(djwhang): Deps are not exports. + # TODO(djwhang): AarImportTest is not expecting jdeps, enable or remove it completely + # jdeps = jdeps_artifact, + ) + providers.append(java_info) + + return struct( + java_info = java_info, + providers = providers, + validation_results = validation_results, + ) + +def _validate_rule( + ctx, + aar, + package, + manifest, + checks): + validation_output = ctx.actions.declare_file("%s_validation_output" % ctx.label.name) + + args = ctx.actions.args() + args.add("-aar", aar) + args.add("-label", str(ctx.label)) + args.add("-pkg", package) + args.add("-manifest", manifest) + if ctx.attr.has_lint_jar: + args.add("-has_lint_jar") + args.add("-output", validation_output) + + ctx.actions.run( + executable = checks, + arguments = [args], + inputs = [aar, manifest], + outputs = [validation_output], + mnemonic = "ValidateAAR", + progress_message = "Validating aar_import %s" % str(ctx.label), + ) + return validation_output + +def _process_lint_rules( + ctx, + aar, + unzip_tool): + providers = [] + + if ctx.attr.has_lint_jar: + lint_jar = _create_aar_artifact(ctx, LINT_JAR) + _extract_single_file( + ctx, + lint_jar, + aar, + LINT_JAR, + unzip_tool, + ) + providers.append(AndroidLintRulesInfo( + lint_jar = lint_jar, + )) + + providers.extend(_utils.collect_providers( + AndroidLintRulesInfo, + ctx.attr.exports, + )) + return providers + +def _collect_proguard( + ctx, + out_proguard, + aar, + aar_embedded_proguard_extractor): + args = ctx.actions.args() + args.add("--input_aar", aar) + args.add("--output_proguard_file", out_proguard) + ctx.actions.run( + executable = aar_embedded_proguard_extractor, + arguments = [args], + inputs = [aar], + outputs = [out_proguard], + mnemonic = "AarEmbeddedProguardExtractor", + progress_message = "Extracting proguard spec from %s" % aar.basename, + ) + transitive_proguard_specs = [] + for p in _utils.collect_providers(ProguardSpecProvider, ctx.attr.deps, ctx.attr.exports): + transitive_proguard_specs.append(p.specs) + return ProguardSpecProvider(depset([out_proguard], transitive = transitive_proguard_specs)) + +def impl(ctx): + """The rule implementation. + + Args: + ctx: The context. + + Returns: + A list of providers. + """ + providers = [] + validation_outputs = [] + + aar = _utils.only(ctx.files.aar) + unzip_tool = _get_android_toolchain(ctx).unzip_tool.files_to_run + package = _java.resolve_package_from_label(ctx.label, ctx.attr.package) + + # Extract the AndroidManifest.xml from the AAR. + android_manifest = _create_aar_artifact(ctx, ANDROID_MANIFEST) + _extract_single_file( + ctx, + android_manifest, + aar, + ANDROID_MANIFEST, + unzip_tool, + ) + + resources_ctx = _process_resources( + ctx, + aar = aar, + package = package, + manifest = android_manifest, + deps = ctx.attr.deps, + aar_resources_extractor_tool = + _get_android_toolchain(ctx).aar_resources_extractor.files_to_run, + unzip_tool = unzip_tool, + ) + providers.extend(resources_ctx.providers) + + merged_jar = _create_aar_artifact(ctx, "classes_and_libs_merged.jar") + jvm_ctx = _process_jars( + ctx, + out_jar = merged_jar, + aar = aar, + source_jar = ctx.file.srcjar, + deps = _utils.collect_providers(JavaInfo, ctx.attr.deps), + r_java = resources_ctx.r_java, + exports = _utils.collect_providers(JavaInfo, ctx.attr.exports), + enable_desugar_java8 = ctx.fragments.android.desugar_java8, + enable_imports_deps_check = + _acls.in_aar_import_deps_checker(str(ctx.label)), + aar_embedded_jars_extractor_tool = + _get_android_toolchain(ctx).aar_embedded_jars_extractor.files_to_run, + bootclasspath = + ctx.attr._java_toolchain[java_common.JavaToolchainInfo].bootclasspath, + desugar_java8_extra_bootclasspath = + _get_android_toolchain(ctx).desugar_java8_extra_bootclasspath.files, + import_deps_checker_tool = + _get_android_toolchain(ctx).import_deps_checker.files_to_run, + single_jar_tool = + ctx.attr._java_toolchain[java_common.JavaToolchainInfo].single_jar, + java_toolchain = + ctx.attr._java_toolchain[java_common.JavaToolchainInfo], + host_javabase = ctx.attr._host_javabase, + ) + providers.extend(jvm_ctx.providers) + validation_outputs.extend(jvm_ctx.validation_results) + + native_libs = _create_aar_artifact(ctx, "native_libs.zip") + _extract_native_libs( + ctx, + native_libs, + aar = aar, + android_cpu = ctx.fragments.android.android_cpu, + aar_native_libs_zip_creator_tool = + _get_android_toolchain(ctx).aar_native_libs_zip_creator.files_to_run, + ) + native_libs_infos = _utils.collect_providers( + AndroidNativeLibsInfo, + ctx.attr.deps, + ctx.attr.exports, + ) + providers.append( + AndroidNativeLibsInfo( + depset( + [native_libs], + transitive = [info.native_libs for info in native_libs_infos], + ), + ), + ) + + # Will be empty if there's no proguard.txt file in the aar + proguard_spec = _create_aar_artifact(ctx, "proguard.txt") + providers.append(_collect_proguard( + ctx, + proguard_spec, + aar, + _get_android_toolchain(ctx).aar_embedded_proguard_extractor.files_to_run, + )) + + lint_providers = _process_lint_rules( + ctx, + aar = aar, + unzip_tool = unzip_tool, + ) + providers.extend(lint_providers) + + validation_outputs.append(_validate_rule( + ctx, + aar = aar, + package = package, + manifest = android_manifest, + checks = _get_android_toolchain(ctx).aar_import_checks.files_to_run, + )) + + providers.append( + intellij.make_android_ide_info( + ctx, + java_package = _java.resolve_package_from_label(ctx.label, ctx.attr.package), + manifest = resources_ctx.merged_manifest, + defines_resources = resources_ctx.defines_resources, + merged_manifest = resources_ctx.merged_manifest, + resources_apk = resources_ctx.resources_apk, + r_jar = _utils.only(resources_ctx.r_java.outputs.jars) if resources_ctx.r_java else None, + java_info = jvm_ctx.java_info, + signed_apk = None, # signed_apk, always empty for aar_import + apks_under_test = [], # apks_under_test, always empty for aar_import + native_libs = dict(), # nativelibs, always empty for aar_import + idlclass = _get_android_toolchain(ctx).idlclass.files_to_run, + host_javabase = _common.get_host_javabase(ctx), + ), + ) + + providers.append(OutputGroupInfo(_validation = depset(validation_outputs))) + + # There isn't really any use case for building an aar_import target on its own, so the files to + # build could be empty. The R class JAR and merged JARs are added here as a sanity check for + # Bazel developers so that `bazel build java/com/my_aar_import` will fail if the resource + # processing or JAR merging steps fail. + files_to_build = [] + files_to_build.extend(resources_ctx.validation_results) # TODO(djwhang): This should be validation. + files_to_build.append(merged_jar) + + providers.append( + DefaultInfo( + files = depset(files_to_build), + runfiles = ctx.runfiles(), + ), + ) + + return providers diff --git a/rules/aar_import/rule.bzl b/rules/aar_import/rule.bzl new file mode 100644 index 0000000..02a1d61 --- /dev/null +++ b/rules/aar_import/rule.bzl @@ -0,0 +1,31 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""aar_import rule.""" + +load(":attrs.bzl", _ATTRS = "ATTRS") +load(":impl.bzl", _impl = "impl") + +aar_import = rule( + attrs = _ATTRS, + fragments = ["android"], + implementation = _impl, + provides = [ + AndroidIdeInfo, + AndroidLibraryResourceClassJarProvider, + AndroidNativeLibsInfo, + JavaInfo, + ], + toolchains = ["@rules_android//toolchains/android:toolchain_type"], +) diff --git a/rules/acls.bzl b/rules/acls.bzl new file mode 100644 index 0000000..96dd8e4 --- /dev/null +++ b/rules/acls.bzl @@ -0,0 +1,333 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Access Control Lists. + +To create a new list: + 1. Create new .bzl file in the acls directory with a list of targets. + 2. Create matching method in this file. + 3. Add matching method to struct. + +To check an ACL: + 1. Import the `acls` struct. + 2. Check `acls.list_name(fqn)` using the //fully/qualified:target + +To update a list: + 1. Directly add/remove/edit targets in the appropriate .bzl file +""" + +load("@rules_android//rules/acls:aar_import_deps_checker.bzl", "AAR_IMPORT_DEPS_CHECKER_FALLBACK", "AAR_IMPORT_DEPS_CHECKER_ROLLOUT") +load("@rules_android//rules/acls:aar_import_explicit_exports_manifest.bzl", "AAR_IMPORT_EXPLICIT_EXPORTS_MANIFEST") +load("@rules_android//rules/acls:aar_import_exports_r_java.bzl", "AAR_IMPORT_EXPORTS_R_JAVA") +load("@rules_android//rules/acls:aar_propagate_resources.bzl", "AAR_PROPAGATE_RESOURCES_FALLBACK", "AAR_PROPAGATE_RESOURCES_ROLLOUT") +load("@rules_android//rules/acls:ait_install_snapshots.bzl", "APP_INSTALLATION_SNAPSHOT", "APP_INSTALLATION_SNAPSHOT_FALLBACK") +load("@rules_android//rules/acls:ait_virtual_device.bzl", "AIT_VIRTUAL_DEVICE_FALLBACK", "AIT_VIRTUAL_DEVICE_ROLLOUT") +load("@rules_android//rules/acls:allow_resource_conflicts.bzl", "ALLOW_RESOURCE_CONFLICTS") +load("@rules_android//rules/acls:android_archive_dogfood.bzl", "ANDROID_ARCHIVE_DOGFOOD") +load("@rules_android//rules/acls:android_archive_excluded_deps_denylist.bzl", "ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST") +load("@rules_android//rules/acls:android_test_lockdown.bzl", "ANDROID_TEST_LOCKDOWN_GENERATOR_FUNCTIONS", "ANDROID_TEST_LOCKDOWN_TARGETS") +load("@rules_android//rules/acls:android_device_plugin_rollout.bzl", "ANDROID_DEVICE_PLUGIN_FALLBACK", "ANDROID_DEVICE_PLUGIN_ROLLOUT") +load("@rules_android//rules/acls:android_instrumentation_binary_starlark_resources.bzl", "ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK", "ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT") +load("@rules_android//rules/acls:android_feature_splits_dogfood.bzl", "ANDROID_FEATURE_SPLITS_DOGFOOD") +load("@rules_android//rules/acls:android_library_implicit_exports.bzl", "ANDROID_LIBRARY_IMPLICIT_EXPORTS", "ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS") +load("@rules_android//rules/acls:android_library_resources_without_srcs.bzl", "ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS", "ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS") +load("@rules_android//rules/acls:android_library_starlark_resource_outputs.bzl", "ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK", "ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT") +load("@rules_android//rules/acls:android_lint_checks_rollout.bzl", "ANDROID_LINT_CHECKS_FALLBACK", "ANDROID_LINT_CHECKS_ROLLOUT") +load("@rules_android//rules/acls:android_lint_rollout.bzl", "ANDROID_LINT_FALLBACK", "ANDROID_LINT_ROLLOUT") +load("@rules_android//rules/acls:lint_registry_rollout.bzl", "LINT_REGISTRY_FALLBACK", "LINT_REGISTRY_ROLLOUT") +load("@rules_android//rules/acls:android_build_stamping_rollout.bzl", "ANDROID_BUILD_STAMPING_FALLBACK", "ANDROID_BUILD_STAMPING_ROLLOUT") +load("@rules_android//rules/acls:b122039567.bzl", "B122039567") +load("@rules_android//rules/acls:b123854163.bzl", "B123854163") +load("@rules_android//rules/acls:dex2oat_opts.bzl", "CAN_USE_DEX2OAT_OPTIONS") +load("@rules_android//rules/acls:fix_export_exporting_rollout.bzl", "FIX_EXPORT_EXPORTING_FALLBACK", "FIX_EXPORT_EXPORTING_ROLLOUT") +load("@rules_android//rules/acls:fix_resource_transitivity_rollout.bzl", "FIX_RESOURCE_TRANSITIVITY_FALLBACK", "FIX_RESOURCE_TRANSITIVITY_ROLLOUT") +load("@rules_android//rules/acls:host_dex2oat_rollout.bzl", "AIT_USE_HOST_DEX2OAT_ROLLOUT", "AIT_USE_HOST_DEX2OAT_ROLLOUT_FALLBACK") +load("@rules_android//rules/acls:install_apps_in_data.bzl", "INSTALL_APPS_IN_DATA") +load("@rules_android//rules/acls:local_test_multi_proto.bzl", "LOCAL_TEST_MULTI_PROTO_PKG") +load("@rules_android//rules/acls:local_test_rollout.bzl", "LOCAL_TEST_FALLBACK", "LOCAL_TEST_ROLLOUT") +load("@rules_android//rules/acls:local_test_starlark_resources.bzl", "LOCAL_TEST_STARLARK_RESOURCES_FALLBACK", "LOCAL_TEST_STARLARK_RESOURCES_ROLLOUT") +load("@rules_android//rules/acls:android_test_platform_rollout.bzl", "ANDROID_TEST_PLATFORM_FALLBACK", "ANDROID_TEST_PLATFORM_ROLLOUT") +load("@rules_android//rules/acls:sourceless_binary_rollout.bzl", "SOURCELESS_BINARY_FALLBACK", "SOURCELESS_BINARY_ROLLOUT") +load("@rules_android//rules/acls:test_to_instrument_test_rollout.bzl", "TEST_TO_INSTRUMENT_TEST_FALLBACK", "TEST_TO_INSTRUMENT_TEST_ROLLOUT") +load( + "@rules_android//rules/acls:partial_jetification_targets.bzl", + "PARTIAL_JETIFICATION_TARGETS_FALLBACK", + "PARTIAL_JETIFICATION_TARGETS_ROLLOUT", +) +load("@rules_android//rules/acls:kt_android_library_rollout.bzl", "KT_ANDROID_LIBRARY_FALLBACK", "KT_ANDROID_LIBRARY_ROLLOUT") +load("@rules_android//rules/acls:android_instrumentation_test_manifest_check_rollout.bzl", "ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK", "ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT") + +def _in_aar_import_deps_checker(fqn): + return not _matches(fqn, AAR_IMPORT_DEPS_CHECKER_FALLBACK_DICT) and _matches(fqn, AAR_IMPORT_DEPS_CHECKER_ROLLOUT_DICT) + +def _in_aar_import_explicit_exports_manifest(fqn): + return _matches(fqn, AAR_IMPORT_EXPLICIT_EXPORTS_MANIFEST_DICT) + +def _in_aar_import_exports_r_java(fqn): + return _matches(fqn, AAR_IMPORT_EXPORTS_R_JAVA_DICT) + +def _in_aar_propagate_resources(fqn): + return not _matches(fqn, AAR_PROPAGATE_RESOURCES_FALLBACK_DICT) and _matches(fqn, AAR_PROPAGATE_RESOURCES_ROLLOUT_DICT) + +def _in_ait_virtual_device(fqn): + return not _matches(fqn, AIT_VIRTUAL_DEVICE_FALLBACK_DICT) and _matches(fqn, AIT_VIRTUAL_DEVICE_ROLLOUT_DICT) + +def _in_android_archive_dogfood(fqn): + return _matches(fqn, ANDROID_ARCHIVE_DOGFOOD_DICT) + +def _in_android_archive_excluded_deps_denylist(fqn): + return _matches(fqn, ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST_DICT) + +def _in_android_device_plugin_rollout(fqn): + return not _matches(fqn, ANDROID_DEVICE_PLUGIN_FALLBACK_DICT) and _matches(fqn, ANDROID_DEVICE_PLUGIN_ROLLOUT_DICT) + +def _in_android_instrumentation_binary_starlark_resources(fqn): + return not _matches(fqn, ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK_DICT) and _matches(fqn, ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT_DICT) + +def _in_android_feature_splits_dogfood(fqn): + return _matches(fqn, ANDROID_FEATURE_SPLITS_DOGFOOD_DICT) + +def _in_android_lint_checks_rollout(fqn): + return not _matches(fqn, ANDROID_LINT_CHECKS_FALLBACK_DICT) and _matches(fqn, ANDROID_LINT_CHECKS_ROLLOUT_DICT) + +def _in_android_lint_rollout(fqn): + return not _matches(fqn, ANDROID_LINT_FALLBACK_DICT) and _matches(fqn, ANDROID_LINT_ROLLOUT_DICT) + +def _in_lint_registry_rollout(fqn): + return not _matches(fqn, LINT_REGISTRY_FALLBACK_DICT) and _matches(fqn, LINT_REGISTRY_ROLLOUT_DICT) + +def _in_android_build_stamping_rollout(fqn): + return not _matches(fqn, ANDROID_BUILD_STAMPING_FALLBACK_DICT) and _matches(fqn, ANDROID_BUILD_STAMPING_ROLLOUT_DICT) + +def _in_android_test_lockdown_allowlist(fqn, generator): + if generator == "android_test": + return _matches(fqn, ANDROID_TEST_LOCKDOWN_TARGETS) + return generator in ANDROID_TEST_LOCKDOWN_GENERATOR_FUNCTIONS_DICT + +def _in_b122039567(fqn): + return _matches(fqn, B122039567_DICT) + +def _in_b123854163(fqn): + return _matches(fqn, B123854163_DICT) + +def _in_android_library_implicit_exports(fqn): + return _matches(fqn, ANDROID_LIBRARY_IMPLICIT_EXPORTS_DICT) + +def _in_android_library_implicit_exports_generator_functions(gfn): + return gfn in ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS_DICT + +def _in_android_library_resources_without_srcs(fqn): + return _matches(fqn, ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_DICT) + +def _in_android_library_resources_without_srcs_generator_functions(gfn): + return gfn in ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS_DICT + +def _in_android_library_starlark_resource_outputs_rollout(fqn): + return not _matches(fqn, ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK_DICT) and _matches(fqn, ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT_DICT) + +def _in_app_installation_snapshot(fqn): + return not _matches(fqn, APP_INSTALLATION_SNAPSHOT_FALLBACK_DICT) and _matches(fqn, APP_INSTALLATION_SNAPSHOT_DICT) + +def _in_dex2oat_opts(fqn): + return _matches(fqn, CAN_USE_DEX2OAT_OPTIONS_DICT) + +def _in_fix_export_exporting_rollout(fqn): + return not _matches(fqn, FIX_EXPORT_EXPORTING_FALLBACK_DICT) and _matches(fqn, FIX_EXPORT_EXPORTING_ROLLOUT_DICT) + +def _in_fix_resource_transivity_rollout(fqn): + return not _matches(fqn, FIX_RESOURCE_TRANSIVITY_FALLBACK_DICT) and _matches(fqn, FIX_RESOURCE_TRANSIVITY_ROLLOUT_DICT) + +def _in_host_dex2oat_rollout(fqn): + return not _matches(fqn, AIT_USE_HOST_DEX2OAT_ROLLOUT_FALLBACK_DICT) and _matches(fqn, AIT_USE_HOST_DEX2OAT_ROLLOUT_DICT) + +def _in_install_apps_in_data(fqn): + return _matches(fqn, AIT_INSTALL_APPS_IN_DATA_DICT) + +def _in_local_test_multi_proto(fqn): + return _matches(fqn, LOCAL_TEST_MULTI_PROTO_PKG_DICT) + +def _in_local_test_rollout(fqn): + return not _matches(fqn, LOCAL_TEST_FALLBACK_DICT) and _matches(fqn, LOCAL_TEST_ROLLOUT_DICT) + +def _in_local_test_starlark_resources(fqn): + return not _matches(fqn, LOCAL_TEST_STARLARK_RESOURCES_FALLBACK_DICT) and _matches(fqn, LOCAL_TEST_STARLARK_RESOURCES_ROLLOUT_DICT) + +def _in_android_test_platform_rollout(fqn): + return not _matches(fqn, ANDROID_TEST_PLATFORM_FALLBACK_DICT) and _matches(fqn, ANDROID_TEST_PLATFORM_ROLLOUT_DICT) + +def _in_sourceless_binary_rollout(fqn): + return not _matches(fqn, SOURCELESS_BINARY_FALLBACK_DICT) and _matches(fqn, SOURCELESS_BINARY_ROLLOUT_DICT) + +def _in_test_to_instrument_test_rollout(fqn): + return not _matches(fqn, TEST_TO_INSTRUMENT_TEST_FALLBACK_DICT) and _matches(fqn, TEST_TO_INSTRUMENT_TEST_ROLLOUT_DICT) + +def _in_allow_resource_conflicts(fqn): + return _matches(fqn, ALLOW_RESOURCE_CONFLICTS_DICT) + +def _in_partial_jetification_targets(fqn): + return not _matches(fqn, PARTIAL_JETIFICATION_TARGETS_FALLBACK_DICT) and _matches(fqn, PARTIAL_JETIFICATION_TARGETS_ROLLOUT_DICT) + +def _in_kt_android_library_rollout(fqn): + return not _matches(fqn, KT_ANDROID_LIBRARY_FALLBACK_DICT) and _matches(fqn, KT_ANDROID_LIBRARY_ROLLOUT_DICT) + +def _in_android_instrumentation_test_manifest_check_rollout(fqn): + return not _matches(fqn, ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK_DICT) and _matches(fqn, ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT_DICT) + +def _make_dict(lst): + """Do not use this method outside of this file.""" + return {t: True for t in lst} + +AAR_IMPORT_DEPS_CHECKER_FALLBACK_DICT = _make_dict(AAR_IMPORT_DEPS_CHECKER_FALLBACK) +AAR_IMPORT_DEPS_CHECKER_ROLLOUT_DICT = _make_dict(AAR_IMPORT_DEPS_CHECKER_ROLLOUT) +AAR_IMPORT_EXPLICIT_EXPORTS_MANIFEST_DICT = _make_dict(AAR_IMPORT_EXPLICIT_EXPORTS_MANIFEST) +AAR_IMPORT_EXPORTS_R_JAVA_DICT = _make_dict(AAR_IMPORT_EXPORTS_R_JAVA) +AAR_PROPAGATE_RESOURCES_FALLBACK_DICT = _make_dict(AAR_PROPAGATE_RESOURCES_FALLBACK) +AAR_PROPAGATE_RESOURCES_ROLLOUT_DICT = _make_dict(AAR_PROPAGATE_RESOURCES_ROLLOUT) +AIT_VIRTUAL_DEVICE_FALLBACK_DICT = _make_dict(AIT_VIRTUAL_DEVICE_FALLBACK) +AIT_VIRTUAL_DEVICE_ROLLOUT_DICT = _make_dict(AIT_VIRTUAL_DEVICE_ROLLOUT) +ANDROID_ARCHIVE_DOGFOOD_DICT = _make_dict(ANDROID_ARCHIVE_DOGFOOD) +ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST_DICT = _make_dict(ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST) +ANDROID_DEVICE_PLUGIN_ROLLOUT_DICT = _make_dict(ANDROID_DEVICE_PLUGIN_ROLLOUT) +ANDROID_DEVICE_PLUGIN_FALLBACK_DICT = _make_dict(ANDROID_DEVICE_PLUGIN_FALLBACK) +ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT_DICT = _make_dict(ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT) +ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK_DICT = _make_dict(ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK) +ANDROID_FEATURE_SPLITS_DOGFOOD_DICT = _make_dict(ANDROID_FEATURE_SPLITS_DOGFOOD) +ANDROID_LIBRARY_IMPLICIT_EXPORTS_DICT = _make_dict(ANDROID_LIBRARY_IMPLICIT_EXPORTS) +ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS_DICT = _make_dict(ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS) +ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_DICT = _make_dict(ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS) +ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS_DICT = _make_dict(ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS) +ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK_DICT = _make_dict(ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK) +ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT_DICT = _make_dict(ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT) +ANDROID_LINT_CHECKS_FALLBACK_DICT = _make_dict(ANDROID_LINT_CHECKS_FALLBACK) +ANDROID_LINT_CHECKS_ROLLOUT_DICT = _make_dict(ANDROID_LINT_CHECKS_ROLLOUT) +ANDROID_LINT_FALLBACK_DICT = _make_dict(ANDROID_LINT_FALLBACK) +ANDROID_LINT_ROLLOUT_DICT = _make_dict(ANDROID_LINT_ROLLOUT) +LINT_REGISTRY_FALLBACK_DICT = _make_dict(LINT_REGISTRY_FALLBACK) +LINT_REGISTRY_ROLLOUT_DICT = _make_dict(LINT_REGISTRY_ROLLOUT) +ANDROID_BUILD_STAMPING_ROLLOUT_DICT = _make_dict(ANDROID_BUILD_STAMPING_ROLLOUT) +ANDROID_BUILD_STAMPING_FALLBACK_DICT = _make_dict(ANDROID_BUILD_STAMPING_FALLBACK) +ANDROID_TEST_LOCKDOWN_GENERATOR_FUNCTIONS_DICT = _make_dict(ANDROID_TEST_LOCKDOWN_GENERATOR_FUNCTIONS) +ANDROID_TEST_LOCKDOWN_TARGETS_DICT = _make_dict(ANDROID_TEST_LOCKDOWN_TARGETS) +APP_INSTALLATION_SNAPSHOT_DICT = _make_dict(APP_INSTALLATION_SNAPSHOT) +APP_INSTALLATION_SNAPSHOT_FALLBACK_DICT = _make_dict(APP_INSTALLATION_SNAPSHOT_FALLBACK) +B122039567_DICT = _make_dict(B122039567) +B123854163_DICT = _make_dict(B123854163) +CAN_USE_DEX2OAT_OPTIONS_DICT = _make_dict(CAN_USE_DEX2OAT_OPTIONS) +FIX_RESOURCE_TRANSIVITY_FALLBACK_DICT = _make_dict(FIX_RESOURCE_TRANSITIVITY_FALLBACK) +FIX_RESOURCE_TRANSIVITY_ROLLOUT_DICT = _make_dict(FIX_RESOURCE_TRANSITIVITY_ROLLOUT) +FIX_EXPORT_EXPORTING_FALLBACK_DICT = _make_dict(FIX_EXPORT_EXPORTING_FALLBACK) +FIX_EXPORT_EXPORTING_ROLLOUT_DICT = _make_dict(FIX_EXPORT_EXPORTING_ROLLOUT) +AIT_USE_HOST_DEX2OAT_ROLLOUT_DICT = _make_dict(AIT_USE_HOST_DEX2OAT_ROLLOUT) +AIT_USE_HOST_DEX2OAT_ROLLOUT_FALLBACK_DICT = _make_dict(AIT_USE_HOST_DEX2OAT_ROLLOUT_FALLBACK) +AIT_INSTALL_APPS_IN_DATA_DICT = _make_dict(INSTALL_APPS_IN_DATA) +LOCAL_TEST_MULTI_PROTO_PKG_DICT = _make_dict(LOCAL_TEST_MULTI_PROTO_PKG) +LOCAL_TEST_FALLBACK_DICT = _make_dict(LOCAL_TEST_FALLBACK) +LOCAL_TEST_ROLLOUT_DICT = _make_dict(LOCAL_TEST_ROLLOUT) +LOCAL_TEST_STARLARK_RESOURCES_FALLBACK_DICT = _make_dict(LOCAL_TEST_STARLARK_RESOURCES_FALLBACK) +LOCAL_TEST_STARLARK_RESOURCES_ROLLOUT_DICT = _make_dict(LOCAL_TEST_STARLARK_RESOURCES_ROLLOUT) +ANDROID_TEST_PLATFORM_FALLBACK_DICT = _make_dict(ANDROID_TEST_PLATFORM_FALLBACK) +ANDROID_TEST_PLATFORM_ROLLOUT_DICT = _make_dict(ANDROID_TEST_PLATFORM_ROLLOUT) +SOURCELESS_BINARY_FALLBACK_DICT = _make_dict(SOURCELESS_BINARY_FALLBACK) +SOURCELESS_BINARY_ROLLOUT_DICT = _make_dict(SOURCELESS_BINARY_ROLLOUT) +TEST_TO_INSTRUMENT_TEST_FALLBACK_DICT = _make_dict(TEST_TO_INSTRUMENT_TEST_FALLBACK) +TEST_TO_INSTRUMENT_TEST_ROLLOUT_DICT = _make_dict(TEST_TO_INSTRUMENT_TEST_ROLLOUT) +ALLOW_RESOURCE_CONFLICTS_DICT = _make_dict(ALLOW_RESOURCE_CONFLICTS) +PARTIAL_JETIFICATION_TARGETS_ROLLOUT_DICT = _make_dict(PARTIAL_JETIFICATION_TARGETS_ROLLOUT) +PARTIAL_JETIFICATION_TARGETS_FALLBACK_DICT = _make_dict(PARTIAL_JETIFICATION_TARGETS_FALLBACK) +KT_ANDROID_LIBRARY_ROLLOUT_DICT = _make_dict(KT_ANDROID_LIBRARY_ROLLOUT) +KT_ANDROID_LIBRARY_FALLBACK_DICT = _make_dict(KT_ANDROID_LIBRARY_FALLBACK) +ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT_DICT = _make_dict(ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT) +ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK_DICT = _make_dict(ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK) + +def _matches(fqn, dct): + # Labels with workspace names ("@workspace//pkg:target") are not supported. + if fqn.startswith("@"): + return False + + if not fqn.startswith("//"): + fail("Fully qualified target should start with '//', got: " + fqn) + + if fqn in dct: + return True + + pkg_and_target = fqn.split(":") + if len(pkg_and_target) != 2: + fail("Expected fully qualified target, got: " + fqn) + pkg = pkg_and_target[0] + + if (pkg + ":__pkg__") in dct: + return True + + pkg = pkg.lstrip("//") + pkg_parts = pkg.split("/") + ancestor_pkg = "//" + + if (ancestor_pkg + ":__subpackages__") in dct: + return True + + for pkg_part in pkg_parts: + ancestor_pkg = ( + (ancestor_pkg + "/" + pkg_part) if ancestor_pkg != "//" else ("//" + pkg_part) + ) + if (ancestor_pkg + ":__subpackages__") in dct: + return True + + return False + +acls = struct( + in_aar_import_deps_checker = _in_aar_import_deps_checker, + in_aar_import_explicit_exports_manifest = _in_aar_import_explicit_exports_manifest, + in_aar_import_exports_r_java = _in_aar_import_exports_r_java, + in_aar_propagate_resources = _in_aar_propagate_resources, + in_ait_virtual_device = _in_ait_virtual_device, + in_b122039567 = _in_b122039567, + in_b123854163 = _in_b123854163, + in_android_archive_dogfood = _in_android_archive_dogfood, + in_android_archive_excluded_deps_denylist = _in_android_archive_excluded_deps_denylist, + in_android_device_plugin_rollout = _in_android_device_plugin_rollout, + in_android_instrumentation_binary_starlark_resources = _in_android_instrumentation_binary_starlark_resources, + in_android_feature_splits_dogfood = _in_android_feature_splits_dogfood, + in_android_library_implicit_exports = _in_android_library_implicit_exports, + in_android_library_implicit_exports_generator_functions = _in_android_library_implicit_exports_generator_functions, + in_android_library_starlark_resource_outputs_rollout = _in_android_library_starlark_resource_outputs_rollout, + in_android_library_resources_without_srcs = _in_android_library_resources_without_srcs, + in_android_library_resources_without_srcs_generator_functions = _in_android_library_resources_without_srcs_generator_functions, + in_android_lint_checks_rollout = _in_android_lint_checks_rollout, + in_android_lint_rollout = _in_android_lint_rollout, + in_lint_registry_rollout = _in_lint_registry_rollout, + in_android_build_stamping_rollout = _in_android_build_stamping_rollout, + in_android_test_lockdown_allowlist = _in_android_test_lockdown_allowlist, + in_app_installation_snapshot = _in_app_installation_snapshot, + in_dex2oat_opts = _in_dex2oat_opts, + in_fix_export_exporting_rollout = _in_fix_export_exporting_rollout, + in_fix_resource_transivity_rollout = _in_fix_resource_transivity_rollout, + in_host_dex2oat_rollout = _in_host_dex2oat_rollout, + in_install_apps_in_data = _in_install_apps_in_data, + in_local_test_multi_proto = _in_local_test_multi_proto, + in_local_test_rollout = _in_local_test_rollout, + in_local_test_starlark_resources = _in_local_test_starlark_resources, + in_android_test_platform_rollout = _in_android_test_platform_rollout, + in_sourceless_binary_rollout = _in_sourceless_binary_rollout, + in_test_to_instrument_test_rollout = _in_test_to_instrument_test_rollout, + in_allow_resource_conflicts = _in_allow_resource_conflicts, + in_partial_jetification_targets = _in_partial_jetification_targets, + in_kt_android_library_rollout = _in_kt_android_library_rollout, + in_android_instrumentation_test_manifest_check_rollout = _in_android_instrumentation_test_manifest_check_rollout, +) + +# Visible for testing +testing = struct( + matches = _matches, + make_dict = _make_dict, +) diff --git a/rules/acls/BUILD b/rules/acls/BUILD new file mode 100644 index 0000000..3c45ae3 --- /dev/null +++ b/rules/acls/BUILD @@ -0,0 +1,6 @@ +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") + +bzl_library( + name = "bzl", + srcs = glob(["*.bzl"]), +) diff --git a/rules/acls/aar_import_deps_checker.bzl b/rules/acls/aar_import_deps_checker.bzl new file mode 100644 index 0000000..dabfd31 --- /dev/null +++ b/rules/acls/aar_import_deps_checker.bzl @@ -0,0 +1,27 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allowlist to run ImportDepsChecker on aar_import. + +See b/148478031 for context. +""" + +# keep sorted +AAR_IMPORT_DEPS_CHECKER_ROLLOUT = [ + "//:__subpackages__", +] + +# keep sorted +AAR_IMPORT_DEPS_CHECKER_FALLBACK = [ +] diff --git a/rules/acls/aar_import_explicit_exports_manifest.bzl b/rules/acls/aar_import_explicit_exports_manifest.bzl new file mode 100644 index 0000000..fb3a0b9 --- /dev/null +++ b/rules/acls/aar_import_explicit_exports_manifest.bzl @@ -0,0 +1,19 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for aar_import rule to explcitly set exports_manifest.""" + +# keep sorted +AAR_IMPORT_EXPLICIT_EXPORTS_MANIFEST = [ +] diff --git a/rules/acls/aar_import_exports_r_java.bzl b/rules/acls/aar_import_exports_r_java.bzl new file mode 100644 index 0000000..7d2aff1 --- /dev/null +++ b/rules/acls/aar_import_exports_r_java.bzl @@ -0,0 +1,19 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for aar_import rule to export the R.java.""" + +# keep sorted +AAR_IMPORT_EXPORTS_R_JAVA = [ +] diff --git a/rules/acls/aar_propagate_resources.bzl b/rules/acls/aar_propagate_resources.bzl new file mode 100644 index 0000000..48ab3ba --- /dev/null +++ b/rules/acls/aar_propagate_resources.bzl @@ -0,0 +1,24 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allowlist for propagating resources and assets from aar_import (b/138322659).""" + +# keep sorted +AAR_PROPAGATE_RESOURCES_ROLLOUT = [ + "//:__subpackages__", +] + +# keep sorted +AAR_PROPAGATE_RESOURCES_FALLBACK = [ +] diff --git a/rules/acls/ait_install_snapshots.bzl b/rules/acls/ait_install_snapshots.bzl new file mode 100644 index 0000000..ff11e64 --- /dev/null +++ b/rules/acls/ait_install_snapshots.bzl @@ -0,0 +1,23 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Packages in this list will use devices with app installation snapshots.""" + +# keep sorted +APP_INSTALLATION_SNAPSHOT = [ +] + +# keep sorted +APP_INSTALLATION_SNAPSHOT_FALLBACK = [ +] diff --git a/rules/acls/ait_virtual_device.bzl b/rules/acls/ait_virtual_device.bzl new file mode 100644 index 0000000..f5ec1b6 --- /dev/null +++ b/rules/acls/ait_virtual_device.bzl @@ -0,0 +1,21 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for switching to virtual_device in android_instrumentation_test.""" +AIT_VIRTUAL_DEVICE_ROLLOUT = [ + "//:__subpackages__", +] + +AIT_VIRTUAL_DEVICE_FALLBACK = [ +] diff --git a/rules/acls/allow_resource_conflicts.bzl b/rules/acls/allow_resource_conflicts.bzl new file mode 100644 index 0000000..d4f9742 --- /dev/null +++ b/rules/acls/allow_resource_conflicts.bzl @@ -0,0 +1,20 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" Packages of android_binary, android_test, and android_local_test targets that are allowed to contain resource conflicts. + +See b/125484033. +""" + +ALLOW_RESOURCE_CONFLICTS = [] diff --git a/rules/acls/android_archive_dogfood.bzl b/rules/acls/android_archive_dogfood.bzl new file mode 100644 index 0000000..2320db3 --- /dev/null +++ b/rules/acls/android_archive_dogfood.bzl @@ -0,0 +1,20 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allowlist for targets able to use the android_archive rule.""" + +# keep sorted +ANDROID_ARCHIVE_DOGFOOD = [ + "//:__subpackages__", +] diff --git a/rules/acls/android_archive_excluded_deps_denylist.bzl b/rules/acls/android_archive_excluded_deps_denylist.bzl new file mode 100644 index 0000000..2a46e68 --- /dev/null +++ b/rules/acls/android_archive_excluded_deps_denylist.bzl @@ -0,0 +1,21 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Denylist for rules that are not allowed in android_archive excluded_deps.""" + +# keep sorted +ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST = [ + # Failure test support. + "@rules_android//test/rules/android_archive/java/com/testdata/denied:__pkg__", +] diff --git a/rules/acls/android_binary_starlark_resources.bzl b/rules/acls/android_binary_starlark_resources.bzl new file mode 100644 index 0000000..0291cb6 --- /dev/null +++ b/rules/acls/android_binary_starlark_resources.bzl @@ -0,0 +1,22 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for rollout of Starlark resource processing pipeline in android_binary.""" + +ANDROID_BINARY_STARLARK_RESOURCES_ROLLOUT = [ + "//:__subpackages__", +] + +ANDROID_BINARY_STARLARK_RESOURCES_FALLBACK = [ +] diff --git a/rules/acls/android_build_stamping_rollout.bzl b/rules/acls/android_build_stamping_rollout.bzl new file mode 100644 index 0000000..8747db8 --- /dev/null +++ b/rules/acls/android_build_stamping_rollout.bzl @@ -0,0 +1,23 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for build stamping in the Android Rules.""" + +# keep sorted +ANDROID_BUILD_STAMPING_ROLLOUT = [ +] + +# keep sorted +ANDROID_BUILD_STAMPING_FALLBACK = [ +] diff --git a/rules/acls/android_device_plugin_rollout.bzl b/rules/acls/android_device_plugin_rollout.bzl new file mode 100644 index 0000000..310da7d --- /dev/null +++ b/rules/acls/android_device_plugin_rollout.bzl @@ -0,0 +1,26 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow and fallback lists for ATP Device Plugin rollout.""" + +# Targets for ATP Device Plugin Rollout +ANDROID_DEVICE_PLUGIN_ROLLOUT = [ + "//:__subpackages__", +] + +_READY_TO_ROLLOUT_IN_NEXT_RELEASE = [ +] + +ANDROID_DEVICE_PLUGIN_FALLBACK = [ +] + _READY_TO_ROLLOUT_IN_NEXT_RELEASE diff --git a/rules/acls/android_feature_splits_dogfood.bzl b/rules/acls/android_feature_splits_dogfood.bzl new file mode 100644 index 0000000..0e7ade0 --- /dev/null +++ b/rules/acls/android_feature_splits_dogfood.bzl @@ -0,0 +1,19 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Packages able to use deprecated Android feature splits features.""" + +# keep sorted +ANDROID_FEATURE_SPLITS_DOGFOOD = [ +] diff --git a/rules/acls/android_instrumentation_binary_starlark_resources.bzl b/rules/acls/android_instrumentation_binary_starlark_resources.bzl new file mode 100644 index 0000000..0e55315 --- /dev/null +++ b/rules/acls/android_instrumentation_binary_starlark_resources.bzl @@ -0,0 +1,22 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for rollout of Starlark resource processing pipeline in instrumentation binaries.""" + +ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT = [ + "//:__subpackages__", +] + +ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK = [ +] diff --git a/rules/acls/android_instrumentation_test_manifest_check_rollout.bzl b/rules/acls/android_instrumentation_test_manifest_check_rollout.bzl new file mode 100644 index 0000000..154f6bb --- /dev/null +++ b/rules/acls/android_instrumentation_test_manifest_check_rollout.bzl @@ -0,0 +1,22 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Rollout list for enabling manifest validation in android_instrumentation_test.""" + +ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT = [ + "//:__subpackages__", +] + +ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK = [ +] diff --git a/rules/acls/android_library_implicit_exports.bzl b/rules/acls/android_library_implicit_exports.bzl new file mode 100644 index 0000000..521c2ac --- /dev/null +++ b/rules/acls/android_library_implicit_exports.bzl @@ -0,0 +1,23 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for b/144163743 - deprecated implicit exports.""" + +# These macros can create android_library targets with deps and no srcs with no way for +# their users to control this. +ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS = [ +] + +ANDROID_LIBRARY_IMPLICIT_EXPORTS = [ +] diff --git a/rules/acls/android_library_resources_without_srcs.bzl b/rules/acls/android_library_resources_without_srcs.bzl new file mode 100644 index 0000000..b376e30 --- /dev/null +++ b/rules/acls/android_library_resources_without_srcs.bzl @@ -0,0 +1,19 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for b/144163743, allowing implicit export of deps when resources and deps are present.""" + +ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS = [] + +ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS = ["//:__subpackages__"] diff --git a/rules/acls/android_library_starlark_resource_outputs.bzl b/rules/acls/android_library_starlark_resource_outputs.bzl new file mode 100644 index 0000000..b798fa3 --- /dev/null +++ b/rules/acls/android_library_starlark_resource_outputs.bzl @@ -0,0 +1,26 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for setting the resource outputs (.srcjar, R.txt, and _resources.jar) of android_library to +those files generated by the Starlark resource processing pipeline and not the native resource +processing pipeline. These resource-related predeclared outputs need to be re-pointed to the corresponding +artifacts in the Starlark pipeline. See b/177261846.""" + +# keep sorted +ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT = [ + "//:__subpackages__", +] + +ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK = [ +] diff --git a/rules/acls/android_library_starlark_resources.bzl b/rules/acls/android_library_starlark_resources.bzl new file mode 100644 index 0000000..46e68a5 --- /dev/null +++ b/rules/acls/android_library_starlark_resources.bzl @@ -0,0 +1,23 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for diff test for android_library native resource processing vs Starlark processed resources.""" + +# keep sorted +ANDROID_LIBRARY_STARLARK_RESOURCES_ROLLOUT = [ + "//:__subpackages__", +] + +ANDROID_LIBRARY_STARLARK_RESOURCES_FALLBACK = [ +] diff --git a/rules/acls/android_lint_checks_rollout.bzl b/rules/acls/android_lint_checks_rollout.bzl new file mode 100644 index 0000000..ee44280 --- /dev/null +++ b/rules/acls/android_lint_checks_rollout.bzl @@ -0,0 +1,22 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for enabling Android Lint checks in the Android Rules.""" + +# keep sorted +ANDROID_LINT_CHECKS_ROLLOUT = [ +] + +ANDROID_LINT_CHECKS_FALLBACK = [ +] diff --git a/rules/acls/android_lint_rollout.bzl b/rules/acls/android_lint_rollout.bzl new file mode 100644 index 0000000..aab1d59 --- /dev/null +++ b/rules/acls/android_lint_rollout.bzl @@ -0,0 +1,21 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for Android Lint in the Android Rules.""" + +# keep sorted +ANDROID_LINT_ROLLOUT = [ +] + +ANDROID_LINT_FALLBACK = [] diff --git a/rules/acls/android_test_lockdown.bzl b/rules/acls/android_test_lockdown.bzl new file mode 100644 index 0000000..8dce844 --- /dev/null +++ b/rules/acls/android_test_lockdown.bzl @@ -0,0 +1,21 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allowlist of macro and targets that are allowed to use android_test.""" + +ANDROID_TEST_LOCKDOWN_GENERATOR_FUNCTIONS = [ +] + +ANDROID_TEST_LOCKDOWN_TARGETS = [ +] diff --git a/rules/acls/android_test_platform_rollout.bzl b/rules/acls/android_test_platform_rollout.bzl new file mode 100644 index 0000000..47b879e --- /dev/null +++ b/rules/acls/android_test_platform_rollout.bzl @@ -0,0 +1,26 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow and fallback lists for Android Test Platform rollout""" + +# Targets for Android Test Platform Rollout +ANDROID_TEST_PLATFORM_ROLLOUT = [ + "//:__subpackages__", +] + +_READY_TO_ROLLOUT_IN_NEXT_RELEASE = [ +] + +ANDROID_TEST_PLATFORM_FALLBACK = [ +] + _READY_TO_ROLLOUT_IN_NEXT_RELEASE diff --git a/rules/acls/android_test_starlark_resources.bzl b/rules/acls/android_test_starlark_resources.bzl new file mode 100644 index 0000000..2510931 --- /dev/null +++ b/rules/acls/android_test_starlark_resources.bzl @@ -0,0 +1,17 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for diff test for android_test native vs Starlark processed resources.""" + +ANDROID_TEST_STARLARK_RESOURCES = [] diff --git a/rules/acls/b122039567.bzl b/rules/acls/b122039567.bzl new file mode 100644 index 0000000..907b08e --- /dev/null +++ b/rules/acls/b122039567.bzl @@ -0,0 +1,18 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for b/122039567.""" + +B122039567 = [ +] diff --git a/rules/acls/b123854163.bzl b/rules/acls/b123854163.bzl new file mode 100644 index 0000000..d23efb5 --- /dev/null +++ b/rules/acls/b123854163.bzl @@ -0,0 +1,18 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for b/123854163.""" + +B123854163 = [ +] diff --git a/rules/acls/dex2oat_opts.bzl b/rules/acls/dex2oat_opts.bzl new file mode 100644 index 0000000..b935a1f --- /dev/null +++ b/rules/acls/dex2oat_opts.bzl @@ -0,0 +1,19 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Packages in this list are allowed to specify custom dex2oat options.""" + +# keep sorted +CAN_USE_DEX2OAT_OPTIONS = [ +] diff --git a/rules/acls/fix_export_exporting_rollout.bzl b/rules/acls/fix_export_exporting_rollout.bzl new file mode 100644 index 0000000..cb68adf --- /dev/null +++ b/rules/acls/fix_export_exporting_rollout.bzl @@ -0,0 +1,24 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allowlist for making exports actually be exported.""" + +# keep sorted +FIX_EXPORT_EXPORTING_ROLLOUT = [ + "//:__subpackages__", +] + +# keep sorted +FIX_EXPORT_EXPORTING_FALLBACK = [ +] diff --git a/rules/acls/fix_resource_transitivity_rollout.bzl b/rules/acls/fix_resource_transitivity_rollout.bzl new file mode 100644 index 0000000..bdde15f --- /dev/null +++ b/rules/acls/fix_resource_transitivity_rollout.bzl @@ -0,0 +1,22 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allowlist for fixing resource dependencies passing through rules with no manifest.""" + +# keep sorted +FIX_RESOURCE_TRANSITIVITY_ROLLOUT = [ + "//:__subpackages__", +] + +FIX_RESOURCE_TRANSITIVITY_FALLBACK = [] diff --git a/rules/acls/host_dex2oat_rollout.bzl b/rules/acls/host_dex2oat_rollout.bzl new file mode 100644 index 0000000..b3da4ee --- /dev/null +++ b/rules/acls/host_dex2oat_rollout.bzl @@ -0,0 +1,27 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Whitelist for using host dex2oat with android_instrumentation_test.""" + +ANDROID_TEST_HOST_DEX2OAT_ROLLOUT = [ + "//...", +] + +# keep sorted +AIT_USE_HOST_DEX2OAT_ROLLOUT = [ + "//:__subpackages__", +] + +AIT_USE_HOST_DEX2OAT_ROLLOUT_FALLBACK = [ +] diff --git a/rules/acls/install_apps_in_data.bzl b/rules/acls/install_apps_in_data.bzl new file mode 100644 index 0000000..58994bf --- /dev/null +++ b/rules/acls/install_apps_in_data.bzl @@ -0,0 +1,18 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Whitelist for using installing apps in data attr in android_instrumentation_test.""" + +INSTALL_APPS_IN_DATA = [ +] diff --git a/rules/acls/kt_android_library_rollout.bzl b/rules/acls/kt_android_library_rollout.bzl new file mode 100644 index 0000000..50c43a4 --- /dev/null +++ b/rules/acls/kt_android_library_rollout.bzl @@ -0,0 +1,24 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list to rollout the kt_android_library rule from rules_android.""" + +# keep sorted +KT_ANDROID_LIBRARY_ROLLOUT = [ + "//:__subpackages__", +] + +# keep sorted +KT_ANDROID_LIBRARY_FALLBACK = [ +] diff --git a/rules/acls/library_rollout.bzl b/rules/acls/library_rollout.bzl new file mode 100644 index 0000000..cb42a88 --- /dev/null +++ b/rules/acls/library_rollout.bzl @@ -0,0 +1,23 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for Starlark android_library rollout""" + +LIBRARY_ROLLOUT = [ + # keep sorted + "//:__subpackages__", +] + +LIBRARY_FALLBACK = [ +] diff --git a/rules/acls/library_rollout_override.bzl b/rules/acls/library_rollout_override.bzl new file mode 100644 index 0000000..805bf0f --- /dev/null +++ b/rules/acls/library_rollout_override.bzl @@ -0,0 +1,17 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""A Starlark android_library rollout override switch, for testing.""" + +LIBRARY_ROLLOUT_OVERRIDE = None # A tristate: None, True or False. diff --git a/rules/acls/lint_registry_rollout.bzl b/rules/acls/lint_registry_rollout.bzl new file mode 100644 index 0000000..3138f94 --- /dev/null +++ b/rules/acls/lint_registry_rollout.bzl @@ -0,0 +1,21 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for the Android Lint Registry target.""" + +# keep sorted +LINT_REGISTRY_ROLLOUT = [ +] + +LINT_REGISTRY_FALLBACK = [] diff --git a/rules/acls/local_test_multi_proto.bzl b/rules/acls/local_test_multi_proto.bzl new file mode 100644 index 0000000..e0bc06b --- /dev/null +++ b/rules/acls/local_test_multi_proto.bzl @@ -0,0 +1,23 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allowlist for android_local_test targets allowed to depend on java_proto_library. + +See b/120162253 for context. +""" + +LOCAL_TEST_MULTI_PROTO = [ +] + +LOCAL_TEST_MULTI_PROTO_PKG = [x + ":__pkg__" for x in LOCAL_TEST_MULTI_PROTO] diff --git a/rules/acls/local_test_rollout.bzl b/rules/acls/local_test_rollout.bzl new file mode 100644 index 0000000..889d82c --- /dev/null +++ b/rules/acls/local_test_rollout.bzl @@ -0,0 +1,22 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow and fallback lists for Starlark android_local_test rollout""" + +LOCAL_TEST_ROLLOUT = [ + "//:__subpackages__", +] + +LOCAL_TEST_FALLBACK = [ +] diff --git a/rules/acls/local_test_starlark_resources.bzl b/rules/acls/local_test_starlark_resources.bzl new file mode 100644 index 0000000..19c1777 --- /dev/null +++ b/rules/acls/local_test_starlark_resources.bzl @@ -0,0 +1,22 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for android_local_test Starlark processed resources.""" + +LOCAL_TEST_STARLARK_RESOURCES_ROLLOUT = [ +] + +LOCAL_TEST_STARLARK_RESOURCES_FALLBACK = [ + "//:__subpackages__", +] diff --git a/rules/acls/nitrogen_test_runner_rollout.bzl b/rules/acls/nitrogen_test_runner_rollout.bzl new file mode 100644 index 0000000..891671e --- /dev/null +++ b/rules/acls/nitrogen_test_runner_rollout.bzl @@ -0,0 +1,28 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow and fallback lists for Nitrogen rollout""" + +# Nitrogen targets for android_instrumentation_test rollout +NITROGEN_TEST_RUNNER_ROLLOUT = [ + "//:__subpackages__", +] + +# Nitrogen targets for android_test rollout +NITROGEN_AT_TEST_RUNNER_ROLLOUT = [ + "//:__subpackages__", +] + +NITROGEN_TEST_RUNNER_FALLBACK = [ +] diff --git a/rules/acls/partial_jetification_targets.bzl b/rules/acls/partial_jetification_targets.bzl new file mode 100644 index 0000000..490c3ea --- /dev/null +++ b/rules/acls/partial_jetification_targets.bzl @@ -0,0 +1,20 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for targets or subsets of targets to partially jetify at build time""" + +PARTIAL_JETIFICATION_TARGETS_ROLLOUT = [ +] + +PARTIAL_JETIFICATION_TARGETS_FALLBACK = [] diff --git a/rules/acls/sourceless_binary_rollout.bzl b/rules/acls/sourceless_binary_rollout.bzl new file mode 100644 index 0000000..132ba10 --- /dev/null +++ b/rules/acls/sourceless_binary_rollout.bzl @@ -0,0 +1,21 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for sourceless android_binary rollout""" + +SOURCELESS_BINARY_ROLLOUT = [ +] + +SOURCELESS_BINARY_FALLBACK = [ +] diff --git a/rules/acls/starlark_resources_diff.bzl b/rules/acls/starlark_resources_diff.bzl new file mode 100644 index 0000000..ed7752e --- /dev/null +++ b/rules/acls/starlark_resources_diff.bzl @@ -0,0 +1,18 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for diff test for native vs Starlark resource processing pipelines.""" + +STARLARK_RESOURCES_DIFF = [ +] diff --git a/rules/acls/test_to_instrument_test_rollout.bzl b/rules/acls/test_to_instrument_test_rollout.bzl new file mode 100644 index 0000000..835880d --- /dev/null +++ b/rules/acls/test_to_instrument_test_rollout.bzl @@ -0,0 +1,21 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for android_test to android_instrumentation_test rollout""" + +TEST_TO_INSTRUMENT_TEST_ROLLOUT = [ +] + +TEST_TO_INSTRUMENT_TEST_FALLBACK = [ +] diff --git a/rules/acls/use_classic_desugar.bzl b/rules/acls/use_classic_desugar.bzl new file mode 100644 index 0000000..37110c4 --- /dev/null +++ b/rules/acls/use_classic_desugar.bzl @@ -0,0 +1,18 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""List of targets that require classic desugar.""" + +USE_CLASSIC_DESUGAR = [ +] diff --git a/rules/android_application/BUILD b/rules/android_application/BUILD new file mode 100644 index 0000000..31cf119 --- /dev/null +++ b/rules/android_application/BUILD @@ -0,0 +1,27 @@ +# The android_application rule. + +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") + +licenses(["notice"]) + +exports_files([ + "bundle_deploy.sh_template", + "feature_module_validation.sh", + "gen_android_feature_manifest.sh", + "gen_priority_android_feature_manifest.sh", + "rule.bzl", +]) + +filegroup( + name = "all_files", + srcs = glob(["**"]), +) + +bzl_library( + name = "bzl", + srcs = glob(["*.bzl"]), + deps = [ + "@rules_android//rules:common_bzl", + "@rules_android//rules/flags:bzl", + ], +) diff --git a/rules/android_application/android_application.bzl b/rules/android_application/android_application.bzl new file mode 100644 index 0000000..f7e1710 --- /dev/null +++ b/rules/android_application/android_application.bzl @@ -0,0 +1,51 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""android_application rule. + +This file exists to inject the correct version of android_binary. +""" + +load(":android_application_rule.bzl", _android_application_macro = "android_application_macro") +load("@rules_android//rules:android_binary.bzl", _android_binary = "android_binary") + +def android_application(**attrs): + """Rule to build an Android Application (app bundle). + + `android_application` produces an app bundle (.aab) rather than an apk, and treats splits + (both configuration and dynamic feature modules) as first-class constructs. If + `feature_modules`, `bundle_config` or both are supplied this rule will produce an .aab. + Otherwise it will fall back to `android_binary` and produce an apk. + + **Attributes** + + `android_application` accepts all the same attributes as `android_binary`, with the following + key differences. + + Name | Description + --- | --- + `srcs` | `android_application` does not accept sources. + `manifest_values` | Required. Must specify `applicationId` in the `manifest_values` + `feature_modules` | New. List of labels to `android_feature_module`s to include as feature splits. Note: must be fully qualified paths (//some:target), not relative. + `bundle_config_file` | New. String path to .pb.json file containing the bundle config. See the [bundletool docs](https://developer.android.com/studio/build/building-cmdline#bundleconfig) for format and examples. Note: this attribute is subject to changes which may require teams to migrate their configurations to a build target. + `app_integrity_config` | Optional. String path to .binarypb file containing the play integrity config. See https://github.com/google/bundletool/blob/master/src/main/proto/app_integrity_config.proto. + `rotation_config` | Optional. String path to .textproto file containing the V3 rotation config. + + Args: + **attrs: Rule attributes + """ + _android_application_macro( + _android_binary = _android_binary, + **attrs + ) diff --git a/rules/android_application/android_application_rule.bzl b/rules/android_application/android_application_rule.bzl new file mode 100644 index 0000000..ab26456 --- /dev/null +++ b/rules/android_application/android_application_rule.bzl @@ -0,0 +1,385 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""android_application rule.""" + +load(":android_feature_module_rule.bzl", "get_feature_module_paths") +load(":attrs.bzl", "ANDROID_APPLICATION_ATTRS") +load( + "@rules_android//rules:aapt.bzl", + _aapt = "aapt", +) +load( + "@rules_android//rules:bundletool.bzl", + _bundletool = "bundletool", +) +load( + "@rules_android//rules:busybox.bzl", + _busybox = "busybox", +) +load( + "@rules_android//rules:common.bzl", + _common = "common", +) +load( + "@rules_android//rules:java.bzl", + _java = "java", +) +load( + "@rules_android//rules:providers.bzl", + "AndroidBundleInfo", + "AndroidFeatureModuleInfo", + "StarlarkAndroidResourcesInfo", +) +load( + "@rules_android//rules:utils.bzl", + "get_android_toolchain", + _log = "log", +) + +UNSUPPORTED_ATTRS = [ + "srcs", +] + +def _verify_attrs(attrs, fqn): + for attr in UNSUPPORTED_ATTRS: + if hasattr(attrs, attr): + _log.error("Unsupported attr: %s in android_application" % attr) + + if not attrs.get("manifest_values", default = {}).get("applicationId"): + _log.error("%s missing required applicationId in manifest_values" % fqn) + + for attr in ["deps"]: + if attr not in attrs: + _log.error("%s missing require attribute `%s`" % (fqn, attr)) + +def _process_feature_module( + ctx, + out = None, + base_apk = None, + feature_target = None, + java_package = None, + application_id = None): + manifest = _create_feature_manifest( + ctx, + base_apk, + java_package, + feature_target, + ctx.attr._android_sdk[AndroidSdkInfo].aapt2, + ctx.executable._feature_manifest_script, + ctx.executable._priority_feature_manifest_script, + get_android_toolchain(ctx).android_resources_busybox, + _common.get_host_javabase(ctx), + ) + res = feature_target[AndroidFeatureModuleInfo].library[StarlarkAndroidResourcesInfo] + binary = feature_target[AndroidFeatureModuleInfo].binary[ApkInfo].unsigned_apk + has_native_libs = bool(feature_target[AndroidFeatureModuleInfo].binary[AndroidIdeInfo].native_libs) + + # Create res .proto-apk_, output depending on whether this split has native libs. + if has_native_libs: + res_apk = ctx.actions.declare_file(ctx.label.name + "/" + feature_target.label.name + "/res.proto-ap_") + else: + res_apk = out + _busybox.package( + ctx, + out_r_src_jar = ctx.actions.declare_file("R.srcjar", sibling = manifest), + out_r_txt = ctx.actions.declare_file("R.txt", sibling = manifest), + out_symbols = ctx.actions.declare_file("merged.bin", sibling = manifest), + out_manifest = ctx.actions.declare_file("AndroidManifest_processed.xml", sibling = manifest), + out_proguard_cfg = ctx.actions.declare_file("proguard.cfg", sibling = manifest), + out_main_dex_proguard_cfg = ctx.actions.declare_file( + "main_dex_proguard.cfg", + sibling = manifest, + ), + out_resource_files_zip = ctx.actions.declare_file("resource_files.zip", sibling = manifest), + out_file = res_apk, + manifest = manifest, + java_package = java_package, + direct_resources_nodes = res.direct_resources_nodes, + transitive_resources_nodes = res.transitive_resources_nodes, + transitive_manifests = [res.transitive_manifests], + transitive_assets = [res.transitive_assets], + transitive_compiled_assets = [res.transitive_compiled_assets], + transitive_resource_files = [res.transitive_resource_files], + transitive_compiled_resources = [res.transitive_compiled_resources], + transitive_r_txts = [res.transitive_r_txts], + additional_apks_to_link_against = [base_apk], + proto_format = True, # required for aab. + android_jar = ctx.attr._android_sdk[AndroidSdkInfo].android_jar, + aapt = get_android_toolchain(ctx).aapt2.files_to_run, + busybox = get_android_toolchain(ctx).android_resources_busybox.files_to_run, + host_javabase = _common.get_host_javabase(ctx), + should_throw_on_conflict = True, + application_id = application_id, + ) + + if not has_native_libs: + return + + # Extract libs/ from split binary + native_libs = ctx.actions.declare_file(ctx.label.name + "/" + feature_target.label.name + "/native_libs.zip") + _common.filter_zip(ctx, binary, native_libs, ["lib/*"]) + + # Extract AndroidManifest.xml and assets from res-ap_ + filtered_res = ctx.actions.declare_file(ctx.label.name + "/" + feature_target.label.name + "/filtered_res.zip") + _common.filter_zip(ctx, res_apk, filtered_res, ["AndroidManifest.xml", "assets/*"]) + + # Merge into output + _java.singlejar( + ctx, + inputs = [filtered_res, native_libs], + output = out, + exclude_build_data = True, + java_toolchain = _common.get_java_toolchain(ctx), + ) + +def _create_feature_manifest( + ctx, + base_apk, + java_package, + feature_target, + aapt2, + feature_manifest_script, + priority_feature_manifest_script, + android_resources_busybox, + host_javabase): + info = feature_target[AndroidFeatureModuleInfo] + manifest = ctx.actions.declare_file(ctx.label.name + "/" + feature_target.label.name + "/AndroidManifest.xml") + + # Rule has not specified a manifest. Populate the default manifest template. + if not info.manifest: + args = ctx.actions.args() + args.add(manifest.path) + args.add(base_apk.path) + args.add(java_package) + args.add(info.feature_name) + args.add(info.title_id) + args.add(info.fused) + args.add(aapt2.executable) + + ctx.actions.run( + executable = feature_manifest_script, + inputs = [base_apk], + outputs = [manifest], + arguments = [args], + tools = [ + aapt2, + ], + mnemonic = "GenFeatureManifest", + progress_message = "Generating AndroidManifest.xml for " + feature_target.label.name, + ) + return manifest + + # Rule has a manifest (already validated by android_feature_module). + # Generate a priority manifest and then merge the user supplied manifest. + priority_manifest = ctx.actions.declare_file( + ctx.label.name + "/" + feature_target.label.name + "/Prioriy_AndroidManifest.xml", + ) + args = ctx.actions.args() + args.add(priority_manifest.path) + args.add(base_apk.path) + args.add(java_package) + args.add(info.feature_name) + args.add(aapt2.executable) + ctx.actions.run( + executable = priority_feature_manifest_script, + inputs = [base_apk], + outputs = [priority_manifest], + arguments = [args], + tools = [ + aapt2, + ], + mnemonic = "GenPriorityFeatureManifest", + progress_message = "Generating Priority AndroidManifest.xml for " + feature_target.label.name, + ) + + _busybox.merge_manifests( + ctx, + out_file = manifest, + manifest = priority_manifest, + mergee_manifests = depset([info.manifest]), + java_package = java_package, + busybox = android_resources_busybox.files_to_run, + host_javabase = host_javabase, + manifest_values = {"MODULE_TITLE": "@string/" + info.title_id}, + ) + + return manifest + +def _impl(ctx): + # Convert base apk to .proto_ap_ + base_apk = ctx.attr.base_module[ApkInfo].unsigned_apk + base_proto_apk = ctx.actions.declare_file(ctx.label.name + "/modules/base.proto-ap_") + _aapt.convert( + ctx, + out = base_proto_apk, + input = base_apk, + to_proto = True, + aapt = get_android_toolchain(ctx).aapt2.files_to_run, + ) + proto_apks = [base_proto_apk] + + # Convert each feature to .proto-ap_ + for feature in ctx.attr.feature_modules: + feature_proto_apk = ctx.actions.declare_file( + "%s.proto-ap_" % feature.label.name, + sibling = base_proto_apk, + ) + _process_feature_module( + ctx, + out = feature_proto_apk, + base_apk = base_apk, + feature_target = feature, + java_package = _java.resolve_package_from_label(ctx.label, ctx.attr.custom_package), + application_id = ctx.attr.application_id, + ) + proto_apks.append(feature_proto_apk) + + # Convert each each .proto-ap_ to module zip + modules = [] + for proto_apk in proto_apks: + module = ctx.actions.declare_file( + proto_apk.basename + ".zip", + sibling = proto_apk, + ) + modules.append(module) + _bundletool.proto_apk_to_module( + ctx, + out = module, + proto_apk = proto_apk, + unzip = get_android_toolchain(ctx).unzip_tool.files_to_run, + zip = get_android_toolchain(ctx).zip_tool.files_to_run, + ) + + metadata = dict() + if ProguardMappingInfo in ctx.attr.base_module: + metadata["com.android.tools.build.obfuscation/proguard.map"] = ctx.attr.base_module[ProguardMappingInfo].proguard_mapping + + if ctx.file.rotation_config: + metadata["com.google.play.apps.signing/RotationConfig.textproto"] = ctx.file.rotation_config + + if ctx.file.app_integrity_config: + metadata["com.google.play.apps.integrity/AppIntegrityConfig.pb"] = ctx.file.app_integrity_config + + # Create .aab + _bundletool.build( + ctx, + out = ctx.outputs.unsigned_aab, + modules = modules, + config = ctx.file.bundle_config_file, + metadata = metadata, + bundletool = get_android_toolchain(ctx).bundletool.files_to_run, + host_javabase = _common.get_host_javabase(ctx), + ) + + # Create `blaze run` script + subs = { + "%bundletool_path%": get_android_toolchain(ctx).bundletool.files_to_run.executable.short_path, + "%aab%": ctx.outputs.unsigned_aab.short_path, + "%key%": ctx.attr.base_module[ApkInfo].signing_keys[0].short_path, + } + ctx.actions.expand_template( + template = ctx.file._bundle_deploy, + output = ctx.outputs.deploy_script, + substitutions = subs, + is_executable = True, + ) + + return [ + ctx.attr.base_module[ApkInfo], + ctx.attr.base_module[AndroidPreDexJarInfo], + AndroidBundleInfo(unsigned_aab = ctx.outputs.unsigned_aab), + DefaultInfo( + executable = ctx.outputs.deploy_script, + runfiles = ctx.runfiles([ + ctx.outputs.unsigned_aab, + ctx.attr.base_module[ApkInfo].signing_keys[0], + get_android_toolchain(ctx).bundletool.files_to_run.executable, + ]), + ), + ] + +android_application = rule( + attrs = ANDROID_APPLICATION_ATTRS, + fragments = [ + "android", + "java", + ], + executable = True, + implementation = _impl, + outputs = { + "deploy_script": "%{name}.sh", + "unsigned_aab": "%{name}_unsigned.aab", + }, + toolchains = ["@rules_android//toolchains/android:toolchain_type"], + _skylark_testable = True, +) + +def android_application_macro(_android_binary, **attrs): + """android_application_macro. + + Args: + _android_binary: The android_binary rule to use. + **attrs: android_application attributes. + """ + + fqn = "//%s:%s" % (native.package_name(), attrs["name"]) + + # Must pop these because android_binary does not have these attributes. + app_integrity_config = attrs.pop("app_integrity_config", default = None) + rotation_config = attrs.pop("rotation_config", default = None) + + # Simply fall back to android_binary if no feature splits or bundle_config + if not attrs.get("feature_modules", None) and not (attrs.get("bundle_config", None) or attrs.get("bundle_config_file", None)): + _android_binary(**attrs) + return + + _verify_attrs(attrs, fqn) + + # Create an android_binary base split, plus an android_application to produce the aab + name = attrs.pop("name") + base_split_name = "%s_base" % name + + # default to [] if feature_modules = None is passed + feature_modules = attrs.pop("feature_modules", default = []) or [] + bundle_config = attrs.pop("bundle_config", default = None) + bundle_config_file = attrs.pop("bundle_config_file", default = None) + + # bundle_config is deprecated in favor of bundle_config_file + # In the future bundle_config will accept a build rule rather than a raw file. + bundle_config_file = bundle_config_file or bundle_config + + for feature_module in feature_modules: + if not feature_module.startswith("//") or ":" not in feature_module: + _log.error("feature_modules expects fully qualified paths, i.e. //some/path:target") + module_targets = get_feature_module_paths(feature_module) + attrs["deps"].append(str(module_targets.title_lib)) + + _android_binary( + name = base_split_name, + **attrs + ) + + android_application( + name = name, + base_module = ":%s" % base_split_name, + bundle_config_file = bundle_config_file, + app_integrity_config = app_integrity_config, + rotation_config = rotation_config, + custom_package = attrs.get("custom_package", None), + testonly = attrs.get("testonly"), + transitive_configs = attrs.get("transitive_configs", []), + feature_modules = feature_modules, + application_id = attrs["manifest_values"]["applicationId"], + ) diff --git a/rules/android_application/android_feature_module.bzl b/rules/android_application/android_feature_module.bzl new file mode 100644 index 0000000..840d961 --- /dev/null +++ b/rules/android_application/android_feature_module.bzl @@ -0,0 +1,58 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""android_feature_module rule. + +This file exists to inject the correct version of android_binary and android_library. +""" + +load( + ":android_feature_module_rule.bzl", + _android_feature_module_macro = "android_feature_module_macro", +) +load( + "@rules_android//rules:android_binary.bzl", + _android_binary = "android_binary", +) +load( + "@rules_android//rules/android_library:rule.bzl", + _android_library_macro = "android_library_macro", +) + +def android_feature_module(**attrs): + """Macro to declare a Dynamic Feature Module. + + Generates the following: + + * strings.xml containing a unique split identifier (currently a hash of the fully qualified target label) + * dummy AndroidManifest.xml for the split + * `android_library` to create the split resources + * `android_feature_module` rule to be consumed by `android_application` + + **Attributes** + + Name | Description + --- | --- + name | Required string, split name + custom_package | Optional string, custom package for this split + manifest | Required label, the AndroidManifest.xml to use for this module. + library | Required label, the `android_library` contained in this split. Must only contain assets. + title | Required string, the split title + feature_flags | Optional dict, pass through feature_flags dict for native split binary. + """ + _android_feature_module_macro( + _android_binary = _android_binary, + _android_library = _android_library_macro, + **attrs + ) diff --git a/rules/android_application/android_feature_module_rule.bzl b/rules/android_application/android_feature_module_rule.bzl new file mode 100644 index 0000000..87c57b5 --- /dev/null +++ b/rules/android_application/android_feature_module_rule.bzl @@ -0,0 +1,196 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""android_feature_module rule.""" + +load(":attrs.bzl", "ANDROID_FEATURE_MODULE_ATTRS") +load("@rules_android//rules:java.bzl", _java = "java") +load( + "@rules_android//rules:providers.bzl", + "AndroidFeatureModuleInfo", +) +load("@rules_android//rules:acls.bzl", "acls") +load( + "@rules_android//rules:utils.bzl", + "get_android_toolchain", +) + +def _impl(ctx): + validation = ctx.actions.declare_file(ctx.label.name + "_validation") + inputs = [ctx.attr.binary[ApkInfo].unsigned_apk] + args = ctx.actions.args() + args.add(validation.path) + if ctx.file.manifest: + args.add(ctx.file.manifest.path) + inputs.append(ctx.file.manifest) + else: + args.add("") + args.add(ctx.attr.binary[ApkInfo].unsigned_apk.path) + args.add(ctx.configuration.coverage_enabled) + args.add(ctx.fragments.android.desugar_java8_libs) + args.add(ctx.attr.library.label) + args.add(get_android_toolchain(ctx).xmllint_tool.files_to_run.executable) + args.add(get_android_toolchain(ctx).unzip_tool.files_to_run.executable) + + ctx.actions.run( + executable = ctx.executable._feature_module_validation_script, + inputs = inputs, + outputs = [validation], + arguments = [args], + tools = [ + get_android_toolchain(ctx).xmllint_tool.files_to_run.executable, + get_android_toolchain(ctx).unzip_tool.files_to_run.executable, + ], + mnemonic = "ValidateFeatureModule", + progress_message = "Validating feature module %s" % str(ctx.label), + ) + + return [ + AndroidFeatureModuleInfo( + binary = ctx.attr.binary, + library = ctx.attr.library, + title_id = ctx.attr.title_id, + title_lib = ctx.attr.title_lib, + feature_name = ctx.attr.feature_name, + fused = ctx.attr.fused, + manifest = ctx.file.manifest, + ), + OutputGroupInfo(_validation = depset([validation])), + ] + +android_feature_module = rule( + attrs = ANDROID_FEATURE_MODULE_ATTRS, + fragments = [ + "android", + "java", + ], + implementation = _impl, + provides = [AndroidFeatureModuleInfo], + toolchains = ["@rules_android//toolchains/android:toolchain_type"], + _skylark_testable = True, +) + +def get_feature_module_paths(fqn): + # Given a fqn to an android_feature_module, returns the absolute paths to + # all implicitly generated targets + return struct( + binary = Label("%s_bin" % fqn), + manifest_lib = Label("%s_AndroidManifest" % fqn), + title_strings_xml = Label("%s_title_strings_xml" % fqn), + title_lib = Label("%s_title_lib" % fqn), + ) + +def android_feature_module_macro(_android_binary, _android_library, **attrs): + """android_feature_module_macro. + + Args: + _android_binary: The android_binary rule to use. + _android_library: The android_library rule to use. + **attrs: android_feature_module attributes. + """ + + # Enable dot syntax + attrs = struct(**attrs) + fqn = "//%s:%s" % (native.package_name(), attrs.name) + + required_attrs = ["name", "library", "title"] + if not acls.in_android_feature_splits_dogfood(fqn): + required_attrs.append("manifest") + + # Check for required macro attributes + for attr in required_attrs: + if not getattr(attrs, attr, None): + fail("%s missing required attr <%s>" % (fqn, attr)) + + if hasattr(attrs, "fused") and hasattr(attrs, "manifest"): + fail("%s cannot specify and . Prefer ") + + targets = get_feature_module_paths(fqn) + + tags = getattr(attrs, "tags", []) + transitive_configs = getattr(attrs, "transitive_configs", []) + visibility = getattr(attrs, "visibility", None) + + # Create strings.xml containing split title + title_id = "split_" + str(hash(fqn)).replace("-", "N") + native.genrule( + name = targets.title_strings_xml.name, + outs = [attrs.name + "/res/values/strings.xml"], + cmd = """cat > $@ < + + {title} + +EOF +""".format(title = attrs.title, title_id = title_id), + ) + + # Create AndroidManifest.xml + min_sdk_version = getattr(attrs, "min_sdk_version", "14") or "14" + package = _java.resolve_package_from_label(Label(fqn), getattr(attrs, "custom_package", None)) + native.genrule( + name = targets.manifest_lib.name, + outs = [attrs.name + "/AndroidManifest.xml"], + cmd = """cat > $@ < + + + +EOF +""".format(package = package, min_sdk_version = min_sdk_version), + ) + + # Resource processing requires an android_library target + _android_library( + name = targets.title_lib.name, + custom_package = getattr(attrs, "custom_package", None), + manifest = str(targets.manifest_lib), + resource_files = [str(targets.title_strings_xml)], + tags = tags, + transitive_configs = transitive_configs, + visibility = visibility, + ) + + # Wrap any deps in an android_binary. Will be validated to ensure does not contain any dexes + binary_attrs = { + "name": targets.binary.name, + "custom_package": getattr(attrs, "custom_package", None), + "manifest": str(targets.manifest_lib), + "deps": [attrs.library], + "multidex": "native", + "tags": tags, + "transitive_configs": transitive_configs, + "visibility": visibility, + "feature_flags": getattr(attrs, "feature_flags", None), + "$enable_manifest_merging": False, + } + _android_binary(**binary_attrs) + + android_feature_module( + name = attrs.name, + library = attrs.library, + binary = str(targets.binary), + title_id = title_id, + title_lib = str(targets.title_lib), + feature_name = getattr(attrs, "feature_name", attrs.name), + fused = getattr(attrs, "fused", True), + manifest = getattr(attrs, "manifest", None), + tags = tags, + transitive_configs = transitive_configs, + visibility = visibility, + ) diff --git a/rules/android_application/attrs.bzl b/rules/android_application/attrs.bzl new file mode 100644 index 0000000..33e43fb --- /dev/null +++ b/rules/android_application/attrs.bzl @@ -0,0 +1,89 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Attributes for android_application.""" + +load( + "@rules_android//rules:attrs.bzl", + _attrs = "attrs", +) + +ANDROID_APPLICATION_ATTRS = _attrs.add( + dict( + application_id = attr.string(), + base_module = attr.label(allow_files = False), + bundle_config_file = attr.label( + allow_single_file = [".pb.json"], + doc = ("Path to config.pb.json file, see " + + "https://github.com/google/bundletool/blob/master/src/main/proto/config.proto " + + "for definition.\n\nNote: this attribute is subject to changes which may " + + "require teams to migrate their configurations to a build target."), + ), + app_integrity_config = attr.label( + allow_single_file = [".binarypb"], + doc = "Configuration of the integrity protection options. " + + "Provide a path to a binary .binarypb instance of " + + "https://github.com/google/bundletool/blob/master/src/main/proto/app_integrity_config.proto", + ), + rotation_config = attr.label( + allow_single_file = [".textproto"], + default = None, + ), + custom_package = attr.string(), + feature_modules = attr.label_list(allow_files = False), + _bundle_deploy = attr.label( + allow_single_file = True, + default = ":bundle_deploy.sh_template", + ), + _feature_manifest_script = attr.label( + allow_single_file = True, + cfg = "host", + executable = True, + default = ":gen_android_feature_manifest.sh", + ), + _java_toolchain = attr.label( + default = Label("//tools/jdk:toolchain_android_only"), + ), + _priority_feature_manifest_script = attr.label( + allow_single_file = True, + cfg = "host", + executable = True, + default = ":gen_priority_android_feature_manifest.sh", + ), + _host_javabase = attr.label( + cfg = "host", + default = Label("//tools/jdk:current_java_runtime"), + ), + ), + _attrs.ANDROID_SDK, +) + +ANDROID_FEATURE_MODULE_ATTRS = dict( + binary = attr.label(), + feature_name = attr.string(), + library = attr.label( + allow_rules = ["android_library"], + mandatory = True, + doc = "android_library target to include as a feature split.", + ), + manifest = attr.label(allow_single_file = True), + title_id = attr.string(), + title_lib = attr.string(), + _feature_module_validation_script = attr.label( + allow_single_file = True, + cfg = "host", + executable = True, + default = ":feature_module_validation.sh", + ), +) diff --git a/rules/android_application/bundle_deploy.sh_template b/rules/android_application/bundle_deploy.sh_template new file mode 100644 index 0000000..37f6d4d --- /dev/null +++ b/rules/android_application/bundle_deploy.sh_template @@ -0,0 +1,26 @@ +#!/bin/bash --posix + +bundletool="%bundletool_path%" +aab="%aab%" +key="%key%" +tmp="$(mktemp /tmp/XXXXbundle.apks)" + +function cleanup { + rm -r "$tmp" +} +trap cleanup EXIT + +java -jar "$bundletool" build-apks \ + --bundle="$aab" \ + --output="$tmp" \ + --overwrite \ + --local-testing \ + --ks="$key" \ + --ks-pass=pass:android \ + --ks-key-alias=androiddebugkey \ + --key-pass=pass:android || exit + +java -jar "$bundletool" install-apks \ + --adb="$(which adb)" \ + --apks "$tmp" \ + --modules=_ALL_ || exit diff --git a/rules/android_application/feature_module_validation.sh b/rules/android_application/feature_module_validation.sh new file mode 100644 index 0000000..405b77d --- /dev/null +++ b/rules/android_application/feature_module_validation.sh @@ -0,0 +1,78 @@ +#!/bin/bash --posix +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +out="${1}" +manifest="${2}" +apk="${3}" +is_coverage="${4}" +is_java8="${5}" +lib_label="${6}" +xmllint="${7}" +unzip="${8}" + +if [[ -n "$manifest" ]]; then + node_count=$("$xmllint" --xpath "count(//manifest/*)" "$manifest") + module_count=$("$xmllint" --xpath "count(//manifest/*[local-name()='module'])" "$manifest") + application_count=$("$xmllint" --xpath "count(//manifest/*[local-name()='application'])" "$manifest") + application_attr_count=$("$xmllint" --xpath "count(//manifest/application/@*)" "$manifest") + application_content_count=$("$xmllint" --xpath "count(//manifest/application/*)" "$manifest") + module_title=$("$xmllint" --xpath "string(//manifest/*[local-name()='module'][1]/@*[local-name()='title'])" "$manifest") + valid=0 + + # Valid manifest, containing a dist:module and an empty + if [[ "$node_count" == "2" && + "$module_count" == "1" && + "$application_count" == "1" && + "$application_attr_count" == "0" && + "$application_content_count" == "0" ]]; then + valid=1 + fi + + # Valid manifest, containing a dist:module + if [[ "$node_count" == "1" && "$module_count" == "1" ]]; then + valid=1 + fi + + if [[ "$valid" == "0" ]]; then + echo "" + echo "$manifest should only contain a single element (and optional empty ), nothing else" + echo "Manifest contents: " + cat "$manifest" + exit 1 + fi + + if [[ "$module_title" != "\${MODULE_TITLE}" ]]; then + echo "" + echo "$manifest dist:title should be \${MODULE_TITLE} placeholder" + echo "" + exit 1 + fi +fi + +# Skip dex validation when running under code coverage. +# When running under code coverage an additional dep is implicitly added to all +# binary targets, causing a validation failure. +if [[ "$is_coverage" == "false" ]]; then + dexes=$("$unzip" -l "$apk" | grep ".dex" | wc -l) + if [[ ("$is_java8" == "true" && "$dexes" -gt 1 ) || ( "$is_java8" == "false" && "$dexes" -gt 0)]]; then + echo "" + echo "android_feature_module does not support Java or Kotlin sources." + echo "Check $lib_label for any srcs or deps." + echo "" + exit 1 + fi +fi + +touch "$out" diff --git a/rules/android_application/gen_android_feature_manifest.sh b/rules/android_application/gen_android_feature_manifest.sh new file mode 100644 index 0000000..5b5552e --- /dev/null +++ b/rules/android_application/gen_android_feature_manifest.sh @@ -0,0 +1,51 @@ +#!/bin/bash --posix +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +out_manifest="${1}" +base_apk="${2}" +package="${3}" +split="${4}" +title_id="${5}" +fused="${6}" +aapt="${7}" + +aapt_cmd="$aapt dump xmltree $base_apk --file AndroidManifest.xml" +version_code=$(${aapt_cmd} | grep "http://schemas.android.com/apk/res/android:versionCode" | cut -d "=" -f2 | head -n 1 ) +if [[ -z "$version_code" ]] +then + echo "Base app missing versionCode in AndroidManifest.xml" + exit 1 +fi + +cat >$out_manifest < + + + + + + + + + + +EOF diff --git a/rules/android_application/gen_priority_android_feature_manifest.sh b/rules/android_application/gen_priority_android_feature_manifest.sh new file mode 100644 index 0000000..cf646c8 --- /dev/null +++ b/rules/android_application/gen_priority_android_feature_manifest.sh @@ -0,0 +1,49 @@ +#!/bin/bash --posix +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +out_manifest="${1}" +base_apk="${2}" +package="${3}" +split="${4}" +aapt="${5}" + +aapt_cmd="$aapt dump xmltree $base_apk --file AndroidManifest.xml" +version_code=$(${aapt_cmd} | grep "http://schemas.android.com/apk/res/android:versionCode" | cut -d "=" -f2 | head -n 1) +min_sdk=$(${aapt_cmd} | grep "http://schemas.android.com/apk/res/android:minSdkVersion" | cut -d "=" -f2 | head -n 1) +if [[ -z "$version_code" ]] +then + echo "Base app missing versionCode in AndroidManifest.xml" + exit 1 +fi + +if [[ -z "$min_sdk" ]] +then + echo "Base app missing minsdk in AndroidManifest.xml" + exit 1 +fi + +cat >$out_manifest < + + + + + +EOF diff --git a/rules/android_binary.bzl b/rules/android_binary.bzl new file mode 100644 index 0000000..5d33512 --- /dev/null +++ b/rules/android_binary.bzl @@ -0,0 +1,42 @@ +# Copyright 2019 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel rule for building an APK.""" + +load(":migration_tag_DONOTUSE.bzl", "add_migration_tag") +load("@rules_android//rules/android_binary_internal:rule.bzl", "android_binary_internal_macro") + +def android_binary(**attrs): + """Bazel android_binary rule. + + https://docs.bazel.build/versions/master/be/android.html#android_binary + + Args: + **attrs: Rule attributes + """ + android_binary_internal_name = ":%s_RESOURCES_DO_NOT_USE" % attrs["name"] + android_binary_internal_macro( + **dict( + attrs, + name = android_binary_internal_name[1:], + visibility = ["//visibility:private"], + ) + ) + + attrs.pop("$enable_manifest_merging", None) + + native.android_binary( + application_resources = android_binary_internal_name, + **add_migration_tag(attrs) + ) diff --git a/rules/android_binary_internal/BUILD b/rules/android_binary_internal/BUILD new file mode 100644 index 0000000..2440016 --- /dev/null +++ b/rules/android_binary_internal/BUILD @@ -0,0 +1,18 @@ +# The android_binary_internal rule. + +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") + +licenses(["notice"]) + +filegroup( + name = "all_files", + srcs = glob(["**"]), +) + +bzl_library( + name = "bzl", + srcs = glob(["*.bzl"]), + deps = [ + "@rules_android//rules:common_bzl", + ], +) diff --git a/rules/android_binary_internal/attrs.bzl b/rules/android_binary_internal/attrs.bzl new file mode 100644 index 0000000..5a7bca7 --- /dev/null +++ b/rules/android_binary_internal/attrs.bzl @@ -0,0 +1,69 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Attributes.""" + +load( + "@rules_android//rules:attrs.bzl", + _attrs = "attrs", +) + +ATTRS = _attrs.replace( + _attrs.add( + dict( + deps = attr.label_list( + allow_files = True, + allow_rules = [ + "aar_import", + "android_library", + "cc_library", + "java_import", + "java_library", + "java_lite_proto_library", + ], + providers = [ + [CcInfo], + [JavaInfo], + ["AndroidResourcesInfo", "AndroidAssetsInfo"], + ], + cfg = android_common.multi_cpu_configuration, + ), + enable_data_binding = attr.bool(), + instruments = attr.label(), + manifest_values = attr.string_dict(), + manifest_merger = attr.string( + default = "auto", + values = ["auto", "legacy", "android", "force_android"], + ), + native_target = attr.label( + allow_files = False, + allow_rules = ["android_binary", "android_test"], + ), + resource_configuration_filters = attr.string_list(), + densities = attr.string_list(), + nocompress_extensions = attr.string_list(), + shrink_resources = _attrs.tristate.create( + default = _attrs.tristate.auto, + ), + _defined_resource_files = attr.bool(default = False), + _enable_manifest_merging = attr.bool(default = True), + ), + _attrs.COMPILATION, + _attrs.DATA_CONTEXT, + ), + # TODO(b/167599192): don't override manifest attr to remove .xml file restriction. + manifest = attr.label( + allow_single_file = True, + ), +) diff --git a/rules/android_binary_internal/impl.bzl b/rules/android_binary_internal/impl.bzl new file mode 100644 index 0000000..6838a81 --- /dev/null +++ b/rules/android_binary_internal/impl.bzl @@ -0,0 +1,111 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Implementation.""" + +load("@rules_android//rules:acls.bzl", "acls") +load("@rules_android//rules:java.bzl", "java") +load( + "@rules_android//rules:processing_pipeline.bzl", + "ProviderInfo", + "processing_pipeline", +) +load("@rules_android//rules:resources.bzl", _resources = "resources") +load("@rules_android//rules:utils.bzl", "compilation_mode", "get_android_toolchain", "utils") + +def _process_resources(ctx, java_package, **unused_ctxs): + packaged_resources_ctx = _resources.package( + ctx, + assets = ctx.files.assets, + assets_dir = ctx.attr.assets_dir, + resource_files = ctx.files.resource_files, + manifest = ctx.file.manifest, + manifest_values = utils.expand_make_vars(ctx, ctx.attr.manifest_values), + resource_configs = ctx.attr.resource_configuration_filters, + densities = ctx.attr.densities, + nocompress_extensions = ctx.attr.nocompress_extensions, + java_package = java_package, + compilation_mode = compilation_mode.get(ctx), + shrink_resources = ctx.attr.shrink_resources, + use_android_resource_shrinking = ctx.fragments.android.use_android_resource_shrinking, + use_android_resource_cycle_shrinking = ctx.fragments.android.use_android_resource_cycle_shrinking, + use_legacy_manifest_merger = use_legacy_manifest_merger(ctx), + should_throw_on_conflict = not acls.in_allow_resource_conflicts(str(ctx.label)), + enable_data_binding = ctx.attr.enable_data_binding, + enable_manifest_merging = ctx.attr._enable_manifest_merging, + deps = utils.dedupe_split_attr(ctx.split_attr.deps), + instruments = ctx.attr.instruments, + aapt = get_android_toolchain(ctx).aapt2.files_to_run, + android_jar = ctx.attr._android_sdk[AndroidSdkInfo].android_jar, + legacy_merger = ctx.attr._android_manifest_merge_tool.files_to_run, + xsltproc = ctx.attr._xsltproc_tool.files_to_run, + instrument_xslt = ctx.file._add_g3itr_xslt, + busybox = get_android_toolchain(ctx).android_resources_busybox.files_to_run, + host_javabase = ctx.attr._host_javabase, + ) + return ProviderInfo( + name = "packaged_resources_ctx", + value = packaged_resources_ctx, + ) + +def use_legacy_manifest_merger(ctx): + """Whether legacy manifest merging is enabled. + + Args: + ctx: The context. + + Returns: + Boolean indicating whether legacy manifest merging is enabled. + """ + manifest_merger = ctx.attr.manifest_merger + android_manifest_merger = ctx.fragments.android.manifest_merger + + if android_manifest_merger == "force_android": + return False + if manifest_merger == "auto": + manifest_merger = android_manifest_merger + + return manifest_merger == "legacy" + +def finalize(ctx, providers, validation_outputs, **unused_ctxs): + providers.append( + OutputGroupInfo( + _validation = depset(validation_outputs), + ), + ) + return providers + +# Order dependent, as providers will not be available to downstream processors +# that may depend on the provider. Iteration order for a dictionary is based on +# insertion. +PROCESSORS = dict( + ResourceProcessor = _process_resources, +) + +_PROCESSING_PIPELINE = processing_pipeline.make_processing_pipeline( + processors = PROCESSORS, + finalize = finalize, +) + +def impl(ctx): + """The rule implementation. + + Args: + ctx: The context. + + Returns: + A list of providers. + """ + java_package = java.resolve_package_from_label(ctx.label, ctx.attr.custom_package) + return processing_pipeline.run(ctx, java_package, _PROCESSING_PIPELINE) diff --git a/rules/android_binary_internal/rule.bzl b/rules/android_binary_internal/rule.bzl new file mode 100644 index 0000000..3070688 --- /dev/null +++ b/rules/android_binary_internal/rule.bzl @@ -0,0 +1,90 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Starlark Android Binary for Android Rules.""" + +load(":attrs.bzl", "ATTRS") +load(":impl.bzl", "impl") +load( + "@rules_android//rules:attrs.bzl", + _attrs = "attrs", +) + +_DEFAULT_ALLOWED_ATTRS = ["name", "visibility", "tags", "testonly", "transitive_configs", "$enable_manifest_merging"] + +_DEFAULT_PROVIDES = [AndroidApplicationResourceInfo, OutputGroupInfo] + +def make_rule( + attrs = ATTRS, + implementation = impl, + provides = _DEFAULT_PROVIDES): + """Makes the rule. + + Args: + attrs: A dict. The attributes for the rule. + implementation: A function. The rule's implementation method. + provides: A list. The providers that the rule must provide. + + Returns: + A rule. + """ + return rule( + attrs = attrs, + implementation = implementation, + provides = provides, + toolchains = ["@rules_android//toolchains/android:toolchain_type"], + _skylark_testable = True, + fragments = [ + "android", + "java", + ], + ) + +android_binary_internal = make_rule() + +def sanitize_attrs(attrs, allowed_attrs = ATTRS.keys()): + """Sanitizes the attributes. + + The android_binary_internal has a subset of the android_binary attributes, but is + called from the android_binary macro with the same full set of attributes. This removes + any unnecessary attributes. + + Args: + attrs: A dict. The attributes for the android_binary_internal rule. + allowed_attrs: The list of attribute keys to keep. + + Returns: + A dictionary containing valid attributes. + """ + for attr_name in attrs.keys(): + if attr_name not in allowed_attrs and attr_name not in _DEFAULT_ALLOWED_ATTRS: + attrs.pop(attr_name, None) + + # Some teams set this to a boolean/None which works for the native attribute but breaks + # the Starlark attribute. + if attr_name == "shrink_resources": + if attrs[attr_name] == None: + attrs.pop(attr_name, None) + else: + attrs[attr_name] = _attrs.tristate.normalize(attrs[attr_name]) + + return attrs + +def android_binary_internal_macro(**attrs): + """android_binary_internal rule. + + Args: + **attrs: Rule attributes + """ + android_binary_internal(**sanitize_attrs(attrs)) diff --git a/rules/android_library/BUILD b/rules/android_library/BUILD new file mode 100644 index 0000000..09ca5b8 --- /dev/null +++ b/rules/android_library/BUILD @@ -0,0 +1,21 @@ +# The android_library rule. + +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") + +licenses(["notice"]) + +exports_files(["rule.bzl"]) + +filegroup( + name = "all_files", + srcs = glob(["**"]), +) + +bzl_library( + name = "bzl", + srcs = glob(["*.bzl"]), + deps = [ + "@rules_android//rules:common_bzl", + "@rules_android//rules/flags:bzl", + ], +) diff --git a/rules/android_library/attrs.bzl b/rules/android_library/attrs.bzl new file mode 100644 index 0000000..8e38b01 --- /dev/null +++ b/rules/android_library/attrs.bzl @@ -0,0 +1,73 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Attributes.""" + +load( + "@rules_android//rules:attrs.bzl", + _attrs = "attrs", +) + +ATTRS = _attrs.add( + dict( + deps = attr.label_list( + providers = [ + [CcInfo], + [JavaInfo], + ], + ), + enable_data_binding = attr.bool(default = False), + exported_plugins = attr.label_list( + providers = [ + [JavaPluginInfo], + ], + cfg = "host", + ), + exports = attr.label_list( + providers = [ + [CcInfo], + [JavaInfo], + ], + ), + exports_manifest = + _attrs.tristate.create(default = _attrs.tristate.no), + idl_import_root = attr.string(), + idl_parcelables = attr.label_list(allow_files = [".aidl"]), + idl_preprocessed = attr.label_list(allow_files = [".aidl"]), + idl_srcs = attr.label_list(allow_files = [".aidl"]), + neverlink = attr.bool(default = False), + proguard_specs = attr.label_list(allow_files = True), + srcs = attr.label_list( + allow_files = [".java", ".srcjar"], + ), + # TODO(b/127517031): Remove these entries once fixed. + _defined_assets = attr.bool(default = False), + _defined_assets_dir = attr.bool(default = False), + _defined_idl_import_root = attr.bool(default = False), + _defined_idl_parcelables = attr.bool(default = False), + _defined_idl_srcs = attr.bool(default = False), + _defined_local_resources = attr.bool(default = False), + _java_toolchain = attr.label( + default = Label("//tools/jdk:toolchain_android_only"), + ), + # TODO(str): Remove when fully migrated to android_instrumentation_test + _android_test_migration = attr.bool(default = False), + _flags = attr.label( + default = "@rules_android//rules/flags", + ), + _package_name = attr.string(), # for sending the package name to the outputs callback + ), + _attrs.COMPILATION, + _attrs.DATA_CONTEXT, +) diff --git a/rules/android_library/impl.bzl b/rules/android_library/impl.bzl new file mode 100644 index 0000000..286d7b5 --- /dev/null +++ b/rules/android_library/impl.bzl @@ -0,0 +1,534 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Implementation.""" + +load("@rules_android//rules:acls.bzl", "acls") +load("@rules_android//rules:attrs.bzl", _attrs = "attrs") +load("@rules_android//rules:common.bzl", _common = "common") +load("@rules_android//rules:data_binding.bzl", _data_binding = "data_binding") +load("@rules_android//rules:idl.bzl", _idl = "idl") +load("@rules_android//rules:intellij.bzl", _intellij = "intellij") +load("@rules_android//rules:java.bzl", _java = "java") +load( + "@rules_android//rules:processing_pipeline.bzl", + "ProviderInfo", + "processing_pipeline", +) +load("@rules_android//rules:proguard.bzl", _proguard = "proguard") +load("@rules_android//rules:resources.bzl", _resources = "resources") +load("@rules_android//rules:utils.bzl", "get_android_sdk", "get_android_toolchain", "log", "utils") +load("@rules_android//rules/flags:flags.bzl", _flags = "flags") + +_USES_DEPRECATED_IMPLICIT_EXPORT_ERROR = ( + "The android_library rule will be deprecating the use of deps to export " + + "targets implicitly. " + + "Please use android_library.exports to explicitly specify the exported " + + "targets of %s." +) + +_SRCS_CONTAIN_RESOURCE_LABEL_ERROR = ( + "The srcs attribute of an android_library rule should not contain label " + + "with resources %s" +) + +_IDL_IMPORT_ROOT_SET_WITHOUT_SRCS_OR_PARCELABLES_ERROR = ( + "The 'idl_import_root' attribute of the android_library rule was set, " + + "but neither 'idl_srcs' nor 'idl_parcelables' were specified." +) + +_IDL_SRC_FROM_DIFFERENT_PACKAGE_ERROR = ( + "Do not import '%s' directly. You should either move the file to this " + + "package or depend on an appropriate rule there." +) + +# Android library AAR context attributes. +_PROVIDERS = "providers" +_VALIDATION_OUTPUTS = "validation_outputs" + +_AARContextInfo = provider( + "Android library AAR context object", + fields = { + _PROVIDERS: "The list of all providers to propagate.", + _VALIDATION_OUTPUTS: "List of outputs given to OutputGroupInfo _validation group", + }, +) + +def _uses_deprecated_implicit_export(ctx): + if not ctx.attr.deps: + return False + return not (ctx.files.srcs or + ctx.files.idl_srcs or + ctx.attr._defined_assets or + ctx.files.resource_files or + ctx.attr.manifest) + +def _uses_resources_and_deps_without_srcs(ctx): + if not ctx.attr.deps: + return False + if not (ctx.attr._defined_assets or + ctx.files.resource_files or + ctx.attr.manifest): + return False + return not (ctx.files.srcs or ctx.files.idl_srcs) + +def _check_deps_without_java_srcs(ctx): + if not ctx.attr.deps or ctx.files.srcs or ctx.files.idl_srcs: + return False + gfn = getattr(ctx.attr, "generator_function", "") + if _uses_deprecated_implicit_export(ctx): + if (acls.in_android_library_implicit_exports_generator_functions(gfn) or + acls.in_android_library_implicit_exports(str(ctx.label))): + return True + else: + # TODO(b/144163743): add a test for this. + log.error(_USES_DEPRECATED_IMPLICIT_EXPORT_ERROR % ctx.label) + if _uses_resources_and_deps_without_srcs(ctx): + if (acls.in_android_library_resources_without_srcs_generator_functions(gfn) or + acls.in_android_library_resources_without_srcs(str(ctx.label))): + return True + return False + +def _validate_rule_context(ctx): + # Verify that idl_import_root is specified with idl_src or idl_parcelables. + if (ctx.attr._defined_idl_import_root and + not (ctx.attr._defined_idl_srcs or ctx.attr._defined_idl_parcelables)): + log.error(_IDL_IMPORT_ROOT_SET_WITHOUT_SRCS_OR_PARCELABLES_ERROR) + + # Verify that idl_srcs are not from another package. + for idl_src in ctx.attr.idl_srcs: + if ctx.label.package != idl_src.label.package: + log.error(_IDL_SRC_FROM_DIFFERENT_PACKAGE_ERROR % idl_src.label) + + return struct( + enable_deps_without_srcs = _check_deps_without_java_srcs(ctx), + ) + +def _exceptions_processor(ctx, **unused_ctxs): + return ProviderInfo( + name = "exceptions_ctx", + value = _validate_rule_context(ctx), + ) + +def _process_resources(ctx, java_package, **unused_ctxs): + # exports_manifest can be overridden by a bazel flag. + if ctx.attr.exports_manifest == _attrs.tristate.auto: + exports_manifest = ctx.fragments.android.get_exports_manifest_default + else: + exports_manifest = ctx.attr.exports_manifest == _attrs.tristate.yes + + # Process Android Resources + resources_ctx = _resources.process( + ctx, + manifest = ctx.file.manifest, + resource_files = ctx.attr.resource_files, + defined_assets = ctx.attr._defined_assets, + assets = ctx.attr.assets, + defined_assets_dir = ctx.attr._defined_assets_dir, + assets_dir = ctx.attr.assets_dir, + exports_manifest = exports_manifest, + java_package = java_package, + custom_package = ctx.attr.custom_package, + neverlink = ctx.attr.neverlink, + enable_data_binding = ctx.attr.enable_data_binding, + deps = ctx.attr.deps, + exports = ctx.attr.exports, + + # Processing behavior changing flags. + enable_res_v3 = _flags.get(ctx).android_enable_res_v3, + # TODO(b/144163743): remove fix_resource_transitivity, which was only added to emulate + # misbehavior on the Java side. + fix_resource_transitivity = bool(ctx.attr.srcs), + fix_export_exporting = acls.in_fix_export_exporting_rollout(str(ctx.label)), + propagate_resources = not ctx.attr._android_test_migration, + + # Tool and Processing related inputs + aapt = get_android_toolchain(ctx).aapt2.files_to_run, + android_jar = get_android_sdk(ctx).android_jar, + android_kit = get_android_toolchain(ctx).android_kit.files_to_run, + busybox = get_android_toolchain(ctx).android_resources_busybox.files_to_run, + java_toolchain = _common.get_java_toolchain(ctx), + host_javabase = _common.get_host_javabase(ctx), + instrument_xslt = utils.only(get_android_toolchain(ctx).add_g3itr_xslt.files.to_list()), + res_v3_dummy_manifest = utils.only( + get_android_toolchain(ctx).res_v3_dummy_manifest.files.to_list(), + ), + res_v3_dummy_r_txt = utils.only( + get_android_toolchain(ctx).res_v3_dummy_r_txt.files.to_list(), + ), + xsltproc = get_android_toolchain(ctx).xsltproc_tool.files_to_run, + zip_tool = get_android_toolchain(ctx).zip_tool.files_to_run, + ) + + # TODO(b/139305816): Remove the ability for android_library to be added in + # the srcs attribute of another android_library. + if resources_ctx.defines_resources: + # Verify that srcs do no contain labels. + for src in ctx.attr.srcs: + if AndroidResourcesInfo in src: + log.error(_SRCS_CONTAIN_RESOURCE_LABEL_ERROR % + src[AndroidResourcesInfo].label) + + return ProviderInfo( + name = "resources_ctx", + value = resources_ctx, + ) + +def _process_idl(ctx, **unused_sub_ctxs): + return ProviderInfo( + name = "idl_ctx", + value = _idl.process( + ctx, + idl_srcs = ctx.files.idl_srcs, + idl_parcelables = ctx.files.idl_parcelables, + idl_import_root = + ctx.attr.idl_import_root if ctx.attr._defined_idl_import_root else None, + idl_preprocessed = ctx.files.idl_preprocessed, + deps = utils.collect_providers(AndroidIdlInfo, ctx.attr.deps), + exports = utils.collect_providers(AndroidIdlInfo, ctx.attr.exports), + aidl = get_android_sdk(ctx).aidl, + aidl_lib = get_android_sdk(ctx).aidl_lib, + aidl_framework = get_android_sdk(ctx).framework_aidl, + ), + ) + +def _process_data_binding(ctx, java_package, resources_ctx, **unused_sub_ctxs): + return ProviderInfo( + name = "db_ctx", + value = _data_binding.process( + ctx, + defines_resources = resources_ctx.defines_resources, + enable_data_binding = ctx.attr.enable_data_binding, + java_package = java_package, + layout_info = resources_ctx.data_binding_layout_info, + deps = utils.collect_providers(DataBindingV2Info, ctx.attr.deps), + exports = utils.collect_providers(DataBindingV2Info, ctx.attr.exports), + data_binding_exec = get_android_toolchain(ctx).data_binding_exec.files_to_run, + data_binding_annotation_processor = + get_android_toolchain(ctx).data_binding_annotation_processor[JavaPluginInfo], + data_binding_annotation_template = + utils.only(get_android_toolchain(ctx).data_binding_annotation_template.files.to_list()), + ), + ) + +def _process_proguard(ctx, idl_ctx, **unused_sub_ctxs): + return ProviderInfo( + name = "proguard_ctx", + value = _proguard.process( + ctx, + proguard_configs = ctx.files.proguard_specs, + proguard_spec_providers = utils.collect_providers( + ProguardSpecProvider, + ctx.attr.deps, + ctx.attr.exports, + ctx.attr.plugins, + ctx.attr.exported_plugins, + idl_ctx.idl_deps, + ), + proguard_allowlister = + get_android_toolchain(ctx).proguard_allowlister.files_to_run, + ), + ) + +def _process_jvm(ctx, exceptions_ctx, resources_ctx, idl_ctx, db_ctx, **unused_sub_ctxs): + java_info = _java.compile_android( + ctx, + ctx.outputs.lib_jar, + ctx.outputs.lib_src_jar, + srcs = ctx.files.srcs + idl_ctx.idl_java_srcs + db_ctx.java_srcs, + javac_opts = ctx.attr.javacopts + db_ctx.javac_opts, + r_java = resources_ctx.r_java, + deps = + utils.collect_providers(JavaInfo, ctx.attr.deps, idl_ctx.idl_deps), + exports = utils.collect_providers(JavaInfo, ctx.attr.exports), + plugins = ( + utils.collect_providers(JavaPluginInfo, ctx.attr.plugins) + + db_ctx.java_plugins + ), + exported_plugins = utils.collect_providers( + JavaPluginInfo, + ctx.attr.exported_plugins, + ), + annotation_processor_additional_outputs = ( + db_ctx.java_annotation_processor_additional_outputs + ), + annotation_processor_additional_inputs = ( + db_ctx.java_annotation_processor_additional_inputs + ), + enable_deps_without_srcs = exceptions_ctx.enable_deps_without_srcs, + neverlink = ctx.attr.neverlink, + strict_deps = "DEFAULT", + java_toolchain = _common.get_java_toolchain(ctx), + ) + + return ProviderInfo( + name = "jvm_ctx", + value = struct( + java_info = java_info, + providers = [java_info], + ), + ) + +def _process_aar(ctx, java_package, resources_ctx, proguard_ctx, **unused_ctx): + aar_ctx = { + _PROVIDERS: [], + _VALIDATION_OUTPUTS: [], + } + + starlark_aar = _resources.make_aar( + ctx, + manifest = resources_ctx.starlark_processed_manifest, + assets = ctx.files.assets, + assets_dir = ctx.attr.assets_dir, + resource_files = resources_ctx.starlark_processed_resources if not ctx.attr.neverlink else [], + class_jar = ctx.outputs.lib_jar, + r_txt = resources_ctx.starlark_r_txt, + proguard_specs = proguard_ctx.proguard_configs, + busybox = get_android_toolchain(ctx).android_resources_busybox.files_to_run, + host_javabase = _common.get_host_javabase(ctx), + ) + + # TODO(b/170409221): Clean this up once Starlark migration is complete. Create and propagate + # a native aar info provider with the Starlark artifacts to avoid breaking downstream + # targets. + if not ctx.attr.neverlink: + aar_ctx[_PROVIDERS].append(AndroidLibraryAarInfo( + aar = starlark_aar, + manifest = resources_ctx.starlark_processed_manifest, + aars_from_deps = utils.collect_providers( + AndroidLibraryAarInfo, + ctx.attr.deps, + ctx.attr.exports, + ), + defines_local_resources = resources_ctx.defines_resources, + )) + + return ProviderInfo( + name = "aar_ctx", + value = _AARContextInfo(**aar_ctx), + ) + +def _process_native(ctx, idl_ctx, **unused_ctx): + return ProviderInfo( + name = "native_ctx", + value = struct( + providers = [ + AndroidNativeLibsInfo( + depset( + transitive = [ + p.native_libs + for p in utils.collect_providers( + AndroidNativeLibsInfo, + ctx.attr.deps, + ctx.attr.exports, + ) + ], + order = "preorder", + ), + ), + AndroidCcLinkParamsInfo( + cc_common.merge_cc_infos( + cc_infos = [ + info.cc_link_params_info + for info in utils.collect_providers( + JavaInfo, + ctx.attr.deps, + ctx.attr.exports, + idl_ctx.idl_deps, + ) + ] + + [ + info.link_params + for info in utils.collect_providers( + AndroidCcLinkParamsInfo, + ctx.attr.deps, + ctx.attr.exports, + idl_ctx.idl_deps, + ) + ] + + utils.collect_providers( + CcInfo, + ctx.attr.deps, + ctx.attr.exports, + idl_ctx.idl_deps, + ), + ), + ), + ], + ), + ) + +def _process_intellij(ctx, java_package, resources_ctx, idl_ctx, jvm_ctx, **unused_sub_ctxs): + android_ide_info = _intellij.make_android_ide_info( + ctx, + java_package = java_package, + manifest = ctx.file.manifest, + defines_resources = resources_ctx.defines_resources, + merged_manifest = resources_ctx.merged_manifest, + resources_apk = resources_ctx.resources_apk, + r_jar = utils.only(resources_ctx.r_java.outputs.jars) if resources_ctx.r_java else None, + idl_import_root = idl_ctx.idl_import_root, + idl_srcs = idl_ctx.idl_srcs, + idl_java_srcs = idl_ctx.idl_java_srcs, + java_info = jvm_ctx.java_info, + signed_apk = None, # signed_apk, always empty for android_library. + aar = getattr(ctx.outputs, "aar", None), # Deprecate aar for android_library. + apks_under_test = [], # apks_under_test, always empty for android_library + native_libs = dict(), # nativelibs, always empty for android_library + idlclass = get_android_toolchain(ctx).idlclass.files_to_run, + host_javabase = _common.get_host_javabase(ctx), + ) + return ProviderInfo( + name = "intellij_ctx", + value = struct( + android_ide_info = android_ide_info, + providers = [android_ide_info], + ), + ) + +def _process_coverage(ctx, **unused_ctx): + return ProviderInfo( + name = "coverage_ctx", + value = struct( + providers = [ + coverage_common.instrumented_files_info( + ctx, + dependency_attributes = ["assets", "deps", "exports"], + ), + ], + ), + ) + +# Order dependent, as providers will not be available to downstream processors +# that may depend on the provider. Iteration order for a dictionary is based on +# insertion. +PROCESSORS = dict( + ExceptionsProcessor = _exceptions_processor, + ResourceProcessor = _process_resources, + IdlProcessor = _process_idl, + DataBindingProcessor = _process_data_binding, + JvmProcessor = _process_jvm, + ProguardProcessor = _process_proguard, + AarProcessor = _process_aar, + NativeProcessor = _process_native, + IntelliJProcessor = _process_intellij, + CoverageProcessor = _process_coverage, +) + +# TODO(b/119560471): Deprecate the usage of legacy providers. +def _make_legacy_provider(intellij_ctx, jvm_ctx, providers): + return struct( + android = _intellij.make_legacy_android_provider(intellij_ctx.android_ide_info), + java = struct( + annotation_processing = jvm_ctx.java_info.annotation_processing, + outputs = jvm_ctx.java_info.outputs, + source_jars = depset(jvm_ctx.java_info.source_jars), + transitive_deps = jvm_ctx.java_info.transitive_compile_time_jars, + transitive_runtime_deps = jvm_ctx.java_info.transitive_runtime_jars, + transitive_source_jars = jvm_ctx.java_info.transitive_source_jars, + ), + providers = providers, + ) + +def finalize( + ctx, + resources_ctx, + intellij_ctx, + jvm_ctx, + proguard_ctx, + providers, + validation_outputs, + **unused_ctxs): + """Creates the DefaultInfo and OutputGroupInfo providers. + + Args: + ctx: The context. + resources_ctx: ProviderInfo. The resources ctx. + intellij_ctx: ProviderInfo. The intellij ctx. + jvm_ctx: ProviderInfo. The jvm ctx. + proguard_ctx: ProviderInfo. The proguard ctx. + providers: sequence of providers. The providers to propagate. + validation_outputs: sequence of Files. The validation outputs. + **unused_ctxs: Unused ProviderInfo. + + Returns: + A struct with Android and Java legacy providers and a list of providers. + """ + transitive_runfiles = [] + if not ctx.attr.neverlink: + for p in utils.collect_providers( + DefaultInfo, + ctx.attr.deps, + ctx.attr.exports, + ): + transitive_runfiles.append(p.data_runfiles.files) + transitive_runfiles.append(p.default_runfiles.files) + runfiles = ctx.runfiles( + files = ( + (resources_ctx.r_java.runtime_output_jars if resources_ctx.r_java and not ctx.attr.neverlink else []) + + ([ctx.outputs.lib_jar] if (ctx.attr.srcs or ctx.attr.idl_srcs) and not ctx.attr.neverlink else []) + ), + transitive_files = depset(transitive = transitive_runfiles), + collect_default = True, + ) + files = [ctx.outputs.lib_jar] + if getattr(ctx.outputs, "resources_src_jar", None): + files.append(ctx.outputs.resources_src_jar) + if getattr(ctx.outputs, "resources_jar", None): + files.append(ctx.outputs.resources_jar) + + providers.extend([ + DefaultInfo( + files = depset(files), + runfiles = runfiles, + ), + OutputGroupInfo( + compilation_outputs = depset([ctx.outputs.lib_jar]), + _source_jars = depset( + [ctx.outputs.lib_src_jar], + transitive = [jvm_ctx.java_info.transitive_source_jars], + ), + _direct_source_jars = depset([ctx.outputs.lib_src_jar]), + _hidden_top_level_INTERNAL_ = depset( + resources_ctx.validation_results, + transitive = [ + info._hidden_top_level_INTERNAL_ + for info in utils.collect_providers( + OutputGroupInfo, + ctx.attr.deps, + ctx.attr.exports, + ) + ] + [proguard_ctx.transitive_proguard_configs], + ), + _validation = depset(validation_outputs), + ), + ]) + return _make_legacy_provider(intellij_ctx, jvm_ctx, providers) + +_PROCESSING_PIPELINE = processing_pipeline.make_processing_pipeline( + processors = PROCESSORS, + finalize = finalize, +) + +def impl(ctx): + """The rule implementation. + + Args: + ctx: The context. + + Returns: + A legacy struct provider. + """ + java_package = _java.resolve_package_from_label(ctx.label, ctx.attr.custom_package) + return processing_pipeline.run(ctx, java_package, _PROCESSING_PIPELINE) diff --git a/rules/android_library/rule.bzl b/rules/android_library/rule.bzl new file mode 100644 index 0000000..91e7c33 --- /dev/null +++ b/rules/android_library/rule.bzl @@ -0,0 +1,146 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""android_library rule.""" + +load("@rules_android//rules:acls.bzl", "acls") +load(":attrs.bzl", _ATTRS = "ATTRS") +load(":impl.bzl", _impl = "impl") +load( + "@rules_android//rules:attrs.bzl", + _attrs = "attrs", +) + +def _outputs(name, _package_name, _defined_local_resources): + outputs = dict( + lib_jar = "lib%{name}.jar", + lib_src_jar = "lib%{name}-src.jar", + aar = "%{name}.aar", + ) + + if _defined_local_resources: + # TODO(b/177261846): resource-related predeclared outputs need to be re-pointed at the + # corresponding artifacts in the Starlark pipeline. + label = "//" + _package_name + ":" + name + if acls.in_android_library_starlark_resource_outputs_rollout(label): + path_prefix = "_migrated/" + else: + path_prefix = "" + outputs.update( + dict( + resources_src_jar = path_prefix + "%{name}.srcjar", + resources_txt = path_prefix + "%{name}_symbols/R.txt", + resources_jar = path_prefix + "%{name}_resources.jar", + ), + ) + + return outputs + +def make_rule( + attrs = _ATTRS, + implementation = _impl, + outputs = _outputs, + additional_toolchains = []): + """Makes the rule. + + Args: + attrs: A dict. The attributes for the rule. + implementation: A function. The rule's implementation method. + + Returns: + A rule. + """ + return rule( + attrs = attrs, + fragments = [ + "android", + "java", + ], + implementation = implementation, + provides = [ + AndroidCcLinkParamsInfo, + AndroidIdeInfo, + AndroidIdlInfo, + AndroidLibraryResourceClassJarProvider, + AndroidNativeLibsInfo, + JavaInfo, + ], + outputs = outputs, + toolchains = [ + "@rules_android//toolchains/android:toolchain_type", + "@rules_android//toolchains/android_sdk:toolchain_type", + ] + additional_toolchains, + _skylark_testable = True, + ) + +android_library = make_rule() + +def _is_defined(name, attrs): + return name in attrs and attrs[name] != None + +def attrs_metadata(attrs): + """Adds additional metadata for specific android_library attrs. + + Bazel native rules have additional capabilities when inspecting attrs that + are not available in Starlark. For example, native rules are able to + determine if an attribute was set by a user and make decisions based on this + knowledge - sometimes the behavior may differ if the user specifies the + default value of the attribute. As such the Starlark android_library uses + this shim to provide similar capabilities. + + Args: + attrs: The attributes passed to the android_library rule. + + Returns: + A dictionary containing attr values with additional metadata. + """ + + # Required for the outputs. + attrs["$defined_local_resources"] = bool( + attrs.get("assets") or + attrs.get("assets_dir") or + attrs.get("assets_dir") == "" or + attrs.get("export_manifest") or + attrs.get("manifest") or + attrs.get("resource_files"), + ) + + # TODO(b/116691720): Remove normalization when bug is fixed. + if _is_defined("exports_manifest", attrs): + attrs["exports_manifest"] = _attrs.tristate.normalize( + attrs.get("exports_manifest"), + ) + + # TODO(b/127517031): Remove these entries once fixed. + attrs["$defined_assets"] = _is_defined("assets", attrs) + attrs["$defined_assets_dir"] = _is_defined("assets_dir", attrs) + attrs["$defined_idl_import_root"] = _is_defined("idl_import_root", attrs) + attrs["$defined_idl_parcelables"] = _is_defined("idl_parcelables", attrs) + attrs["$defined_idl_srcs"] = _is_defined("idl_srcs", attrs) + + # Required for ACLs check in _outputs(), since the callback can't access + # the native module. + attrs["$package_name"] = native.package_name() + + return attrs + +def android_library_macro(**attrs): + """Bazel android_library rule. + + https://docs.bazel.build/versions/master/be/android.html#android_library + + Args: + **attrs: Rule attributes + """ + android_library(**attrs_metadata(attrs)) diff --git a/rules/android_local_test.bzl b/rules/android_local_test.bzl new file mode 100644 index 0000000..9b1fe39 --- /dev/null +++ b/rules/android_local_test.bzl @@ -0,0 +1,27 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel rule for Android local test.""" + +load(":migration_tag_DONOTUSE.bzl", _add_migration_tag = "add_migration_tag") + +def android_local_test(**attrs): + """Bazel android_local_test rule. + + https://docs.bazel.build/versions/master/be/android.html#android_local_test + + Args: + **attrs: Rule attributes + """ + native.android_local_test(**_add_migration_tag(attrs)) diff --git a/rules/android_ndk_repository.bzl b/rules/android_ndk_repository.bzl new file mode 100644 index 0000000..34aa2e3 --- /dev/null +++ b/rules/android_ndk_repository.bzl @@ -0,0 +1,25 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel rule for Android ndk repository.""" + +def android_ndk_repository(**attrs): + """Bazel android_ndk_repository rule. + + https://docs.bazel.build/versions/master/be/android.html#android_ndk_repository + + Args: + **attrs: Rule attributes + """ + native.android_ndk_repository(**attrs) diff --git a/rules/android_sdk.bzl b/rules/android_sdk.bzl new file mode 100644 index 0000000..e5e1fa7 --- /dev/null +++ b/rules/android_sdk.bzl @@ -0,0 +1,54 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel rule for Android sdk.""" + +load(":attrs.bzl", "ANDROID_SDK_ATTRS") + +def _impl(ctx): + proguard = ctx.attr._proguard if ctx.attr._proguard else ctx.attr.proguard + android_sdk_info = AndroidSdkInfo( + ctx.attr.build_tools_version, + ctx.file.framework_aidl, + ctx.attr.aidl_lib, + ctx.file.android_jar, + ctx.file.source_properties, + ctx.file.shrinked_android_jar, + ctx.file.main_dex_classes, + ctx.attr.adb.files_to_run, + ctx.attr.dx.files_to_run, + ctx.attr.main_dex_list_creator.files_to_run, + ctx.attr.aidl.files_to_run, + ctx.attr.aapt.files_to_run, + ctx.attr.aapt2.files_to_run, + ctx.attr.apkbuilder.files_to_run if ctx.attr.apkbuilder else None, + ctx.attr.apksigner.files_to_run, + proguard.files_to_run, + ctx.attr.zipalign.files_to_run, + # Passing the 'system' here is only necessary to support native android_binary. + # TODO(b/149114743): remove this after the migration to android_application. + ctx.attr._system[java_common.BootClassPathInfo] if ctx.attr._system and java_common.BootClassPathInfo in ctx.attr._system else None, + ctx.attr.legacy_main_dex_list_generator.files_to_run if ctx.attr.legacy_main_dex_list_generator else None, + ) + return [ + android_sdk_info, + platform_common.ToolchainInfo(android_sdk_info = android_sdk_info), + ] + +android_sdk = rule( + attrs = ANDROID_SDK_ATTRS, + implementation = _impl, + fragments = ["java"], + provides = [AndroidSdkInfo], +) diff --git a/rules/android_sdk_repository.bzl b/rules/android_sdk_repository.bzl new file mode 100644 index 0000000..f82f6e6 --- /dev/null +++ b/rules/android_sdk_repository.bzl @@ -0,0 +1,25 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel rule for Android sdk repository.""" + +def android_sdk_repository(**attrs): + """Bazel android_sdk_repository rule. + + https://docs.bazel.build/versions/master/be/android.html#android_sdk_repository + + Args: + **attrs: Rule attributes + """ + native.android_sdk_repository(**attrs) diff --git a/rules/android_tools_defaults_jar.bzl b/rules/android_tools_defaults_jar.bzl new file mode 100644 index 0000000..b226882 --- /dev/null +++ b/rules/android_tools_defaults_jar.bzl @@ -0,0 +1,32 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel rule for Android tools defaults jar.""" + +load(":attrs.bzl", "ANDROID_TOOLS_DEFAULTS_JAR_ATTRS") +load(":utils.bzl", "get_android_sdk") + +def _impl(ctx): + return [ + DefaultInfo( + files = depset([get_android_sdk(ctx).android_jar]), + ), + ] + +android_tools_defaults_jar = rule( + attrs = ANDROID_TOOLS_DEFAULTS_JAR_ATTRS, + implementation = _impl, + fragments = ["android"], + toolchains = ["@rules_android//toolchains/android_sdk:toolchain_type"], +) diff --git a/rules/attrs.bzl b/rules/attrs.bzl new file mode 100644 index 0000000..af389ff --- /dev/null +++ b/rules/attrs.bzl @@ -0,0 +1,290 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Common attributes for Android rules.""" + +load(":utils.bzl", "log") + +def _add(attrs, *others): + new = {} + new.update(attrs) + for o in others: + for name in o.keys(): + if name in new: + log.error("Attr '%s' is defined twice." % name) + new[name] = o[name] + return new + +def _replace(attrs, **kwargs): + # Verify that new values are replacing existing ones, not adding. + for name in kwargs.keys(): + if name not in attrs: + log.error("Attr '%s' is not defined, replacement failed." % name) + new = dict() + new.update(attrs) + new.update(kwargs) + return new + +def _make_tristate_attr(default, doc = "", mandatory = False): + return attr.int( + default = default, + doc = doc, + mandatory = mandatory, + values = [-1, 0, 1], + ) + +def _normalize_tristate(attr_value): + """Normalizes the tristate value going into a rule. + + This is required because "tristate" is not officially supported as an + attribute type. An equivalent attribute type is an in with a constrained + set of values, namely [-1, 0, 1]. Unfortunately, tristate accepts + multiple types, integers, booleans and strings ("auto"). As a result, this + method normalizes the inputs to an integer. + + This method needs to be applied to attributes that were formally tristate + to normalize the inputs. + """ + if type(attr_value) == "int": + return attr_value + + if type(attr_value) == "string": + if attr_value.lower() == "auto": + return -1 + + if type(attr_value) == "bool": + return int(attr_value) + + return attr_value # Return unknown type, let the rule fail. + +_tristate = struct( + create = _make_tristate_attr, + normalize = _normalize_tristate, + yes = 1, + no = 0, + auto = -1, +) + +_JAVA_RUNTIME = dict( + _host_javabase = attr.label( + cfg = "host", + default = Label("@rules_android//tools/jdk:current_java_runtime"), + ), +) + + +# Android SDK attribute. +_ANDROID_SDK = dict( + _android_sdk = attr.label( + allow_rules = ["android_sdk"], + default = configuration_field( + fragment = "android", + name = "android_sdk_label", + ), + providers = [AndroidSdkInfo], + ), +) + +# Compilation attributes for Android rules. +_COMPILATION = _add( + dict( + assets = attr.label_list( + allow_files = True, + cfg = "target", + ), + assets_dir = attr.string(), + custom_package = attr.string(), + manifest = attr.label( + allow_single_file = [".xml"], + ), + resource_files = attr.label_list( + allow_files = True, + ), + data = attr.label_list( + allow_files = True, + ), + plugins = attr.label_list( + providers = [JavaPluginInfo], + cfg = "host", + ), + javacopts = attr.string_list(), + # TODO: Expose getPlugins() in JavaConfiguration.java + # com/google/devtools/build/lib/rules/java/JavaConfiguration.java + # com/google/devtools/build/lib/rules/java/JavaOptions.java + # + # _java_plugins = attr.label( + # allow_rules = ["java_plugin"], + # default = configuration_field( + # fragment = "java", + # name = "plugin", + # ), + # ), + ), + _JAVA_RUNTIME, +) + +# Attributes for rules that use the AndroidDataContext android_data.make_context +_DATA_CONTEXT = _add( + dict( + # Additional attrs needed for AndroidDataContext + _add_g3itr_xslt = attr.label( + cfg = "host", + default = Label("//tools/android/xslt:add_g3itr.xslt"), + allow_single_file = True, + ), + _android_manifest_merge_tool = attr.label( + cfg = "host", + default = Label("//tools/android:merge_manifests"), + executable = True, + ), + # TODO(b/145617058) Switching back to head RPBB until the Android rules release process is improved + _android_resources_busybox = attr.label( + cfg = "host", + default = Label("@rules_android//rules:ResourceProcessorBusyBox"), + executable = True, + ), + _xsltproc_tool = attr.label( + cfg = "host", + default = Label("//tools/android/xslt:xslt"), + allow_files = True, + ), + ), + _ANDROID_SDK, +) + + + + + + + +ANDROID_SDK_ATTRS = dict( + aapt = attr.label( + allow_single_file = True, + cfg = "host", + executable = True, + mandatory = True, + ), + aapt2 = attr.label( + allow_single_file = True, + cfg = "host", + executable = True, + ), + aidl = attr.label( + allow_files = True, + cfg = "host", + executable = True, + mandatory = True, + ), + aidl_lib = attr.label( + allow_files = [".jar"], + ), + android_jar = attr.label( + allow_single_file = [".jar"], + cfg = "host", + mandatory = True, + ), + annotations_jar = attr.label( + allow_single_file = [".jar"], + cfg = "host", + ), + apkbuilder = attr.label( + allow_files = True, + cfg = "host", + executable = True, + ), + apksigner = attr.label( + allow_files = True, + cfg = "host", + executable = True, + mandatory = True, + ), + adb = attr.label( + allow_single_file = True, + cfg = "host", + executable = True, + mandatory = True, + ), + build_tools_version = attr.string(), + dx = attr.label( + allow_files = True, + cfg = "host", + executable = True, + mandatory = True, + ), + framework_aidl = attr.label( + allow_single_file = True, + cfg = "host", + mandatory = True, + ), + legacy_main_dex_list_generator = attr.label( + allow_files = True, + cfg = "host", + executable = True, + ), + main_dex_classes = attr.label( + allow_single_file = True, + cfg = "host", + mandatory = True, + ), + main_dex_list_creator = attr.label( + allow_files = True, + cfg = "host", + executable = True, + mandatory = True, + ), + proguard = attr.label( + allow_files = True, + cfg = "host", + executable = True, + mandatory = True, + ), + shrinked_android_jar = attr.label( + allow_single_file = True, + cfg = "host", + ), + source_properties = attr.label( + allow_single_file = True, + cfg = "host", + ), + zipalign = attr.label( + allow_single_file = True, + cfg = "host", + executable = True, + mandatory = True, + ), + _proguard = attr.label( + cfg = "host", + default = configuration_field( + fragment = "java", + name = "proguard_top", + ), + ), + _system = attr.label( + default = Label("//tools/android:bootclasspath_android_only"), + ), +) + +ANDROID_TOOLS_DEFAULTS_JAR_ATTRS = _add(_ANDROID_SDK) + + +attrs = struct( + ANDROID_SDK = _ANDROID_SDK, + COMPILATION = _COMPILATION, + DATA_CONTEXT = _DATA_CONTEXT, + JAVA_RUNTIME = _JAVA_RUNTIME, + tristate = _tristate, + add = _add, + replace = _replace, +) diff --git a/rules/bundletool.bzl b/rules/bundletool.bzl new file mode 100644 index 0000000..161882a --- /dev/null +++ b/rules/bundletool.bzl @@ -0,0 +1,263 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel Bundletool Commands.""" + +load(":java.bzl", _java = "java") + +_density_mapping = { + "ldpi": 120, + "mdpi": 160, + "hdpi": 240, + "xhdpi": 320, + "xxhdpi": 480, + "xxxhdpi": 640, + "tvdpi": 213, +} + +def _proto_apk_to_module( + ctx, + out = None, + proto_apk = None, + zip = None, + unzip = None): + # TODO(timpeut): rewrite this as a standalone golang tool + ctx.actions.run_shell( + command = """ +set -e + +IN_DIR=$(mktemp -d) +OUT_DIR=$(mktemp -d) +CUR_PWD=$(pwd) +UNZIP=%s +ZIP=%s +INPUT=%s +OUTPUT=%s + +"${UNZIP}" -qq "${INPUT}" -d "${IN_DIR}" +cd "${IN_DIR}" + +if [ -f resources.pb ]; then + mv resources.pb "${OUT_DIR}/" +fi + +if [ -f AndroidManifest.xml ]; then + mkdir "${OUT_DIR}/manifest" + mv AndroidManifest.xml "${OUT_DIR}/manifest/" +fi + +NUM_DEX=`ls -1 *.dex 2>/dev/null | wc -l` +if [ $NUM_DEX != 0 ]; then + mkdir "${OUT_DIR}/dex" + mv *.dex "${OUT_DIR}/dex/" +fi + +if [ -d res ]; then + mv res "${OUT_DIR}/res" +fi + +if [ -d assets ]; then + mv assets "${OUT_DIR}/" +fi + +if [ -d lib ]; then + mv lib "${OUT_DIR}/" +fi + +UNKNOWN=`ls -1 * 2>/dev/null | wc -l` +if [ $UNKNOWN != 0 ]; then + mkdir "${OUT_DIR}/root" + mv * "${OUT_DIR}/root/" +fi + +cd "${OUT_DIR}" +"${CUR_PWD}/${ZIP}" "${CUR_PWD}/${OUTPUT}" -Drq0 . +""" % ( + unzip.executable.path, + zip.executable.path, + proto_apk.path, + out.path, + ), + tools = [zip, unzip], + arguments = [], + inputs = [proto_apk], + outputs = [out], + mnemonic = "Rebundle", + progress_message = "Rebundle to %s" % out.short_path, + ) + +def _build( + ctx, + out = None, + modules = [], + config = None, + metadata = dict(), + bundletool = None, + host_javabase = None): + args = ctx.actions.args() + args.add("build-bundle") + args.add("--output", out) + if modules: + args.add_joined("--modules", modules, join_with = ",") + if config: + args.add("--config", config) + for path, f in metadata.items(): + args.add("--metadata-file", "%s:%s" % (path, f.path)) + + _java.run( + ctx = ctx, + host_javabase = host_javabase, + executable = bundletool, + arguments = [args], + inputs = ( + modules + + ([config] if config else []) + + metadata.values() + ), + outputs = [out], + mnemonic = "BuildBundle", + progress_message = "Building bundle %s" % out.short_path, + ) + +def _extract_config( + ctx, + out = None, + aab = None, + bundletool = None, + host_javabase = None): + # Need to execute as a shell script as the tool outputs to stdout + cmd = """ +set -e +contents=`%s -jar %s dump config --bundle %s` +echo "$contents" > %s +""" % ( + host_javabase[java_common.JavaRuntimeInfo].java_executable_exec_path, + bundletool.executable.path, + aab.path, + out.path, + ) + + ctx.actions.run_shell( + inputs = [aab], + outputs = [out], + tools = depset([bundletool.executable], transitive = [host_javabase[java_common.JavaRuntimeInfo].files]), + mnemonic = "ExtractBundleConfig", + progress_message = "Extract bundle config to %s" % out.short_path, + command = cmd, + ) + +def _extract_manifest( + ctx, + out = None, + aab = None, + module = None, + xpath = None, + bundletool = None, + host_javabase = None): + # Need to execute as a shell script as the tool outputs to stdout + extra_flags = [] + if module: + extra_flags.append("--module " + module) + if xpath: + extra_flags.append("--xpath " + xpath) + cmd = """ +set -e +contents=`%s -jar %s dump manifest --bundle %s %s` +echo "$contents" > %s +""" % ( + host_javabase[java_common.JavaRuntimeInfo].java_executable_exec_path, + bundletool.executable.path, + aab.path, + " ".join(extra_flags), + out.path, + ) + + ctx.actions.run_shell( + inputs = [aab], + outputs = [out], + tools = depset([bundletool.executable], transitive = [host_javabase[java_common.JavaRuntimeInfo].files]), + mnemonic = "ExtractBundleManifest", + progress_message = "Extract bundle manifest to %s" % out.short_path, + command = cmd, + ) + +def _bundle_to_apks( + ctx, + out = None, + bundle = None, + universal = False, + device_spec = None, + keystore = None, + modules = None, + aapt2 = None, + bundletool = None, + host_javabase = None): + inputs = [bundle] + args = ctx.actions.args() + args.add("build-apks") + args.add("--output", out) + args.add("--bundle", bundle) + args.add("--aapt2", aapt2.executable.path) + + if universal: + args.add("--mode=universal") + + if keystore: + args.add("--ks", keystore.path) + args.add("--ks-pass", "pass:android") + args.add("--ks-key-alias", "AndroidDebugKey") + inputs.append(keystore) + + if device_spec: + args.add("--device-spec", device_spec) + inputs.append(device_spec) + + if modules: + args.add_joined("--modules", modules, join_with = ",") + + _java.run( + ctx = ctx, + host_javabase = host_javabase, + executable = bundletool, + arguments = [args], + inputs = inputs, + outputs = [out], + tools = [aapt2], + mnemonic = "BundleToApks", + progress_message = "Converting bundle to .apks: %s" % out.short_path, + ) + +def _build_device_json( + ctx, + out, + abis, + locales, + density, + sdk_version): + json_content = json.encode(struct( + supportedAbis = abis, + supportedLocales = locales, + screenDensity = _density_mapping[density], + sdkVersion = int(sdk_version), + )) + ctx.actions.write(out, json_content) + +bundletool = struct( + build = _build, + build_device_json = _build_device_json, + bundle_to_apks = _bundle_to_apks, + extract_config = _extract_config, + extract_manifest = _extract_manifest, + proto_apk_to_module = _proto_apk_to_module, +) diff --git a/rules/busybox.bzl b/rules/busybox.bzl new file mode 100644 index 0000000..ff64d0c --- /dev/null +++ b/rules/busybox.bzl @@ -0,0 +1,1042 @@ +# Copyright 2019 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel ResourcesBusyBox Commands.""" + +load(":java.bzl", _java = "java") + +_ANDROID_RESOURCES_STRICT_DEPS = "android_resources_strict_deps" + +def _sanitize_assets_dir(assets_dir): + sanitized_assets_dir = "/".join( + [ + part + for part in assets_dir.split("/") + if part != "" and part != "." + ], + ) + + return "/" + sanitized_assets_dir if assets_dir.startswith("/") else sanitized_assets_dir + +def _get_unique_assets_dirs(assets, assets_dir): + """Find the unique assets directories, partitioned by assets_dir. + + Args: + assets: A list of Files. List of asset files to process. + assets_dir: String. String giving the path to the files in assets. + + Returns: + A list of short_paths representing unique asset dirs. + """ + if not assets: + return [] + + dirs = dict() + + assets_dir = _sanitize_assets_dir(assets_dir) + if assets_dir: + partition_by = "/%s/" % assets_dir.strip("/") + for f in assets: + if f.is_directory and f.path.endswith(partition_by[:-1]): + # If f is a directory, check if its path ends with the assets_dir. + dirs[f.path] = True + elif f.is_directory and "_aar/unzipped" in f.path: + # Assets from an aar_import rule are extracted in a + # "assets" subdirectory of the given path + dirs["%s/assets" % f.path] = True + else: + # Partition to remove subdirectories beneath assets_dir + # Also removes the trailing / + dirs["".join(f.path.rpartition(partition_by)[:2])[:-1]] = True + else: + # Use the dirname of the generating target if no assets_dir. + for f in assets: + if f.is_source: + dirs[f.owner.package] = True + else: + # Prepend the root path for generated files. + dirs[f.root.path + "/" + f.owner.package] = True + return dirs.keys() + +def _get_unique_res_dirs(resource_files): + """Find the unique res dirs. + + Args: + resource_files: A list of Files. A list of resource_files. + + Returns: + A list of short_paths representing unique res dirs from the given resource files. + """ + dirs = dict() + for f in resource_files: + if f.is_directory: + dirs[f.path] = True + else: + dirs[f.dirname.rpartition("/" + f.dirname.split("/")[-1])[0]] = True + return dirs.keys() + +def _make_serialized_resources_flag( + assets = [], + assets_dir = None, + resource_files = [], + label = "", + symbols = None): + return ";".join( + [ + "#".join(_get_unique_res_dirs(resource_files)), + "#".join(_get_unique_assets_dirs(assets, assets_dir)), + label, + symbols.path if symbols else "", + ], + ).rstrip(":") + +def _make_resources_flag( + assets = [], + assets_dir = None, + resource_files = [], + manifest = None, + r_txt = None, + symbols = None): + return ":".join( + [ + "#".join(_get_unique_res_dirs(resource_files)), + "#".join(_get_unique_assets_dirs(assets, assets_dir)), + manifest.path if manifest else "", + r_txt.path if r_txt else "", + symbols.path if symbols else "", + ], + ) + +def _path(f): + return f.path + +def _make_package_resources_flags(resources_node): + if not (resources_node.manifest and resources_node.r_txt and resources_node.compiled_resources): + return None + flag = _make_resources_flag( + resource_files = resources_node.resource_files.to_list(), + assets = resources_node.assets.to_list(), + assets_dir = resources_node.assets_dir, + manifest = resources_node.manifest, + r_txt = resources_node.r_txt, + symbols = resources_node.compiled_resources, + ) + return flag + +def _make_package_assets_flags(resources_node): + assets = resources_node.assets.to_list() + if not assets: + return None + return _make_serialized_resources_flag( + assets = assets, + assets_dir = resources_node.assets_dir, + label = str(resources_node.label), + symbols = resources_node.compiled_assets, + ) + +def _extract_filters( + raw_list): + """Extract densities and resource_configuration filters from raw string lists. + + In BUILD files, string lists can be represented as a list of strings, a single comma-separated + string, or a combination of both. This method outputs a single list of individual string values, + which can then be passed directly to resource processing actions. Empty strings are removed and + the final list is sorted. + + Args: + raw_list: List of strings. The raw densities or resource configuration filters. + + Returns: + List of strings extracted from the raw list. + """ + out_filters = [] + for item in raw_list: + if "," in item: + item_list = item.split(",") + for entry in item_list: + stripped_entry = entry.strip() + if stripped_entry: + out_filters.append(stripped_entry) + elif item: + out_filters.append(item) + return sorted(out_filters) + +def _package( + ctx, + out_r_src_jar = None, + out_r_txt = None, + out_symbols = None, + out_manifest = None, + out_proguard_cfg = None, + out_main_dex_proguard_cfg = None, + out_resource_files_zip = None, + out_file = None, + package_type = None, + java_package = None, + manifest = None, + assets = [], + assets_dir = None, + resource_files = [], + resource_configs = None, + densities = [], + application_id = None, + package_id = None, + direct_resources_nodes = [], + transitive_resources_nodes = [], + transitive_manifests = [], + transitive_assets = [], + transitive_compiled_assets = [], + transitive_resource_files = [], + transitive_compiled_resources = [], + transitive_r_txts = [], + additional_apks_to_link_against = [], + nocompress_extensions = [], + proto_format = False, + version_name = None, + version_code = None, + android_jar = None, + aapt = None, + busybox = None, + host_javabase = None, + should_throw_on_conflict = True, # TODO: read this from allowlist at caller + debug = True): # TODO: we will set this to false in prod builds + """Packages the compiled Android Resources with AAPT. + + Args: + ctx: The context. + out_r_src_jar: A File. The R.java outputted by linking resources in a srcjar. + out_r_txt: A File. The resource IDs outputted by linking resources in text. + out_symbols: A File. The output zip containing compiled resources. + out_manifest: A File. The output processed manifest. + out_proguard_cfg: A File. The proguard config to be generated. + out_main_dex_proguard_cfg: A File. The main dex proguard config to be generated. + out_resource_files_zip: A File. The resource files zipped by linking resources. + out_file: A File. The Resource APK outputted by linking resources. + package_type: A string. The configuration type to use when packaging. + java_package: A string. The Java package for the generated R.java. + manifest: A File. The AndroidManifest.xml. + assets: sequence of Files. A list of Android assets files to be processed. + assets_dir: String. The name of the assets directory. + resource_files: A list of Files. The resource files. + resource_configs: A list of strings. The list of resource configuration + filters. + densities: A list of strings. The list of screen densities to filter for when + building the apk. + application_id: An optional string. The applicationId set in manifest values. + package_id: An optional integer in [2,255]. This is the prefix byte for + all generated resource IDs, defaults to 0x7F (127). 1 is reserved by the + framework, and some builds are known to crash when given IDs > 127. + Shared libraries are also assigned monotonically increasing IDs in + [2,126], so care should be taken that there is room at the lower end. + direct_resources_nodes: Depset of ResourcesNodeInfo providers. The set of + ResourcesNodeInfo from direct dependencies. + transitive_resources_nodes: Depset of ResourcesNodeInfo providers. The set + of ResourcesNodeInfo from transitive dependencies (not including directs). + transitive_manifests: List of Depsets. Depsets contain all transitive manifests. + transitive_assets: List of Depsets. Depsets contain all transitive assets. + transitive_compiled_assets: List of Depsets. Depsets contain all transitive + compiled_assets. + transitive_resource_files: List of Depsets. Depsets contain all transitive + resource files. + transitive_compiled_resources: List of Depsets. Depsets contain all transitive + compiled_resources. + transitive_r_txts: List of Depsets. Depsets contain all transitive R txt files. + additional_apks_to_link_against: A list of Files. Additional APKs to link + against. Optional. + nocompress_extensions: A list of strings. File extension to leave uncompressed + in the apk. + proto_format: Boolean, whether to generate the resource table in proto format. + version_name: A string. The version name to stamp the generated manifest with. Optional. + version_code: A string. The version code to stamp the generated manifest with. Optional. + android_jar: A File. The Android Jar. + aapt: A FilesToRunProvider. The AAPT executable. + busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. + host_javabase: Target. The host javabase. + should_throw_on_conflict: A boolean. Determines whether an error should be thrown + when a resource conflict occurs. + debug: A boolean. Determines whether to enable debugging. + """ + if not manifest: + fail("No manifest given, the manifest is mandatory.") + + direct_data_flag = [] + direct_compiled_resources = [] + + output_files = [] + input_files = [] + transitive_input_files = [] + + args = ctx.actions.args() + args.use_param_file("@%s") + args.add("--tool", "AAPT2_PACKAGE") + args.add("--") + args.add("--aapt2", aapt.executable) + args.add_joined( + "--data", + transitive_resources_nodes, + map_each = _make_package_resources_flags, + join_with = ",", + ) + args.add_joined( + "--directData", + direct_resources_nodes, + map_each = _make_package_resources_flags, + join_with = ",", + ) + args.add_joined( + "--directAssets", + direct_resources_nodes, + map_each = _make_package_assets_flags, + join_with = "&", + omit_if_empty = True, + ) + args.add_joined( + "--assets", + transitive_resources_nodes, + map_each = _make_package_assets_flags, + join_with = "&", + omit_if_empty = True, + ) + transitive_input_files.extend(transitive_resource_files) + transitive_input_files.extend(transitive_assets) + transitive_input_files.extend(transitive_compiled_assets) + transitive_input_files.extend(transitive_compiled_resources) + transitive_input_files.extend(transitive_manifests) + transitive_input_files.extend(transitive_r_txts) + args.add( + "--primaryData", + _make_resources_flag( + manifest = manifest, + assets = assets, + assets_dir = assets_dir, + resource_files = resource_files, + ), + ) + input_files.append(manifest) + input_files.extend(resource_files) + input_files.extend(assets) + args.add("--androidJar", android_jar) + input_files.append(android_jar) + args.add("--rOutput", out_r_txt) + output_files.append(out_r_txt) + if out_symbols: + args.add("--symbolsOut", out_symbols) + output_files.append(out_symbols) + args.add("--srcJarOutput", out_r_src_jar) + output_files.append(out_r_src_jar) + if out_proguard_cfg: + args.add("--proguardOutput", out_proguard_cfg) + output_files.append(out_proguard_cfg) + if out_main_dex_proguard_cfg: + args.add("--mainDexProguardOutput", out_main_dex_proguard_cfg) + output_files.append(out_main_dex_proguard_cfg) + args.add("--manifestOutput", out_manifest) + output_files.append(out_manifest) + if out_resource_files_zip: + args.add("--resourcesOutput", out_resource_files_zip) + output_files.append(out_resource_files_zip) + if out_file: + args.add("--packagePath", out_file) + output_files.append(out_file) + args.add("--useAaptCruncher=no") # Unnecessary, used for AAPT1 only but added here to minimize diffs. + if package_type: + args.add("--packageType", package_type) + if debug: + args.add("--debug") + if should_throw_on_conflict: + args.add("--throwOnResourceConflict") + if resource_configs: + args.add_joined("--resourceConfigs", _extract_filters(resource_configs), join_with = ",") + if densities: + args.add_joined("--densities", _extract_filters(densities), join_with = ",") + if application_id: + args.add("--applicationId", application_id) + if package_id: + args.add("--packageId", package_id) + if additional_apks_to_link_against: + args.add_joined( + "--additionalApksToLinkAgainst", + additional_apks_to_link_against, + join_with = ",", + map_each = _path, + ) + input_files.extend(additional_apks_to_link_against) + if nocompress_extensions: + args.add_joined("--uncompressedExtensions", nocompress_extensions, join_with = ",") + if proto_format: + args.add("--resourceTableAsProto") + if version_name: + args.add("--versionName", version_name) + if version_code: + args.add("--versionCode", version_code) + args.add("--packageForR", java_package) + + _java.run( + ctx = ctx, + host_javabase = host_javabase, + executable = busybox, + tools = [aapt], + arguments = [args], + inputs = depset(input_files, transitive = transitive_input_files), + outputs = output_files, + mnemonic = "PackageAndroidResources", + progress_message = "Packaging Android Resources in %s" % ctx.label, + ) + +def _parse( + ctx, + out_symbols = None, + assets = [], + assets_dir = None, + busybox = None, + host_javabase = None): + """Parses Android assets. + + Args: + ctx: The context. + out_symbols: A File. The output bin containing parsed assets. + assets: sequence of Files. A list of Android assets files to be processed. + assets_dir: String. The name of the assets directory. + busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. + host_javabase: Target. The host javabase. + """ + args = ctx.actions.args() + args.use_param_file("@%s") + args.add("--tool", "PARSE") + args.add("--") + args.add( + "--primaryData", + _make_resources_flag( + assets = assets, + assets_dir = assets_dir, + ), + ) + args.add("--output", out_symbols) + + _java.run( + ctx = ctx, + host_javabase = host_javabase, + executable = busybox, + arguments = [args], + inputs = assets, + outputs = [out_symbols], + mnemonic = "ParseAndroidResources", + progress_message = "Parsing Android Resources in %s" % out_symbols.short_path, + ) + +def _make_merge_assets_flags(resources_node): + assets = resources_node.assets.to_list() + if not (assets or resources_node.assets_dir): + return None + return _make_serialized_resources_flag( + assets = assets, + assets_dir = resources_node.assets_dir, + label = str(resources_node.label), + symbols = resources_node.assets_symbols, + ) + +def _merge_assets( + ctx, + out_assets_zip = None, + assets = [], + assets_dir = None, + symbols = None, + transitive_assets = [], + transitive_assets_symbols = [], + direct_resources_nodes = [], + transitive_resources_nodes = [], + busybox = None, + host_javabase = None): + """Merges Android assets. + + Args: + ctx: The context. + out_assets_zip: A File. + assets: sequence of Files. A list of Android assets files to be processed. + assets_dir: String. The name of the assets directory. + symbols: A File. The parsed assets. + transitive_assets: Sequence of Depsets. The list of transitive + assets from deps. + transitive_assets_symbols: Sequence of Depsets. The list of + transitive assets_symbols files from deps. + direct_resources_nodes: Sequence of ResourcesNodeInfo providers. The list + of ResourcesNodeInfo providers that are direct depencies. + transitive_resources_nodes: Sequence of ResourcesNodeInfo providers. The + list of ResourcesNodeInfo providers that are transitive depencies. + busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. + host_javabase: Target. The host javabase. + """ + args = ctx.actions.args() + args.use_param_file("@%s") + args.add("--tool", "MERGE_ASSETS") + args.add("--") + args.add("--assetsOutput", out_assets_zip) + args.add( + "--primaryData", + _make_serialized_resources_flag( + assets = assets, + assets_dir = assets_dir, + label = str(ctx.label), + symbols = symbols, + ), + ) + args.add_joined( + "--directData", + direct_resources_nodes, + map_each = _make_merge_assets_flags, + join_with = "&", + ) + args.add_joined( + "--data", + transitive_resources_nodes, + map_each = _make_merge_assets_flags, + join_with = "&", + ) + + _java.run( + ctx = ctx, + host_javabase = host_javabase, + executable = busybox, + arguments = [args], + inputs = depset( + assets + [symbols], + transitive = transitive_assets + transitive_assets_symbols, + ), + outputs = [out_assets_zip], + mnemonic = "MergeAndroidAssets", + progress_message = + "Merging Android Assets in %s" % out_assets_zip.short_path, + ) + +def _validate_and_link( + ctx, + out_r_src_jar = None, + out_r_txt = None, + out_file = None, + compiled_resources = None, + transitive_compiled_resources = depset(), + java_package = None, + manifest = None, + android_jar = None, + busybox = None, + host_javabase = None, + aapt = None): + """Links compiled Android Resources with AAPT. + + Args: + ctx: The context. + out_r_src_jar: A File. The R.java outputted by linking resources in a srcjar. + out_r_txt: A File. The resource IDs outputted by linking resources in text. + out_file: A File. The Resource APK outputted by linking resources. + compiled_resources: A File. The symbols.zip of compiled resources for + this target. + transitive_compiled_resources: Depset of Files. The symbols.zip of the + compiled resources from the transitive dependencies of this target. + java_package: A string. The Java package for the generated R.java. + manifest: A File. The AndroidManifest.xml. + android_jar: A File. The Android Jar. + busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. + host_javabase: Target. The host javabase. + aapt: A FilesToRunProvider. The AAPT executable. + """ + output_files = [] + input_files = [android_jar] + transitive_input_files = [] + + # Retrieves the list of files at runtime when a directory is passed. + args = ctx.actions.args() + args.use_param_file("@%s") + args.add("--tool", "LINK_STATIC_LIBRARY") + args.add("--") + args.add("--aapt2", aapt.executable) + args.add("--libraries", android_jar) + if compiled_resources: + args.add("--compiled", compiled_resources) + input_files.append(compiled_resources) + args.add_joined( + "--compiledDep", + transitive_compiled_resources, + join_with = ":", + ) + transitive_input_files.append(transitive_compiled_resources) + args.add("--manifest", manifest) + input_files.append(manifest) + if java_package: + args.add("--packageForR", java_package) + args.add("--sourceJarOut", out_r_src_jar) + output_files.append(out_r_src_jar) + args.add("--rTxtOut", out_r_txt) + output_files.append(out_r_txt) + args.add("--staticLibraryOut", out_file) + output_files.append(out_file) + + _java.run( + ctx = ctx, + host_javabase = host_javabase, + executable = busybox, + tools = [aapt], + arguments = [args], + inputs = depset(input_files, transitive = transitive_input_files), + outputs = output_files, + mnemonic = "LinkAndroidResources", + progress_message = + "Linking Android Resources in " + out_file.short_path, + ) + +def _compile( + ctx, + out_file = None, + assets = [], + assets_dir = None, + resource_files = [], + busybox = None, + aapt = None, + host_javabase = None): + """Compile and store resources in a single archive. + + Args: + ctx: The context. + out_file: File. The output zip containing compiled resources. + resource_files: A list of Files. The list of resource files or directories + assets: A list of Files. The list of assets files or directories + to process. + assets_dir: String. The name of the assets directory. + busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. + aapt: AAPT. Tool for compiling resources. + host_javabase: Target. The host javabase. + """ + if not out_file: + fail("No output directory specified.") + + # Retrieves the list of files at runtime when a directory is passed. + args = ctx.actions.args() + args.use_param_file("@%s") + args.add("--tool", "COMPILE_LIBRARY_RESOURCES") + args.add("--") + args.add("--aapt2", aapt.executable) + args.add( + "--resources", + _make_resources_flag( + resource_files = resource_files, + assets = assets, + assets_dir = assets_dir, + ), + ) + args.add("--output", out_file) + + _java.run( + ctx = ctx, + host_javabase = host_javabase, + executable = busybox, + tools = [aapt], + arguments = [args], + inputs = resource_files + assets, + outputs = [out_file], + mnemonic = "CompileAndroidResources", + progress_message = "Compiling Android Resources in %s" % out_file.short_path, + ) + +def _make_merge_compiled_flags(resources_node_info): + if not resources_node_info.compiled_resources: + return None + return _make_serialized_resources_flag( + label = str(resources_node_info.label), + symbols = resources_node_info.compiled_resources, + ) + +def _merge_compiled( + ctx, + out_class_jar = None, + out_manifest = None, + out_aapt2_r_txt = None, + java_package = None, + manifest = None, + compiled_resources = None, + direct_resources_nodes = [], + transitive_resources_nodes = [], + direct_compiled_resources = depset(), + transitive_compiled_resources = depset(), + android_jar = None, + busybox = None, + host_javabase = None): + """Merges the compile resources. + + Args: + ctx: The context. + out_class_jar: A File. The compiled R.java outputted by linking resources. + out_manifest: A File. The list of resource files or directories + out_aapt2_r_txt: A File. The resource IDs outputted by linking resources in text. + java_package: A string. The Java package for the generated R.java. + manifest: A File. The AndroidManifest.xml. + compiled_resources: A File. The symbols.zip of compiled resources for this target. + direct_resources_nodes: Sequence of ResourcesNodeInfo providers. The list + of ResourcesNodeInfo providers that are direct depencies. + transitive_resources_nodes: Sequence of ResourcesNodeInfo providers. The + list of ResourcesNodeInfo providers that are transitive depencies. + direct_compiled_resources: Depset of Files. A depset of symbols.zip of + compiled resources from direct dependencies. + transitive_compiled_resources: Depset of Files. A depset of symbols.zip of + compiled resources from transitive dependencies. + android_jar: A File. The Android Jar. + busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. + host_javabase: Target. The host javabase. + """ + output_files = [] + input_files = [android_jar] + transitive_input_files = [] + + args = ctx.actions.args() + args.use_param_file("@%s") + args.add("--tool", "MERGE_COMPILED") + args.add("--") + args.add("--classJarOutput", out_class_jar) + output_files.append(out_class_jar) + args.add("--targetLabel", ctx.label) + args.add("--manifestOutput", out_manifest) + output_files.append(out_manifest) + args.add("--rTxtOut", out_aapt2_r_txt) + output_files.append(out_aapt2_r_txt) + args.add("--androidJar", android_jar) + args.add("--primaryManifest", manifest) + input_files.append(manifest) + if java_package: + args.add("--packageForR", java_package) + args.add( + "--primaryData", + _make_serialized_resources_flag( + label = str(ctx.label), + symbols = compiled_resources, + ), + ) + input_files.append(compiled_resources) + args.add_joined( + "--directData", + direct_resources_nodes, + map_each = _make_merge_compiled_flags, + join_with = "&", + ) + transitive_input_files.append(direct_compiled_resources) + if _ANDROID_RESOURCES_STRICT_DEPS in ctx.disabled_features: + args.add_joined( + "--data", + transitive_resources_nodes, + map_each = _make_merge_compiled_flags, + join_with = "&", + ) + transitive_input_files.append(transitive_compiled_resources) + + _java.run( + ctx = ctx, + host_javabase = host_javabase, + executable = busybox, + arguments = [args], + inputs = depset(input_files, transitive = transitive_input_files), + outputs = output_files, + mnemonic = "StarlarkMergeCompiledAndroidResources", + progress_message = + "Merging compiled Android Resources in " + out_class_jar.short_path, + ) + +def _escape_mv(s): + """Escapes `:` and `,` in manifest values so they can be used as a busybox flag.""" + return s.replace(":", "\\:").replace(",", "\\,") + +def _owner_label(file): + return "//" + file.owner.package + ":" + file.owner.name + +# We need to remove the "/_migrated/" path segment from file paths in order for sorting to +# match the order of the native manifest merging action. +def _manifest_short_path(manifest): + return manifest.short_path.replace("/_migrated/", "/") + +def _mergee_manifests_flag(manifests): + ordered_manifests = sorted(manifests.to_list(), key = _manifest_short_path) + entries = [] + for manifest in ordered_manifests: + label = _owner_label(manifest).replace(":", "\\:") + entries.append((manifest.path + ":" + label).replace(",", "\\,")) + flag_entry = ",".join(entries) + if not flag_entry: + return None + return flag_entry + +def _merge_manifests( + ctx, + out_file = None, + out_log_file = None, + merge_type = "APPLICATION", + manifest = None, + mergee_manifests = depset(), + manifest_values = None, + java_package = None, + busybox = None, + host_javabase = None): + """Merge multiple AndroidManifest.xml files into a single one. + + Args: + ctx: The context. + out_file: A File. The output merged manifest. + out_log_file: A File. The output log from the merge tool. + merge_type: A string, either APPLICATION or LIBRARY. Type of merging. + manifest: A File. The primary AndroidManifest.xml. + mergee_manifests: A depset of Files. All transitive manifests to be merged. + manifest_values: A dictionary. Manifest values to substitute. + java_package: A string. Custom java package to insert in manifest package attribute. + busybox: A FilesToRunProvider. The ResourceProcessorBusyBox executable. + host_javabase: Target. The host javabase. + """ + if merge_type not in ["APPLICATION", "LIBRARY"]: + fail("Unexpected manifest merge type: " + merge_type) + + outputs = [out_file] + directs = [manifest] + transitives = [mergee_manifests] + + # Args for busybox + args = ctx.actions.args() + args.use_param_file("@%s", use_always = True) + args.add("--tool", "MERGE_MANIFEST") + args.add("--") + args.add("--manifest", manifest) + args.add_all( + "--mergeeManifests", + [mergee_manifests], + map_each = _mergee_manifests_flag, + ) + if manifest_values: + args.add( + "--manifestValues", + ",".join(["%s:%s" % (_escape_mv(k), _escape_mv(v)) for k, v in manifest_values.items()]), + ) + args.add("--mergeType", merge_type) + args.add("--customPackage", java_package) + args.add("--manifestOutput", out_file) + if out_log_file: + args.add("--log", out_log_file) + outputs.append(out_log_file) + + _java.run( + ctx = ctx, + host_javabase = host_javabase, + executable = busybox, + arguments = [args], + inputs = depset(directs, transitive = transitives), + outputs = outputs, + mnemonic = "MergeManifests", + progress_message = "Merging Android Manifests in %s" % out_file.short_path, + ) + +def _process_databinding( + ctx, + out_databinding_info = None, + out_databinding_processed_resources = None, + databinding_resources_dirname = None, + resource_files = None, + java_package = None, + busybox = None, + host_javabase = None): + """Processes databinding for android_binary. + + Processes databinding declarations over resources, populates the databinding layout + info file, and generates new resources with databinding expressions stripped out. + + Args: + ctx: The context. + out_databinding_info: File. The output databinding layout info zip file. + out_databinding_processed_resources: List of Files. The generated databinding + processed resource files. + databinding_resources_dirname: String. The execution path to the directory where + the out_databinding_processed_resources are generated. + resource_files: List of Files. The resource files to be processed. + java_package: String. Java package for which java sources will be + generated. By default the package is inferred from the directory where + the BUILD file containing the rule is. + busybox: FilesToRunProvider. The ResourceBusyBox executable or + FilesToRunprovider + host_javabase: A Target. The host javabase. + """ + res_dirs = _get_unique_res_dirs(resource_files) + + args = ctx.actions.args() + args.add("--tool", "PROCESS_DATABINDING") + args.add("--") + args.add("--output_resource_directory", databinding_resources_dirname) + args.add_all(res_dirs, before_each = "--resource_root") + args.add("--dataBindingInfoOut", out_databinding_info) + args.add("--appId", java_package) + + _java.run( + ctx = ctx, + host_javabase = host_javabase, + executable = busybox, + arguments = [args], + inputs = resource_files, + outputs = [out_databinding_info] + out_databinding_processed_resources, + mnemonic = "StarlarkProcessDatabinding", + progress_message = "Processing data binding", + ) + +def _make_generate_binay_r_flags(resources_node): + if not (resources_node.r_txt or resources_node.manifest): + return None + return ",".join( + [ + resources_node.r_txt.path if resources_node.r_txt else "", + resources_node.manifest.path if resources_node.manifest else "", + ], + ) + +def _generate_binary_r( + ctx, + out_class_jar = None, + r_txt = None, + manifest = None, + package_for_r = None, + final_fields = None, + resources_nodes = depset(), + transitive_r_txts = [], + transitive_manifests = [], + busybox = None, + host_javabase = None): + """Generate compiled resources class jar. + + Args: + ctx: The context. + out_class_jar: File. The output class jar file. + r_txt: File. The resource IDs outputted by linking resources in text. + manifest: File. The primary AndroidManifest.xml. + package_for_r: String. The Java package for the generated R class files. + final_fields: Bool. Whether fields get declared as final. + busybox: FilesToRunProvider. The ResourceBusyBox executable or + FilesToRunprovider + host_javabase: A Target. The host javabase. + """ + args = ctx.actions.args() + args.add("--tool", "GENERATE_BINARY_R") + args.add("--") + args.add("--primaryRTxt", r_txt) + args.add("--primaryManifest", manifest) + args.add("--packageForR", package_for_r) + args.add_all( + resources_nodes, + map_each = _make_generate_binay_r_flags, + before_each = "--library", + ) + if final_fields: + args.add("--finalFields") + else: + args.add("--nofinalFields") + + # TODO(b/154003916): support transitive "--library transitive_r_txt_path,transitive_manifest_path" flags + args.add("--classJarOutput", out_class_jar) + args.add("--targetLabel", str(ctx.label)) + args.use_param_file("@%s") + + _java.run( + ctx = ctx, + host_javabase = host_javabase, + executable = busybox, + arguments = [args], + inputs = depset([r_txt, manifest], transitive = transitive_r_txts + transitive_manifests), + outputs = [out_class_jar], + mnemonic = "StarlarkRClassGenerator", + progress_message = "Generating R classes", + ) + +def _make_aar( + ctx, + out_aar = None, + assets = [], + assets_dir = None, + resource_files = [], + class_jar = None, + r_txt = None, + manifest = None, + proguard_specs = [], + should_throw_on_conflict = False, + busybox = None, + host_javabase = None): + """Generate an android archive file. + + Args: + ctx: The context. + out_aar: File. The output AAR file. + assets: sequence of Files. A list of Android assets files to be processed. + assets_dir: String. The name of the assets directory. + resource_files: A list of Files. The resource files. + class_jar: File. The class jar file. + r_txt: File. The resource IDs outputted by linking resources in text. + manifest: File. The primary AndroidManifest.xml. + proguard_specs: List of File. The proguard spec files. + busybox: FilesToRunProvider. The ResourceBusyBox executable or + FilesToRunprovider + host_javabase: A Target. The host javabase. + should_throw_on_conflict: A boolean. Determines whether an error should be thrown + when a resource conflict occurs. + """ + args = ctx.actions.args() + args.add("--tool", "GENERATE_AAR") + args.add("--") + args.add( + "--mainData", + _make_resources_flag( + manifest = manifest, + assets = assets, + assets_dir = assets_dir, + resource_files = resource_files, + ), + ) + args.add("--manifest", manifest) + args.add("--rtxt", r_txt) + args.add("--classes", class_jar) + args.add("--aarOutput", out_aar) + args.add_all(proguard_specs, before_each = "--proguardSpec") + if should_throw_on_conflict: + args.add("--throwOnResourceConflict") + + _java.run( + ctx = ctx, + host_javabase = host_javabase, + executable = busybox, + arguments = [args], + inputs = ( + resource_files + + assets + + proguard_specs + + [r_txt, manifest, class_jar] + ), + outputs = [out_aar], + mnemonic = "StarlarkAARGenerator", + progress_message = "Generating AAR package for %s" % ctx.label, + ) + +busybox = struct( + compile = _compile, + merge_compiled = _merge_compiled, + validate_and_link = _validate_and_link, + merge_manifests = _merge_manifests, + package = _package, + parse = _parse, + merge_assets = _merge_assets, + make_resources_flag = _make_resources_flag, + process_databinding = _process_databinding, + generate_binary_r = _generate_binary_r, + make_aar = _make_aar, + + # Exposed for testing + mergee_manifests_flag = _mergee_manifests_flag, + get_unique_res_dirs = _get_unique_res_dirs, + sanitize_assets_dir = _sanitize_assets_dir, + extract_filters = _extract_filters, +) diff --git a/rules/common.bzl b/rules/common.bzl new file mode 100644 index 0000000..6e84559 --- /dev/null +++ b/rules/common.bzl @@ -0,0 +1,111 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel common library for the Android rules.""" + +load(":java.bzl", _java = "java") +load(":utils.bzl", "get_android_sdk", "get_android_toolchain", _log = "log") + +# TODO(ostonge): Remove once kotlin/jvm_library.internal.bzl +# is updated and released to use the java.resolve_package function +def _java_package(label, custom_package): + return _java.resolve_package_from_label(label, custom_package) + +# Validates that the packages listed under "deps" all have the given constraint. If a package +# does not have this attribute, an error is generated. +def _validate_constraints(targets, constraint): + for target in targets: + if JavaInfo in target: + if constraint not in java_common.get_constraints(target[JavaInfo]): + _log.error("%s: does not have constraint '%s'" % (target.label, constraint)) + +TARGET_DNE = "Target '%s' does not exist or is a file and is not allowed." + +def _check_rule(targets): + _validate_constraints(targets, "android") + +def _get_java_toolchain(ctx): + if not hasattr(ctx.attr, "_java_toolchain"): + _log.error("Missing _java_toolchain attr") + return ctx.attr._java_toolchain + +def _get_host_javabase(ctx): + if not hasattr(ctx.attr, "_host_javabase"): + _log.error("Missing _host_javabase attr") + return ctx.attr._host_javabase + +def _sign_apk(ctx, unsigned_apk, signed_apk, keystore = None, signing_keys = [], signing_lineage = None): + """Signs an apk. Usage of keystore is deprecated. Prefer using signing_keys.""" + inputs = [unsigned_apk] + signer_args = ctx.actions.args() + signer_args.add("sign") + + if signing_keys: + inputs.extend(signing_keys) + for i, key in enumerate(signing_keys): + if i > 0: + signer_args.add("--next-signer") + signer_args.add("--ks") + signer_args.add(key.path) + signer_args.add("--ks-pass") + signer_args.add("pass:android") + if signing_lineage: + inputs.append(signing_lineage) + signer_args.add("--lineage", signing_lineage.path) + elif keystore: + inputs.append(keystore) + signer_args.add("--ks", keystore.path) + signer_args.add("--ks-pass", "pass:android") + + signer_args.add("--v1-signing-enabled", ctx.fragments.android.apk_signing_method_v1) + signer_args.add("--v1-signer-name", "CERT") + signer_args.add("--v2-signing-enabled", ctx.fragments.android.apk_signing_method_v2) + signer_args.add("--out", signed_apk.path) + signer_args.add(unsigned_apk.path) + ctx.actions.run( + executable = get_android_sdk(ctx).apk_signer, + inputs = inputs, + outputs = [signed_apk], + arguments = [signer_args], + mnemonic = "ApkSignerTool", + progress_message = "Signing APK for %s" % unsigned_apk.path, + ) + return signed_apk + +def _filter_zip(ctx, in_zip, out_zip, filters = []): + """Creates a copy of a zip file with files that match filters.""" + args = ctx.actions.args() + args.add("-q") + args.add(in_zip.path) + args.add_all(filters) + args.add("--copy") + args.add("--out") + args.add(out_zip.path) + ctx.actions.run( + executable = get_android_toolchain(ctx).zip_tool.files_to_run, + arguments = [args], + inputs = [in_zip], + outputs = [out_zip], + mnemonic = "FilterZip", + progress_message = "Filtering %s" % in_zip.short_path, + ) + +common = struct( + check_rule = _check_rule, + get_host_javabase = _get_host_javabase, + get_java_toolchain = _get_java_toolchain, + filter_zip = _filter_zip, + java_package = _java_package, + sign_apk = _sign_apk, +) diff --git a/rules/data_binding.bzl b/rules/data_binding.bzl new file mode 100644 index 0000000..9243673 --- /dev/null +++ b/rules/data_binding.bzl @@ -0,0 +1,299 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel Android Data Binding.""" + +load(":utils.bzl", _utils = "utils") + +# Data Binding context attributes. +_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS = \ + "java_annotation_processor_additional_inputs" +_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS = \ + "java_annotation_processor_additional_outputs" +_JAVA_PLUGINS = "java_plugins" +_JAVA_SRCS = "java_srcs" +_JAVAC_OPTS = "javac_opts" +_PROVIDERS = "providers" + +DataBindingContextInfo = provider( + doc = "Contains data from processing Android Data Binding.", + fields = { + _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS: ( + "Additional inputs required by the Java annotation processor." + ), + _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS: ( + "Additional outputs produced by the Java annotation processor." + ), + _JAVA_PLUGINS: "Data Binding Java annotation processor", + _JAVA_SRCS: "Java sources required by the Java annotation processor.", + _JAVAC_OPTS: ( + "Additional Javac opts required by the Java annotation processor." + ), + _PROVIDERS: "The list of all providers to propagate.", + }, +) + +# Path used when resources have not been defined. +_NO_RESOURCES_PATH = "/tmp/no_resources" + +def _copy_annotation_file(ctx, output_dir, annotation_template): + annotation_out = ctx.actions.declare_file( + output_dir + "/android/databinding/layouts/DataBindingInfo.java", + ) + _utils.copy_file(ctx, annotation_template, annotation_out) + return annotation_out + +def _gen_sources(ctx, output_dir, java_package, deps, layout_info, data_binding_exec): + class_info = ctx.actions.declare_file(output_dir + "class-info.zip") + srcjar = ctx.actions.declare_file(output_dir + "baseClassSrc.srcjar") + + args = ctx.actions.args() + args.add("-layoutInfoFiles", layout_info) + args.add("-package", java_package) + args.add("-classInfoOut", class_info) + args.add("-sourceOut", srcjar) + args.add("-zipSourceOutput", "true") + args.add("-useAndroidX", "false") + + if deps: + if type(deps[0].class_infos) == "depset": + class_infos = depset(transitive = [info.class_infos for info in deps]) + inputs = depset(direct = [layout_info], transitive = [class_infos]) + elif type(deps[0].class_infos) == "list": + class_infos = [] + for info in deps: + class_infos.extend(info.class_infos) + inputs = class_infos + [layout_info] + else: + fail("Expected list or depset. Got %s" % type(deps[0].class_infos)) + else: + class_infos = [] + inputs = [layout_info] + + args.add_all(class_infos, before_each = "-dependencyClassInfoList") + + ctx.actions.run( + executable = data_binding_exec, + arguments = ["GEN_BASE_CLASSES", args], + inputs = inputs, + outputs = [class_info, srcjar], + mnemonic = "GenerateDataBindingBaseClasses", + progress_message = ( + "GenerateDataBindingBaseClasses %s" % class_info.short_path + ), + ) + return srcjar, class_info + +def _setup_dependent_lib_artifacts(ctx, output_dir, deps): + # DataBinding requires files in very specific locations. + # The following expand_template (copy actions) are moving the files + # to the correct locations. + dep_lib_artifacts = [] + for info in deps: + # Yes, DataBinding requires depsets iterations. + for artifact in (info.transitive_br_files.to_list() + + _utils.list_or_depset_to_list(info.setter_stores) + + _utils.list_or_depset_to_list(info.class_infos)): + # short_path might contain a parent directory reference if the + # databinding artifact is from an external repository (e.g. an aar + # from Maven). If that's the case, just remove the parent directory + # reference, otherwise the "dependent-lib-artifacts" directory will + # get removed by the "..". + path = artifact.short_path + if path.startswith("../"): + path = path[3:] + dep_lib_artifact = ctx.actions.declare_file( + output_dir + "dependent-lib-artifacts/" + path, + ) + + # Copy file to a location required by the DataBinding annotation + # processor. + # TODO(djwhang): Look into SymlinkAction. + if artifact.is_directory: + _utils.copy_dir(ctx, artifact, dep_lib_artifact) + else: + _utils.copy_file(ctx, artifact, dep_lib_artifact) + dep_lib_artifacts.append(dep_lib_artifact) + return dep_lib_artifacts + +def _get_javac_opts( + ctx, + java_package, + dependency_artifacts_dir, + aar_out_dir, + class_info_path, + layout_info_path, + deps): + java_packages = [] + for info in deps: + for label_and_java_package in info.label_and_java_packages: + java_packages.append(label_and_java_package.java_package) + + javac_opts = [] + javac_opts.append("-Aandroid.databinding.dependencyArtifactsDir=" + + dependency_artifacts_dir) + javac_opts.append("-Aandroid.databinding.aarOutDir=" + aar_out_dir) + javac_opts.append("-Aandroid.databinding.sdkDir=/not/used") + javac_opts.append("-Aandroid.databinding.artifactType=LIBRARY") + javac_opts.append("-Aandroid.databinding.exportClassListOutFile=" + + "/tmp/exported_classes") + javac_opts.append("-Aandroid.databinding.modulePackage=" + java_package) + javac_opts.append("-Aandroid.databinding.directDependencyPkgs=[%s]" % + ",".join(java_packages)) + + # The minimum Android SDK compatible with this rule. + # TODO(djwhang): This probably should be based on the actual min-sdk from + # the manifest, or an appropriate rule attribute. + javac_opts.append("-Aandroid.databinding.minApi=14") + javac_opts.append("-Aandroid.databinding.enableV2=1") + + javac_opts.append("-Aandroid.databinding.classLogDir=" + class_info_path) + javac_opts.append("-Aandroid.databinding.layoutInfoDir=" + layout_info_path) + return javac_opts + +def _process( + ctx, + resources_ctx = None, + defines_resources = False, + enable_data_binding = False, + java_package = None, + layout_info = None, + deps = [], + exports = [], + data_binding_exec = None, + data_binding_annotation_processor = None, + data_binding_annotation_template = None): + """Processes Android Data Binding. + + Args: + ctx: The context. + resources_ctx: The Android Resources context. + defines_resources: boolean. Determines whether resources were defined. + enable_data_binding: boolean. Determines whether Data Binding should be + enabled. + java_package: String. The Java package. + deps: sequence of DataBindingV2Info providers. A list of deps. Optional. + exports: sequence of DataBindingV2Info providers. A list of exports. + Optional. + layout_info: A file. The layout-info zip file. + data_binding_exec: The DataBinding executable. + data_binding_annotation_processor: JavaInfo. The JavaInfo for the + annotation processor. + data_binding_annotation_template: A file. Used to generate data binding + classes. + + Returns: + A DataBindingContextInfo provider. + """ + + # TODO(b/154513292): Clean up bad usages of context objects. + if resources_ctx: + defines_resources = resources_ctx.defines_resources + + # The Android Data Binding context object. + db_info = { + _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS: [], + _JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS: [], + _JAVA_PLUGINS: [], + _JAVA_SRCS: [], + _JAVAC_OPTS: [], + _PROVIDERS: [], + } + + if not enable_data_binding: + db_info[_PROVIDERS] = [ + DataBindingV2Info( + databinding_v2_providers_in_deps = deps, + databinding_v2_providers_in_exports = exports, + ), + ] + return struct(**db_info) + + output_dir = "databinding/%s/" % ctx.label.name + + db_info[_JAVA_SRCS].append(_copy_annotation_file( + ctx, + output_dir, + data_binding_annotation_template, + )) + db_info[_JAVA_PLUGINS].append(data_binding_annotation_processor) + + br_out = None + setter_store_out = None + class_info = None + if defines_resources: + # Outputs of the Data Binding annotation processor. + br_out = ctx.actions.declare_file( + output_dir + "bin-files/%s-br.bin" % java_package, + ) + db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS].append(br_out) + setter_store_out = ctx.actions.declare_file( + output_dir + "bin-files/%s-setter_store.json" % java_package, + ) + db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_OUTPUTS].append( + setter_store_out, + ) + + srcjar, class_info = _gen_sources( + ctx, + output_dir, + java_package, + deps, + layout_info, + data_binding_exec, + ) + db_info[_JAVA_SRCS].append(srcjar) + db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS].append(class_info) + db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS].append( + layout_info, + ) + + dep_lib_artifacts = _setup_dependent_lib_artifacts(ctx, output_dir, deps) + db_info[_JAVA_ANNOTATION_PROCESSOR_ADDITIONAL_INPUTS].extend( + dep_lib_artifacts, + ) + + db_info[_JAVAC_OPTS] = _get_javac_opts( + ctx, + java_package, + ( + br_out.path.rpartition(br_out.short_path)[0] + + ctx.label.package + + "/" + + output_dir + + "dependent-lib-artifacts" + ), + br_out.dirname, + class_info.path if class_info else _NO_RESOURCES_PATH, + layout_info.path if layout_info else _NO_RESOURCES_PATH, + deps, + ) + + db_info[_PROVIDERS] = [ + DataBindingV2Info( + setter_store_file = setter_store_out, + class_info_file = class_info, + br_file = br_out, + label = str(ctx.label), + java_package = java_package, + databinding_v2_providers_in_deps = deps, + databinding_v2_providers_in_exports = exports, + ), + ] + + return DataBindingContextInfo(**db_info) + +data_binding = struct( + process = _process, +) diff --git a/rules/data_binding_annotation_template.txt b/rules/data_binding_annotation_template.txt new file mode 100644 index 0000000..295397e --- /dev/null +++ b/rules/data_binding_annotation_template.txt @@ -0,0 +1,15 @@ +package android.databinding.layouts; + +import android.databinding.BindingBuildInfo; + +/** + * Template for the file that feeds data binding's annotation processor. The + * processor reads the values set here to generate .java files that link XML + * data binding declarations (from layoutInfoDir) to app code. + */ +@BindingBuildInfo( + buildId="not_used_here" // Adds incrementality, which Bazel already supports +) +public class DataBindingInfo { + /* This only exists for annotation processing. */ +} diff --git a/rules/flags/BUILD b/rules/flags/BUILD new file mode 100644 index 0000000..4895b09 --- /dev/null +++ b/rules/flags/BUILD @@ -0,0 +1,22 @@ +# Flags for Android rules and mobile-install + +load("@rules_android//rules/flags:flags.bzl", "flags") +load("@rules_android//rules/flags:flag_defs.bzl", "define_flags") +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") + + +licenses(["notice"]) + +filegroup( + name = "all_files", + srcs = glob(["**"]), +) + +bzl_library( + name = "bzl", + srcs = glob(["*.bzl"]), +) + +define_flags() + +flags.FLAGS() diff --git a/rules/flags/flag_defs.bzl b/rules/flags/flag_defs.bzl new file mode 100644 index 0000000..08eee2d --- /dev/null +++ b/rules/flags/flag_defs.bzl @@ -0,0 +1,93 @@ +# Copyright 2019 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Flag definitions.""" + +load("@rules_android//rules/flags:flags.bzl", "flags") + +def define_flags(): + flags.DEFINE_bool( + name = "android_enable_res_v3", + default = False, + description = "Enable Resource Processing Pipeline v3.", + ) + + flags.DEFINE_bool( + name = "use_direct_deploy", + default = False, + description = "Enable direct deployment.", + ) + + flags.DEFINE_int( + name = "num_dex_shards", + default = 32, + description = "Number of dex shards to use for mobile-install.", + ) + + flags.DEFINE_bool( + name = "use_custom_dex_shards", + default = False, + description = "Whether to use custom dex shard value for mobile-install.", + ) + + flags.DEFINE_bool_group( + name = "mi_v3", + default = True, + description = "Enable mobile-install v3.", + flags = [ + # TODO(b/160897244): resv3 temporarily disabled while Starlark + # resource processing is implemented and rolled out + # ":android_enable_res_v3", + ":use_custom_dex_shards", + ":use_direct_deploy", + ], + ) + + flags.DEFINE_bool_group( + name = "mi_dogfood", + default = False, + description = "Opt-in to mobile-install dogfood track.", + flags = [ + ], + ) + + flags.DEFINE_bool( + name = "enable_splits", + default = True, + description = "Build and install split apks if the device supports them.", + ) + + flags.DEFINE_bool( + name = "use_adb_root", + default = True, + description = "Restart adb with root permissions.", + ) + + flags.DEFINE_bool( + name = "mi_desugar_java8_libs", + default = False, + description = "Set True with --config=android_java8_libs", + ) + + flags.DEFINE_bool( + name = "debug", + default = False, + description = "", + ) + + + flags.EXPOSE_native_bool( + name = "stamp", + description = "Accesses the native --stamp CLI flag", + ) diff --git a/rules/flags/flags.bzl b/rules/flags/flags.bzl new file mode 100644 index 0000000..e6e25a6 --- /dev/null +++ b/rules/flags/flags.bzl @@ -0,0 +1,255 @@ +# Copyright 2019 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel Flags.""" + +load("@rules_android//rules:utils.bzl", "utils") + +_BoolFlagInfo = provider( + doc = "Provides information about a boolean flag", + fields = dict( + name = "flag name", + value = "flag value", + explicit = "whether value was set explicitly", + ), +) +_BoolFlagGroupInfo = provider( + doc = "Provides information about a boolean flag group", + fields = dict( + name = "group name", + value = "group value", + flags = "flag names that belong to this group", + ), +) +_IntFlagInfo = provider( + doc = "Provides information about an integer flag", + fields = dict( + name = "flag name", + value = "flag value", + ), +) +_NativeBoolFlagInfo = provider( + doc = "Provides information about a native boolean flag", + fields = dict( + name = "flag name, the name of the native flag being accessed.", + value = "flag value, derived from config_setting targets that access the value", + ), +) +FlagsInfo = provider( + doc = "Provides all flags", +) + +def _native_bool_impl(ctx): + return _NativeBoolFlagInfo( + name = ctx.label.name, + value = ctx.attr.value, + ) + +native_bool_flag = rule( + implementation = _native_bool_impl, + attrs = dict( + value = attr.bool(mandatory = True), + ), + provides = [_NativeBoolFlagInfo], +) + +def native_bool_flag_macro(name, description): + """Provides access to a native boolean flag from Starlark. + + Args: + name: The name of the native flag to access. + description: The description of the flag. + """ + native.config_setting( + name = name + "_on", + values = {name: "True"}, + ) + native.config_setting( + name = name + "_off", + values = {name: "False"}, + ) + native_bool_flag( + name = name, + value = select({ + (":" + name + "_on"): True, + (":" + name + "_off"): False, + }), + ) + +def _get_bool(v): + v = v.lower() + if v == "true": + return True + if v == "false": + return False + fail("Unknown bool: " + v) + +def _bool_impl(ctx): + if ctx.label.name in ctx.var: + value = _get_bool(ctx.var[ctx.label.name]) + return _BoolFlagInfo( + name = ctx.label.name, + value = value, + explicit = True, + ) + return _BoolFlagInfo( + name = ctx.label.name, + value = ctx.attr.default, + explicit = False, + ) + +bool_flag = rule( + implementation = _bool_impl, + attrs = dict( + default = attr.bool( + mandatory = True, + ), + description = attr.string( + mandatory = True, + ), + ), + provides = [_BoolFlagInfo], +) + +def _bool_group_impl(ctx): + if ctx.label.name in ctx.var: + value = _get_bool(ctx.var[ctx.label.name]) + return _BoolFlagGroupInfo( + name = ctx.label.name, + value = value, + flags = [f[_BoolFlagInfo].name for f in ctx.attr.flags], + ) + return _BoolFlagGroupInfo( + name = ctx.label.name, + value = ctx.attr.default, + flags = [f[_BoolFlagInfo].name for f in ctx.attr.flags], + ) + +bool_flag_group = rule( + implementation = _bool_group_impl, + attrs = dict( + default = attr.bool( + mandatory = True, + ), + description = attr.string( + mandatory = True, + ), + flags = attr.label_list( + mandatory = True, + providers = [_BoolFlagInfo], + ), + ), + provides = [_BoolFlagGroupInfo], +) + +def _int_impl(ctx): + if ctx.label.name in ctx.var: + value = int(ctx.var[ctx.label.name]) + else: + value = ctx.attr.default + return _IntFlagInfo( + name = ctx.label.name, + value = value, + ) + +int_flag = rule( + implementation = _int_impl, + attrs = dict( + default = attr.int( + mandatory = True, + ), + description = attr.string( + mandatory = True, + ), + ), + provides = [_IntFlagInfo], +) + +def _flags_impl_internal(bool_flags, bool_flag_groups, int_flags, native_bool_flags): + flags = dict() + + # For each group, set all flags to the group value + for fg in bool_flag_groups: + for f in fg.flags: + if f in flags: + fail("Flag '%s' referenced in multiple flag groups" % f) + flags[f] = fg.value + + # Set booleans + for b in bool_flags: + # Always set explicitly specified flags + if b.explicit: + flags[b.name] = b.value + # If not explicit, only set when not set by a group + + elif b.name not in flags: + flags[b.name] = b.value + + # Set ints + for i in int_flags: + flags[i.name] = i.value + + # Set native bool flags + for n in native_bool_flags: + if n.name in flags: + fail("Flag '%s' defined as both native and non-native flag type" % n.name) + flags[n.name] = n.value + + return FlagsInfo(**flags) + +def _flags_impl(ctx): + return _flags_impl_internal( + utils.collect_providers(_BoolFlagInfo, ctx.attr.targets), + utils.collect_providers(_BoolFlagGroupInfo, ctx.attr.targets), + utils.collect_providers(_IntFlagInfo, ctx.attr.targets), + utils.collect_providers(_NativeBoolFlagInfo, ctx.attr.targets), + ) + +flags_rule = rule( + implementation = _flags_impl, + attrs = dict( + targets = attr.label_list(), + ), +) + +def _flags_macro(): + flags_rule( + name = "flags", + targets = native.existing_rules().keys(), + visibility = ["//visibility:public"], + ) + +def _get_flags(ctx): + return ctx.attr._flags[FlagsInfo] + +flags = struct( + DEFINE_bool = bool_flag, + DEFINE_bool_group = bool_flag_group, + DEFINE_int = int_flag, + EXPOSE_native_bool = native_bool_flag_macro, + FLAGS = _flags_macro, + FlagsInfo = FlagsInfo, + get = _get_flags, +) + +exported_for_test = struct( + BoolFlagGroupInfo = _BoolFlagGroupInfo, + BoolFlagInfo = _BoolFlagInfo, + IntFlagInfo = _IntFlagInfo, + NativeBoolFlagInfo = _NativeBoolFlagInfo, + bool_impl = _bool_impl, + flags_impl_internal = _flags_impl_internal, + int_impl = _int_impl, + native_bool_flag_macro = native_bool_flag_macro, +) diff --git a/rules/idl.bzl b/rules/idl.bzl new file mode 100644 index 0000000..8dc52d3 --- /dev/null +++ b/rules/idl.bzl @@ -0,0 +1,264 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel Android IDL library for the Android rules.""" + +load(":java.bzl", _java = "java") +load(":path.bzl", _path = "path") +load(":utils.bzl", _log = "log") + +_AIDL_TOOLCHAIN_MISSING_ERROR = ( + "IDL sources provided without the Android IDL toolchain." +) + +_AIDL_JAVA_ROOT_UNDETERMINABLE_ERROR = ( + "Cannot determine java/javatests root for import %s." +) + +IDLContextInfo = provider( + doc = "Contains data from processing Android IDL.", + fields = dict( + idl_srcs = "List of IDL sources", + idl_import_root = "IDL import root", + idl_java_srcs = "List of IDL Java sources", + idl_deps = + "List of IDL targets required for Java compilation, Proguard, etc.", + providers = "The list of all providers to propagate.", + ), +) + +def _gen_java_from_idl( + ctx, + out_idl_java_src = None, + idl_src = None, + transitive_idl_import_roots = [], + transitive_idl_imports = [], + transitive_idl_preprocessed = [], + aidl = None, + aidl_lib = None, + aidl_framework = None): + args = ctx.actions.args() + args.add("-b") + args.add_all(transitive_idl_import_roots, format_each = "-I%s") + args.add(aidl_framework, format = "-p%s") + args.add_all(transitive_idl_preprocessed, format_each = "-p%s") + args.add(idl_src) + args.add(out_idl_java_src) + + ctx.actions.run( + executable = aidl, + arguments = [args], + inputs = depset( + [aidl_framework], + transitive = [ + aidl_lib.files, + transitive_idl_imports, + transitive_idl_preprocessed, + ], + ), + outputs = [out_idl_java_src], + mnemonic = "AndroidIDLGenerate", + progress_message = "Android IDL generation %s" % idl_src.path, + ) + +def _get_idl_import_root_path( + package, + idl_import_root, + idl_file_root_path): + package_path = _path.relative( + idl_file_root_path, + package, + ) + return _path.relative( + package_path, + idl_import_root, + ) + +def _collect_unique_idl_import_root_paths( + package, + idl_import_root, + idl_imports): + idl_import_roots = dict() + for idl_import in idl_imports: + idl_import_roots[_get_idl_import_root_path( + package, + idl_import_root, + idl_import.root.path, + )] = True + return sorted(idl_import_roots.keys()) + +def _collect_unique_java_roots(idl_imports): + idl_import_roots = dict() + for idl_import in idl_imports: + java_root = _java.root(idl_import.path) + if not java_root: + _log.error(_AIDL_JAVA_ROOT_UNDETERMINABLE_ERROR % idl_import.path) + idl_import_roots[java_root] = True + return sorted(idl_import_roots.keys()) + +def _determine_idl_import_roots( + package, + idl_import_root = None, + idl_imports = []): + if idl_import_root == None: + return _collect_unique_java_roots(idl_imports) + return _collect_unique_idl_import_root_paths( + package, + idl_import_root, + idl_imports, + ) + +def _process( + ctx, + idl_srcs = [], + idl_parcelables = [], + idl_import_root = None, + idl_preprocessed = [], + deps = [], + exports = [], + aidl = None, + aidl_lib = None, + aidl_framework = None): + """Processes Android IDL. + + Args: + ctx: The context. + idl_srcs: sequence of Files. A list of the aidl source files to be + processed into Java source files and then compiled. Optional. + idl_parcelables: sequence of Files. A list of Android IDL definitions to + supply as imports. These files will be made available as imports for any + android_library target that depends on this library, directly or via its + transitive closure, but will not be translated to Java or compiled. + + Only .aidl files that correspond directly to .java sources in this library + should be included (e.g. custom implementations of Parcelable), otherwise + idl_srcs should be used. + + These files must be placed appropriately for the aidl compiler to find + them. See the description of idl_import_root for information about what + this means. Optional. + idl_import_root: string. Package-relative path to the root of the java + package tree containing idl sources included in this library. This path + will be used as the import root when processing idl sources that depend on + this library. + + When idl_import_root is specified, both idl_parcelables and idl_srcs must + be at the path specified by the java package of the object they represent + under idl_import_root. When idl_import_root is not specified, both + idl_parcelables and idl_srcs must be at the path specified by their + package under a Java root. Optional. + idl_preprocessed: sequence of Files. A list of preprocessed Android IDL + definitions to supply as imports. These files will be made available as + imports for any android_library target that depends on this library, + directly or via its transitive closure, but will not be translated to + Java or compiled. + + Only preprocessed .aidl files that correspond directly to .java sources + in this library should be included (e.g. custom implementations of + Parcelable), otherwise use idl_srcs for Android IDL definitions that + need to be translated to Java interfaces and use idl_parcelable for + non-preprcessed AIDL files. Optional. + deps: sequence of Targets. A list of dependencies. Optional. + exports: sequence of Targets. A list of exports. Optional. + aidl: Target. A target pointing to the aidl executable to be used for + Java code generation from *.idl source files. Optional, unless idl_srcs + are supplied. + aidl_lib: Target. A target pointing to the aidl_lib library required + during Java compilation when Java code is generated from idl sources. + Optional, unless idl_srcs are supplied. + aidl_framework: Target. A target pointing to the aidl framework. Optional, + unless idl_srcs are supplied. + + Returns: + A IDLContextInfo provider. + """ + if idl_srcs and not (aidl and aidl_lib and aidl_framework): + _log.error(_AIDL_TOOLCHAIN_MISSING_ERROR) + + transitive_idl_import_roots = [] + transitive_idl_imports = [] + transitive_idl_preprocessed = [] + for dep in deps + exports: + transitive_idl_import_roots.append(dep.transitive_idl_import_roots) + transitive_idl_imports.append(dep.transitive_idl_imports) + transitive_idl_preprocessed.append(dep.transitive_idl_preprocessed) + + idl_java_srcs = [] + for idl_src in idl_srcs: + idl_java_src = ctx.actions.declare_file( + ctx.label.name + "_aidl/" + idl_src.path.replace(".aidl", ".java"), + ) + idl_java_srcs.append(idl_java_src) + _gen_java_from_idl( + ctx, + out_idl_java_src = idl_java_src, + idl_src = idl_src, + transitive_idl_import_roots = depset( + _determine_idl_import_roots( + ctx.label.package, + idl_import_root, + idl_parcelables + idl_srcs, + ), + transitive = transitive_idl_import_roots, + order = "preorder", + ), + transitive_idl_imports = depset( + idl_parcelables + idl_srcs, + transitive = transitive_idl_imports, + order = "preorder", + ), + transitive_idl_preprocessed = depset( + transitive = transitive_idl_preprocessed, + ), + aidl = aidl, + aidl_lib = aidl_lib, + aidl_framework = aidl_framework, + ) + + return IDLContextInfo( + idl_srcs = idl_srcs, + idl_import_root = idl_import_root, + idl_java_srcs = idl_java_srcs, + idl_deps = [aidl_lib] if idl_java_srcs else [], + providers = [ + # TODO(b/146216105): Make this a Starlark provider. + AndroidIdlInfo( + depset( + _determine_idl_import_roots( + ctx.label.package, + idl_import_root, + idl_parcelables + idl_srcs + idl_preprocessed, + ), + transitive = transitive_idl_import_roots, + order = "preorder", + ), + depset( + idl_parcelables + idl_srcs + idl_preprocessed, + transitive = transitive_idl_imports, + order = "preorder", + ), + depset(), # TODO(b/146216105): Delete this field once in Starlark. + depset(idl_preprocessed, transitive = transitive_idl_preprocessed), + ), + ], + ) + +idl = struct( + process = _process, +) + +# Visible for testing. +testing = struct( + get_idl_import_root_path = _get_idl_import_root_path, +) diff --git a/rules/intellij.bzl b/rules/intellij.bzl new file mode 100644 index 0000000..c870b1b --- /dev/null +++ b/rules/intellij.bzl @@ -0,0 +1,165 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Common methods for use by the IntelliJ Aspect.""" + +load(":java.bzl", _java = "java") +load(":utils.bzl", _utils = "utils") + +def _extract_idl_jars( + ctx, + idl_java_srcs = [], + jar = None, + manifest_proto = None, + out_srcjar = None, + out_jar = None, + idlclass = None, + host_javabase = None): + """Extracts the idl class and src jars.""" + args = ctx.actions.args() + args.add("--class_jar", jar) + args.add("--manifest_proto", manifest_proto) + args.add("--output_class_jar", out_jar) + args.add("--output_source_jar", out_srcjar) + args.add("--temp_dir", out_jar.dirname) + args.add_all(idl_java_srcs) + + _java.run( + ctx = ctx, + host_javabase = host_javabase, + executable = idlclass, + arguments = [args], + inputs = idl_java_srcs + [jar, manifest_proto], + outputs = [out_srcjar, out_jar], + mnemonic = "AndroidIdlJars", + progress_message = "Building idl jars %s" % out_jar.path, + ) + +def _make_android_ide_info( + ctx, + idl_ctx = None, + resources_ctx = None, + defines_resources = False, + java_package = None, + manifest = None, + merged_manifest = None, + resources_apk = None, + idl_import_root = None, + idl_srcs = [], + idl_java_srcs = [], + java_info = None, + r_jar = None, + signed_apk = None, + aar = None, + apks_under_test = [], + native_libs = dict(), + idlclass = None, + host_javabase = None): + # TODO(b/154513292): Clean up bad usages of context objects. + if idl_ctx: + idl_import_root = idl_ctx.idl_import_root + idl_srcs = idl_ctx.idl_srcs + idl_java_srcs = idl_ctx.idl_java_srcs + if resources_ctx: + defines_resources = resources_ctx.defines_resources + merged_manifest = resources_ctx.merged_manifest + resources_apk = resources_ctx.resources_apk + + if not defines_resources: + java_package = None + merged_manifest = None + + # Extracts idl related classes from the jar and creates a src jar + # for the idl generated java. + idl_jar = None + idl_srcjar = None + + # TODO(djwhang): JavaInfo.outputs.jar.manifest_proto is not created by + # Kotlin compile. Determine if this is the same manifest_proto produced + # by turbine, this could be pulled during annotation processing. + jar = _utils.only(java_info.outputs.jars) + if idl_java_srcs and jar.manifest_proto: + idl_jar = ctx.actions.declare_file("lib%s-idl.jar" % ctx.label.name) + idl_srcjar = \ + ctx.actions.declare_file("lib%s-idl.srcjar" % ctx.label.name) + + jar = _utils.only(java_info.outputs.jars) + _extract_idl_jars( + ctx, + idl_java_srcs = idl_java_srcs, + jar = jar.class_jar, + manifest_proto = jar.manifest_proto, + out_jar = idl_jar, + out_srcjar = idl_srcjar, + idlclass = idlclass, + host_javabase = host_javabase, + ) + + return AndroidIdeInfo( + java_package, + manifest, + merged_manifest, + idl_import_root, + idl_srcs, + idl_java_srcs, + idl_srcjar, + idl_jar, + defines_resources, + r_jar, + resources_apk, + signed_apk, + aar, + apks_under_test, + native_libs, + ) + +def _make_legacy_android_provider(android_ide_info): + # Create the ClassJar "object" for the target.android.idl.output field. + if android_ide_info.idl_class_jar: + idl_class_jar = struct( + class_jar = android_ide_info.idl_class_jar, + ijar = None, + source_jar = android_ide_info.idl_source_jar, + ) + else: + idl_class_jar = None + + return struct( + aar = android_ide_info.aar, + apk = android_ide_info.signed_apk, + apks_under_test = android_ide_info.apks_under_test, + defines_resources = android_ide_info.defines_android_resources, + idl = struct( + import_root = android_ide_info.idl_import_root, + sources = android_ide_info.idl_srcs, + generated_java_files = android_ide_info.idl_generated_java_files, + output = idl_class_jar, + ), + java_package = android_ide_info.java_package, + manifest = android_ide_info.manifest, + merged_manifest = android_ide_info.generated_manifest, + native_libs = android_ide_info.native_libs, + resource_apk = android_ide_info.resource_apk, + resource_jar = android_ide_info.resource_jar, + ) + +intellij = struct( + make_android_ide_info = _make_android_ide_info, + make_legacy_android_provider = _make_legacy_android_provider, +) + +# Only visible for testing. +testing = struct( + extract_idl_jars = _extract_idl_jars, +) diff --git a/rules/java.bzl b/rules/java.bzl new file mode 100644 index 0000000..8193bdf --- /dev/null +++ b/rules/java.bzl @@ -0,0 +1,454 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel Java APIs for the Android rules.""" + +load(":path.bzl", _path = "path") +load(":utils.bzl", "log") + +_ANDROID_CONSTRAINT_MISSING_ERROR = ( + "A list of constraints provided without the 'android' constraint." +) + +def _segment_idx(path_segments): + """Finds the index of the segment in the path that preceeds the source root. + + Args: + path_segments: A list of strings, where each string is the segment of a + filesystem path. + + Returns: + An index to the path segment that represents the Java segment or -1 if + none found. + """ + if _path.is_absolute(path_segments[0]): + log.error("path must not be absolute: %s" % _path.join(path_segments)) + + root_idx = -1 + for idx, segment in enumerate(path_segments): + if segment in ["java", "javatests", "src", "testsrc"]: + root_idx = idx + break + if root_idx < 0: + return root_idx + + is_src = path_segments[root_idx] == "src" + check_maven_idx = root_idx if is_src else -1 + if root_idx == 0 or is_src: + # Check for a nested root directory. + for idx in range(root_idx + 1, len(path_segments) - 2): + segment = path_segments[idx] + if segment == "src" or (is_src and segment in ["java", "javatests"]): + next_segment = path_segments[idx + 1] + if next_segment in ["com", "org", "net"]: + root_idx = idx + elif segment == "src": + check_maven_idx = idx + break + + if check_maven_idx >= 0 and check_maven_idx + 2 < len(path_segments): + next_segment = path_segments[check_maven_idx + 1] + if next_segment in ["main", "test"]: + next_segment = path_segments[check_maven_idx + 2] + if next_segment in ["java", "resources"]: + root_idx = check_maven_idx + 2 + return root_idx + +def _resolve_package(path): + """Determines the Java package name from the given path. + + Examples: + "{workspace}/java/foo/bar/wiz" -> "foo.bar.wiz" + "{workspace}/javatests/foo/bar/wiz" -> "foo.bar.wiz" + + Args: + path: A string, representing a file path. + + Returns: + A string representing a Java package name or None if could not be + determined. + """ + path_segments = _path.split(path.partition(":")[0]) + java_idx = _segment_idx(path_segments) + if java_idx < 0: + return None + else: + return ".".join(path_segments[java_idx + 1:]) + +def _resolve_package_from_label( + label, + custom_package = None, + fallback = True): + """Resolves the Java package from a Label. + + When no legal Java package can be resolved from the label, None will be + returned unless fallback is specified. + + When a fallback is requested, a not safe for Java compilation package will + be returned. The fallback value will be derrived by taking the label.package + and replacing all path separators with ".". + """ + if custom_package: + return custom_package + + # For backwards compatibility, also include directories + # from the label's name + # Ex: "//foo/bar:java/com/google/baz" is a legal one and + # results in "com.google" + label_path = _path.join( + [label.package] + + _path.split(label.name)[:-1], + ) + java_package = _resolve_package(label_path) + + if java_package != None: # "" is a valid result. + return java_package + + if fallback: + return label.package.replace("/", ".") + + return None + +def _root(path): + """Determines the Java root from the given path. + + Examples: + "{workspace}/java/foo/bar/wiz" -> "{workspace}/java" + "{workspace}/javatests/foo/bar/wiz" -> "{workspace}/javatests" + "java/foo/bar/wiz" -> "java" + "javatests/foo/bar/wiz" -> "javatests" + + Args: + path: A string, representing a file path. + + Returns: + A string representing the Java root path or None if could not be + determined. + """ + path_segments = _path.split(path.partition(":")[0]) + java_idx = _segment_idx(path_segments) + if java_idx < 0: + return None + else: + return _path.join(path_segments[0:java_idx + 1]) + +def _check_for_invalid_java_package(java_package): + return "-" in java_package or len(java_package.split(".")) < 2 + +def _invalid_java_package(custom_package, java_package): + """Checks if the given java package is invalid. + + Only checks if either custom_package or java_package contains the + illegal character "-" or if they are composed of only one word. + Only checks java_package if custom_package is an empty string or None. + + Args: + custom_package: string. Java package given as an attribute to a rule to override + the java_package. + java_package: string. Java package inferred from the directory where the BUILD + containing the rule is. + + Returns: + A boolean. True if custom_package or java_package contains "-" or is only one word. + Only checks java_package if custom_package is an empty string or None. + """ + return ( + (custom_package and _check_for_invalid_java_package(custom_package)) or + (not custom_package and _check_for_invalid_java_package(java_package)) + ) + +# The Android specific Java compile. +def _compile_android( + ctx, + output_jar, + output_srcjar = None, + srcs = [], + resources = [], + javac_opts = [], + r_java = None, + deps = [], + exports = [], + plugins = [], + exported_plugins = [], + annotation_processor_additional_outputs = [], + annotation_processor_additional_inputs = [], + enable_deps_without_srcs = False, + neverlink = False, + constraints = ["android"], + strict_deps = "Error", + java_toolchain = None): + """Compiles the Java and IDL sources for Android. + + Args: + ctx: The context. + output_jar: File. The artifact to place the compilation unit. + output_srcjar: File. The artifact to place the sources of the compilation + unit. Optional. + srcs: sequence of Files. A list of files and jars to be compiled. + resources: sequence of Files. Will be added to the output jar - see + java_library.resources. Optional. + javac_opts: sequence of strings. A list of the desired javac options. + Optional. + r_java: JavaInfo. The R.jar dependency. Optional. + deps: sequence of JavaInfo providers. A list of dependencies. Optional. + exports: sequence of JavaInfo providers. A list of exports. Optional. + plugins: sequence of JavaPluginInfo providers. A list of plugins. Optional. + exported_plugins: sequence of JavaPluginInfo providers. A list of exported + plugins. Optional. + annotation_processor_additional_outputs: sequence of Files. A list of + files produced by an annotation processor. + annotation_processor_additional_inputs: sequence of Files. A list of + files consumed by an annotation processor. + enable_deps_without_srcs: Enables the behavior from b/14473160. + neverlink: Bool. Makes the compiled unit a compile-time only dependency. + constraints: sequence of Strings. A list of constraints, to constrain the + target. Optional. By default []. + strict_deps: string. A string that specifies how to handle strict deps. + Possible values: 'OFF', 'ERROR','WARN' and 'DEFAULT'. For more details + see https://docs.bazel.build/versions/master/user-manual.html#flag--strict_java_deps. + By default 'ERROR'. + java_toolchain: The java_toolchain Target. + + Returns: + A JavaInfo provider representing the Java compilation. + """ + if "android" not in constraints: + log.error(_ANDROID_CONSTRAINT_MISSING_ERROR) + + if not srcs: + if deps and enable_deps_without_srcs: + # TODO(b/122039567): Produces a JavaInfo that exports the deps, but + # not the plugins. To reproduce the "deps without srcs" bug, + # b/14473160, behavior in Starlark. + exports = exports + [ + android_common.enable_implicit_sourceless_deps_exports_compatibility(dep) + for dep in deps + ] + if not exports: + # Add a "no-op JavaInfo" to propagate the exported_plugins when + # deps or exports have not been specified by the target and + # additionally forces java_common.compile method to create the + # empty output jar and srcjar when srcs have not been specified. + noop_java_info = java_common.merge([]) + exports = exports + [noop_java_info] + + r_java_info = [r_java] if r_java else [] + + java_info = _compile( + ctx, + output_jar, + output_srcjar = output_srcjar, + srcs = srcs, + resources = resources, + javac_opts = javac_opts, + deps = r_java_info + deps, + # In native, the JavaInfo exposes two Jars as compile-time deps, the + # compiled sources and the Android R.java jars. To simulate this + # behavior, the JavaInfo of the R.jar is also exported. + exports = r_java_info + exports, + plugins = plugins, + exported_plugins = exported_plugins, + annotation_processor_additional_outputs = ( + annotation_processor_additional_outputs + ), + annotation_processor_additional_inputs = ( + annotation_processor_additional_inputs + ), + neverlink = neverlink, + constraints = constraints, + strict_deps = strict_deps, + java_toolchain = java_toolchain, + ) + return java_info + +def _compile( + ctx, + output_jar, + output_srcjar = None, + srcs = [], + resources = [], + javac_opts = [], + deps = [], + exports = [], + plugins = [], + exported_plugins = [], + annotation_processor_additional_outputs = [], + annotation_processor_additional_inputs = [], + neverlink = False, + constraints = [], + strict_deps = "Error", + java_toolchain = None): + """Compiles the Java and IDL sources for Android. + + Args: + ctx: The context. + output_jar: File. The artifact to place the compilation unit. + output_srcjar: File. The artifact to place the sources of the compilation + unit. Optional. + srcs: sequence of Files. A list of files and jars to be compiled. + resources: sequence of Files. Will be added to the output jar - see + java_library.resources. Optional. + javac_opts: sequence of strings. A list of the desired javac options. + Optional. + deps: sequence of JavaInfo providers. A list of dependencies. Optional. + exports: sequence of JavaInfo providers. A list of exports. Optional. + plugins: sequence of JavaPluginInfo providers. A list of plugins. Optional. + exported_plugins: sequence of JavaPluginInfo providers. A list of exported + plugins. Optional. + annotation_processor_additional_outputs: sequence of Files. A list of + files produced by an annotation processor. + annotation_processor_additional_inputs: sequence of Files. A list of + files consumed by an annotation processor. + resources: sequence of Files. Will be added to the output jar - see + java_library.resources. Optional. + neverlink: Bool. Makes the compiled unit a compile-time only dependency. + constraints: sequence of Strings. A list of constraints, to constrain the + target. Optional. By default []. + strict_deps: string. A string that specifies how to handle strict deps. + Possible values: 'OFF', 'ERROR','WARN' and 'DEFAULT'. For more details + see https://docs.bazel.build/versions/master/user-manual.html#flag--strict_java_deps. + By default 'ERROR'. + java_toolchain: The java_toolchain Target. + + Returns: + A JavaInfo provider representing the Java compilation. + """ + + # Split javac opts. + opts = [] + for opt in javac_opts: + opts.extend(opt.split(" ")) + + # Separate the sources *.java from *.srcjar. + source_files = [] + source_jars = [] + for src in srcs: + if src.path.endswith(".srcjar"): + source_jars.append(src) + else: + source_files.append(src) + + return java_common.compile( + ctx, + output = output_jar, + output_source_jar = output_srcjar, + source_files = source_files, + source_jars = source_jars, + resources = resources, + javac_opts = opts, + deps = deps, + exports = exports, + plugins = plugins, + exported_plugins = exported_plugins, + annotation_processor_additional_outputs = ( + annotation_processor_additional_outputs + ), + annotation_processor_additional_inputs = ( + annotation_processor_additional_inputs + ), + neverlink = neverlink, + strict_deps = strict_deps, + java_toolchain = java_toolchain[java_common.JavaToolchainInfo], + ) + +def _singlejar( + ctx, + inputs, + output, + mnemonic = "SingleJar", + progress_message = "Merge into a single jar.", + exclude_build_data = False, + java_toolchain = None): + args = ctx.actions.args() + args.add("--output") + args.add(output) + args.add("--compression") + args.add("--normalize") + if exclude_build_data: + args.add("--exclude_build_data") + args.add("--warn_duplicate_resources") + if inputs: + args.add("--sources") + args.add_all(inputs) + + args.use_param_file("@%s") + args.set_param_file_format("multiline") + + ctx.actions.run( + executable = java_toolchain[java_common.JavaToolchainInfo].single_jar, + arguments = [args], + inputs = inputs, + outputs = [output], + mnemonic = mnemonic, + progress_message = progress_message, + ) + +def _run( + ctx, + host_javabase, + jvm_flags = [], + **args): + """Run a java binary + + Args: + ctx: The context. + host_javabase: Target. The host_javabase. + jvm_flags: Additional arguments to the JVM itself. + **args: Additional arguments to pass to ctx.actions.run(). Some will get modified. + """ + + if type(ctx) != "ctx": + fail("Expected type ctx for argument ctx, got %s" % type(ctx)) + + if type(host_javabase) != "Target": + fail("Expected type Target for argument host_javabase, got %s" % type(host_javabase)) + + # Set reasonable max heap default. Required to prevent runaway memory usage. + # Can still be overridden by callers of this method. + jvm_flags = ["-Xmx4G", "-XX:+ExitOnOutOfMemoryError"] + jvm_flags + + # executable should be a File or a FilesToRunProvider + jar = args.get("executable") + if type(jar) == "FilesToRunProvider": + jar = jar.executable + elif type(jar) != "File": + fail("Expected type File or FilesToRunProvider for argument executable, got %s" % type(jar)) + + java_runtime = host_javabase[java_common.JavaRuntimeInfo] + args["executable"] = java_runtime.java_executable_exec_path + + # inputs can be a list or a depset of File + inputs = args.get("inputs", default = []) + if type(inputs) == type([]): + args["inputs"] = depset(direct = inputs + [jar], transitive = [java_runtime.files]) + else: # inputs is a depset + args["inputs"] = depset(direct = [jar], transitive = [inputs, java_runtime.files]) + + jar_args = ctx.actions.args() + jar_args.add("-jar", jar) + + args["arguments"] = jvm_flags + [jar_args] + args.get("arguments", default = []) + + ctx.actions.run(**args) + +java = struct( + compile = _compile, + compile_android = _compile_android, + resolve_package = _resolve_package, + resolve_package_from_label = _resolve_package_from_label, + root = _root, + invalid_java_package = _invalid_java_package, + run = _run, + singlejar = _singlejar, +) diff --git a/rules/migration_tag_DONOTUSE.bzl b/rules/migration_tag_DONOTUSE.bzl new file mode 100644 index 0000000..539aca9 --- /dev/null +++ b/rules/migration_tag_DONOTUSE.bzl @@ -0,0 +1,25 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel component for the Android Skylark Migration.""" + +_MIGRATION_TAG = "__ANDROID_RULES_MIGRATION__" +_TAG_ATTR = "tags" + +def add_migration_tag(attrs): + if _TAG_ATTR in attrs and attrs[_TAG_ATTR] != None: + attrs[_TAG_ATTR] = attrs[_TAG_ATTR] + [_MIGRATION_TAG] + else: + attrs[_TAG_ATTR] = [_MIGRATION_TAG] + return attrs diff --git a/rules/path.bzl b/rules/path.bzl new file mode 100644 index 0000000..28d18f1 --- /dev/null +++ b/rules/path.bzl @@ -0,0 +1,102 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel Path APIs for the Android rules.""" + +# TODO(djwhang): Get the path separator in a platform agnostic manner. +_PATH_SEP = "/" +_TEST_SRCDIR = "${TEST_SRCDIR}" + +def _is_absolute(path): + # TODO(djwhang): This is not cross platform safe. Windows absolute paths + # do not start with "//", rather "C:\". + return path.startswith(_PATH_SEP) + +def _split(path): + return path.split(_PATH_SEP) + +def _join(path_segments): + return _PATH_SEP.join(path_segments) + +def _normalize_path(path, posix = False): + return _PATH_SEP.join( + _normalize_path_fragments( + path.split(_PATH_SEP), + posix = posix, + ), + ) + +def _normalize_path_fragments(path_fragments, posix = False): + normalized_path_fragments = [] + for idx, fragment in enumerate(path_fragments): + if not fragment and idx > 0: + continue + if fragment == ".": + continue + if fragment == ".." and not posix: + if normalized_path_fragments: + last = normalized_path_fragments.pop() + if last == ".." or last == "": + normalized_path_fragments.append(last) + else: + continue + normalized_path_fragments.append(fragment) + if len(normalized_path_fragments) == 1 and not normalized_path_fragments[0]: + normalized_path_fragments.append("") + return normalized_path_fragments + +def _relative_path(path1, path2): + if not path1 or _is_absolute(path2): + return path2 + + path1_fragments = _normalize_path_fragments(_split(path1)) + path2_fragments = _normalize_path_fragments(_split(path2)) + path1_idx = len(path1_fragments) # index move backwards + path2_idx = -1 + for idx, fragment in enumerate(path2_fragments): + if fragment == "..": + path1_idx -= 1 + else: + path2_idx = idx + break + + relative_path_fragments = [] + if path1_idx >= 0: + relative_path_fragments.extend(path1_fragments[:path1_idx]) + if path2_idx >= 0: + relative_path_fragments.extend(path2_fragments[path2_idx:]) + return _join(_normalize_path_fragments(relative_path_fragments)) + +def _make_test_srcdir_path(ctx, *path_fragments): + """Creates a filepath relative to TEST_SRCDIR. + + Args: + ctx: Starlark context. + *path_fragments: Directories/file to join into a single path. + Returns: + A filepath that's spearated by the host's filepath separator. + """ + fragments = [_TEST_SRCDIR, ctx.workspace_name] + for path_fragment in path_fragments: + fragments += _normalize_path_fragments(_split(path_fragment)) + return _join(fragments) + +path = struct( + is_absolute = _is_absolute, + join = _join, + normalize = _normalize_path, + relative = _relative_path, + split = _split, + make_test_srcdir_path = _make_test_srcdir_path, +) diff --git a/rules/processing_pipeline.bzl b/rules/processing_pipeline.bzl new file mode 100644 index 0000000..6ba2ae7 --- /dev/null +++ b/rules/processing_pipeline.bzl @@ -0,0 +1,161 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Common implementation for processing pipelines.""" + +PROVIDERS = "providers" +VALIDATION_OUTPUTS = "validation_outputs" + +# TODO(djwhang): When a provider type can be retrieved from a Starlark provider +# ProviderInfo is necessary. Once this is possible, processor methods can have a +# uniform method signature foo(ctx, target_ctx) where we can pull the provider +# off the target_ctx using the provider type. +# +# Yes, this effectively leads to producing a build rule like system within a +# build rule, rather than resorting to rule based composition. +ProviderInfo = provider( + "Stores metadata about the actual Starlark provider returned.", + fields = dict( + name = "The type of the provider", + value = "The actual provider", + runfiles = "Runfiles to pass to the DefaultInfo provider", + ), +) + +_ProcessingPipelineInfo = provider( + "Stores functions that forms a rule's implementation.", + fields = dict( + processors = "Ordered dictionary of processing functions.", + finalize = "Function to form the final providers to propagate.", + ), +) + +def _make_processing_pipeline(processors = dict(), finalize = None): + """Creates the combined processing pipeline. + + Args: + processors: Ordered dictionary of processing functions. + finalize: Function to form the final providers to propagate. + + Returns: + A _ProcessingPipelineInfo provider. + """ + return _ProcessingPipelineInfo( + processors = processors, + finalize = finalize, + ) + +def _run(ctx, java_package, processing_pipeline): + """Runs the processing pipeline and populates the target context. + + Args: + ctx: The context. + java_package: The java package resolved from the target's path + or the custom_package attr. + processing_pipeline: The _ProcessingPipelineInfo provider for this target. + + Returns: + The output of the _ProcessingPipelineInfo.finalize function. + """ + target_ctx = dict( + java_package = java_package, + providers = [], + validation_outputs = [], + runfiles = ctx.runfiles(), + ) + + for execute in processing_pipeline.processors.values(): + info = execute(ctx, **target_ctx) + if info: + if info.name in target_ctx: + fail("%s provider already registered in target context" % info.name) + target_ctx[info.name] = info.value + target_ctx[PROVIDERS].extend(getattr(info.value, PROVIDERS, [])) + target_ctx[VALIDATION_OUTPUTS].extend(getattr(info.value, VALIDATION_OUTPUTS, [])) + if hasattr(info, "runfiles") and info.runfiles: + target_ctx["runfiles"] = target_ctx["runfiles"].merge(info.runfiles) + + return processing_pipeline.finalize(ctx, **target_ctx) + +def _prepend(processors, **new_processors): + """Prepends processors in a given processing pipeline. + + Args: + processors: The dictionary representing the processing pipeline. + **new_processors: The processors to add where the key represents the + name of the processor and value is the function pointer to the new + processor. + + Returns: + A dictionary which represents the new processing pipeline. + """ + updated_processors = dict() + for name, processor in new_processors.items(): + updated_processors[name] = processor + + for key in processors.keys(): + updated_processors[key] = processors[key] + + return updated_processors + +def _append(processors, **new_processors): + """Appends processors in a given processing pipeline. + + Args: + processors: The dictionary representing the processing pipeline. + **new_processors: The processors to append where the key represents the + name of the processor and value is the function pointer to the new + processor. + + Returns: + A dictionary which represents the new processing pipeline. + """ + updated_processors = dict(processors) + for name, processor in new_processors.items(): + updated_processors[name] = processor + + return updated_processors + +def _replace(processors, **new_processors): + """Replace processors in a given processing pipeline. + + Args: + processors: The dictionary representing the processing pipeline. + **new_processors: The processors to override where the key represents the + name of the processor and value is the function pointer to the new + processor. + + Returns: + A dictionary which represents the new processing pipeline. + """ + updated_processors = dict(processors) + for name, processor in new_processors.items(): + if name not in processors: + fail("Error, %s not found, unable to override." % name) + + # NOTE: Overwriting an existing value does not break iteration order. + # However, if a new processor is being added that needs to be injected + # between other processors, the processing pipeline dictionary will need + # to be recreated. + updated_processors[name] = processor + + return updated_processors + +processing_pipeline = struct( + make_processing_pipeline = _make_processing_pipeline, + run = _run, + prepend = _prepend, + append = _append, + replace = _replace, +) diff --git a/rules/proguard.bzl b/rules/proguard.bzl new file mode 100644 index 0000000..837d622 --- /dev/null +++ b/rules/proguard.bzl @@ -0,0 +1,110 @@ +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel Android Proguard library for the Android rules.""" + +_ProguardContextInfo = provider( + doc = "Contains data from processing Proguard specs.", + fields = dict( + proguard_configs = "The direct proguard configs", + transitive_proguard_configs = + "The proguard configs within the transitive closure of the target", + providers = "The list of all providers to propagate.", + ), +) + +def _validate_proguard_spec( + ctx, + out_validated_proguard_spec, + proguard_spec, + proguard_allowlister): + args = ctx.actions.args() + args.add("--path", proguard_spec) + args.add("--output", out_validated_proguard_spec) + + ctx.actions.run( + executable = proguard_allowlister, + arguments = [args], + inputs = [proguard_spec], + outputs = [out_validated_proguard_spec], + mnemonic = "ValidateProguard", + progress_message = ( + "Validating proguard configuration %s" % proguard_spec.short_path + ), + ) + +def _process( + ctx, + proguard_configs = [], + proguard_spec_providers = [], + proguard_allowlister = None): + """Processes Proguard Specs + + Args: + ctx: The context. + proguard_configs: sequence of Files. A list of proguard config files to be + processed. Optional. + proguard_spec_providers: sequence of ProguardSpecProvider providers. A + list of providers from the dependencies, exports, plugins, + exported_plugins, etc. Optional. + proguard_allowlister: The proguard_allowlister exeutable provider. + + Returns: + A _ProguardContextInfo provider. + """ + + # TODO(djwhang): Look to see if this can be just a validation action and the + # proguard_spec provided by the rule can be propagated. + validated_proguard_configs = [] + for proguard_spec in proguard_configs: + validated_proguard_spec = ctx.actions.declare_file( + "validated_proguard/%s/%s_valid" % + (ctx.label.name, proguard_spec.path), + ) + _validate_proguard_spec( + ctx, + validated_proguard_spec, + proguard_spec, + proguard_allowlister, + ) + validated_proguard_configs.append(validated_proguard_spec) + + transitive_validated_proguard_configs = [] + for info in proguard_spec_providers: + transitive_validated_proguard_configs.append(info.specs) + + transitive_proguard_configs = depset( + validated_proguard_configs, + transitive = transitive_validated_proguard_configs, + order = "preorder", + ) + return _ProguardContextInfo( + proguard_configs = proguard_configs, + transitive_proguard_configs = transitive_proguard_configs, + providers = [ + ProguardSpecProvider(transitive_proguard_configs), + # TODO(b/152659272): Remove this once the android_archive rule is + # able to process a transitive closure of deps to produce an aar. + AndroidProguardInfo(proguard_configs), + ], + ) + +proguard = struct( + process = _process, +) + +testing = struct( + validate_proguard_spec = _validate_proguard_spec, + ProguardContextInfo = _ProguardContextInfo, +) diff --git a/rules/providers.bzl b/rules/providers.bzl new file mode 100644 index 0000000..e7db209 --- /dev/null +++ b/rules/providers.bzl @@ -0,0 +1,149 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel providers for Android rules.""" + + + +AndroidAppsInfo = provider( + doc = "Provides information about app to install.", + fields = dict( + apps = "List of app provider artifacts.", + ), +) + + + + + + + + +AndroidJavaInfo = provider( + doc = "Provides outputs for the Android Java Compilation", + fields = dict( + aidl = "AndroidIdlInfo", + aide = "AndroidIdeInfo", + java = "JavaInfo", + ), +) + +AndroidFilteredJdepsInfo = provider( + doc = "Provides a filtered jdeps proto.", + fields = dict( + jdeps = "Filtered jdeps", + ), +) + + +StarlarkApkInfo = provider( + doc = "Provides APK outputs of a rule.", + fields = dict( + keystore = "Keystore used to sign the APK. Deprecated, prefer signing_keys.", + signing_keys = "List of keys used to sign the APK", + signing_lineage = "Optional sigining lineage file", + signed_apk = "Signed APK", + unsigned_apk = "Unsigned APK", + ), +) + +ResourcesNodeInfo = provider( + doc = "Provides information for building ResourceProcessorBusyBox flags", + fields = dict( + label = "A label, the target's label", + + # Assets related fields + assets = "A depset of files, assets files of the target", + assets_dir = "A string, the name of the assets directory", + assets_symbols = "A file, the merged assets", + compiled_assets = "A file, the compiled assets", + + # Resource related fields + resource_files = "A depset of files, resource files of the target", + compiled_resources = "A file, the compiled resources", + r_txt = "A file, the R.txt file", + manifest = "A file, the AndroidManifest.xml", + # TODO(ostonge): Add the manifest if it's exported, otherwise leave empty + exports_manifest = "Boolean, whether the manifest is exported", + ), +) + +StarlarkAndroidResourcesInfo = provider( + doc = "Provides information about direct and transitive resources", + fields = dict( + direct_resources_nodes = "Depset of ResourcesNodeInfo providers, can contain multiple providers due to exports", + transitive_resources_nodes = "Depset of transitive ResourcesNodeInfo providers, not including directs", + transitive_assets = "Depset of transitive assets files", + transitive_assets_symbols = "Depset of transitive merged assets", + transitive_compiled_assets = "Depset of transitive compiled assets", + direct_compiled_resources = "Depset of direct compiled_resources, can contain multiple files due to exports", + transitive_compiled_resources = "Depset of transitive compiled resources", + transitive_manifests = "Depset of transitive manifests", + transitive_r_txts = "Depset of transitive R.txt files", + transitive_resource_files = "Depset of transitive resource files", + packages_to_r_txts = "Map of packages to depset of r_txt files", + ), +) + +AndroidLintRulesInfo = provider( + doc = "Provides extra lint rules to use with AndroidLint.", + fields = dict( + lint_jar = "A file, a lint jar found in an aar.", + ), +) + +AndroidFeatureModuleInfo = provider( + doc = "Contains data required to build an Android feature split.", + fields = dict( + binary = "String, target of the underlying split android_binary target", + feature_name = "String, the name of the feature module. If unspecified, the target name will be used.", + fused = "Boolean, whether the split is \"fused\" for the system image and for pre-L devices.", + library = "String, target of the underlying split android_library target", + manifest = "Optional AndroidManifest.xml file to use for this feature.", + min_sdk_version = "String, the min SDK version for this feature.", + title_id = "String, resource identifier for the split title.", + title_lib = "String, target of the split title android_library.", + ), +) + + +Dex2OatApkInfo = provider( + doc = "Contains data about artifacts generated through host dex2oat.", + fields = dict( + signed_apk = "Signed APK", + oat_file = "Oat file generated through dex2oat.", + vdex_file = "Vdex file generated through dex2oat.", + art_file = "ART file generated through dex2oat.", + ), +) + +InstrumentedAppInfo = provider( + doc = "Contains data about an android_binary's instrumented android_binary.", + fields = dict( + android_ide_info = "AndroidIdeInfo provider from the instrumented android_binary.", + ), +) + +FailureInfo = provider( + fields = dict( + error = "Error message", + ), +) + +AndroidBundleInfo = provider( + doc = "Provides .aab outputs from a rule.", + fields = dict( + unsigned_aab = "File, the unsigned .aab", + ), +) diff --git a/rules/res_v3_dummy_AndroidManifest.xml b/rules/res_v3_dummy_AndroidManifest.xml new file mode 100644 index 0000000..8072ee0 --- /dev/null +++ b/rules/res_v3_dummy_AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/rules/res_v3_dummy_R.txt b/rules/res_v3_dummy_R.txt new file mode 100644 index 0000000..2b3d8b7 --- /dev/null +++ b/rules/res_v3_dummy_R.txt @@ -0,0 +1 @@ +int string fake 0x00000000 diff --git a/rules/resources.bzl b/rules/resources.bzl new file mode 100644 index 0000000..a08b6df --- /dev/null +++ b/rules/resources.bzl @@ -0,0 +1,1687 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Bazel Android Resources.""" + +load(":attrs.bzl", _attrs = "attrs") +load(":busybox.bzl", _busybox = "busybox") +load(":common.bzl", _common = "common") +load(":java.bzl", _java = "java") +load(":path.bzl", _path = "path") +load( + ":providers.bzl", + "ResourcesNodeInfo", + "StarlarkAndroidResourcesInfo", +) +load( + ":utils.bzl", + "utils", + _compilation_mode = "compilation_mode", + _log = "log", +) + +_RESOURCE_FOLDER_TYPES = [ + "anim", + "animator", + "color", + "drawable", + "font", + "interpolator", + "layout", + "menu", + "mipmap", + "navigation", + "values", + "xml", + "raw", + "transition", +] + +_RESOURCE_QUALIFIER_SEP = "-" + +_MANIFEST_MISSING_ERROR = ( + "In target %s, manifest attribute is required when resource_files or " + + "assets are defined." +) + +_ASSET_DEFINITION_ERROR = ( + "In target %s, the assets and assets_dir attributes should be either " + + "both empty or non-empty." +) + +_JAVA_PACKAGE_MISSING_ERROR = ( + "In target %s, a java package is required when stamping " + + "the manifest." +) + +_INCORRECT_RESOURCE_LAYOUT_ERROR = ( + "'%s' is not in the expected resource directory structure of " + + "/{%s}/" % (",").join(_RESOURCE_FOLDER_TYPES) +) + +# Keys for manifest_values +_VERSION_NAME = "versionName" +_VERSION_CODE = "versionCode" + +# Resources context attributes. +_ASSETS_PROVIDER = "assets_provider" +_DATA_BINDING_LAYOUT_INFO = "data_binding_layout_info" +_DEFINES_RESOURCES = "defines_resources" +_DIRECT_ANDROID_RESOURCES = "direct_android_resources" +_MERGED_MANIFEST = "merged_manifest" +_PROVIDERS = "providers" +_R_JAVA = "r_java" +_RESOURCES_APK = "resources_apk" +_VALIDATION_RESULTS = "validation_results" +_VALIDATION_OUTPUTS = "validation_outputs" +_RESOURCES_PROVIDER = "resources_provider" +_STARLARK_PROCESSED_MANIFEST = "starlark_processed_manifest" +_STARLARK_R_TXT = "starlark_r_txt" +_STARLARK_PROCESSED_RESOURCES = "starlark_processed_resources" + +_ResourcesProcessContextInfo = provider( + "Resources context object", + fields = { + _DEFINES_RESOURCES: "If local resources were defined.", + _DIRECT_ANDROID_RESOURCES: "Direct android resources.", + _MERGED_MANIFEST: "Merged manifest.", + _PROVIDERS: "The list of all providers to propagate.", + _R_JAVA: "JavaInfo for R.jar.", + _DATA_BINDING_LAYOUT_INFO: "Databinding layout info file.", + _RESOURCES_APK: "ResourcesApk.", + _VALIDATION_RESULTS: "List of validation results.", + _VALIDATION_OUTPUTS: "List of outputs given to OutputGroupInfo _validation group", + + # TODO(djwhang): The android_library aar generation requires direct + # access to providers. Remove once aar is its own rule. + _ASSETS_PROVIDER: "AndroidAssetsInfo provider.", + _RESOURCES_PROVIDER: "AndroidResourcesInfo provider.", + _STARLARK_PROCESSED_MANIFEST: "The processed manifest from the starlark resource processing pipeline.", + _STARLARK_R_TXT: "The R.txt from the starlark resource processing pipeline.", + _STARLARK_PROCESSED_RESOURCES: "The processed resources from the starlark processing pipeline.", + }, +) + +# Packaged resources context attributes. +_PACKAGED_FINAL_MANIFEST = "processed_manifest" +_PACKAGED_RESOURCE_APK = "resources_apk" +_PACKAGED_CLASS_JAR = "class_jar" +_PACKAGED_VALIDATION_RESULT = "validation_result" + +_ResourcesPackageContextInfo = provider( + "Packaged resources context object", + fields = { + _PACKAGED_FINAL_MANIFEST: "Final processed manifest.", + _PACKAGED_RESOURCE_APK: "ResourceApk.", + _PACKAGED_CLASS_JAR: "R class jar.", + _PACKAGED_VALIDATION_RESULT: "Validation result.", + _R_JAVA: "JavaInfo for R.jar", + _DATA_BINDING_LAYOUT_INFO: "Databinding layout info file.", + _PROVIDERS: "The list of all providers to propagate.", + }, +) + +def _generate_dummy_manifest( + ctx, + out_manifest = None, + java_package = None, + min_sdk_version = None): + content = """ +""" % java_package + + if min_sdk_version: + content = content + """ + """ % min_sdk_version + + content = content + """ + + +""" + + ctx.actions.write( + output = out_manifest, + content = content, + ) + +def _add_g3itr( + ctx, + manifest = None, + out_manifest = None, + xsltproc = None, + instrument_xslt = None): + """Adds Google3InstrumentationTestRunner instrumentation element to the manifest. + + Element is only added if the manifest contains an instrumentation element with + name "android.test.InstrumentationTestRunner". The added element's name attr is + "com.google.android.apps.common.testing.testrunner.Google3InstrumentationTestRunner". + + Args: + ctx: The context. + manifest: File. The AndroidManifest.xml file. + out_manifest: File. The transformed AndroidManifest.xml. + xsltproc: FilesToRunProvider. The xsltproc executable or + FilesToRunProvider. + instrument_xslt: File. The add_g3itr.xslt file describing the xslt + transformation to apply. + """ + args = ctx.actions.args() + args.add("--nonet") + args.add("--novalid") + args.add("-o", out_manifest) + args.add(instrument_xslt) + args.add(manifest) + + ctx.actions.run( + executable = xsltproc, + arguments = [args], + inputs = [manifest, instrument_xslt], + outputs = [out_manifest], + mnemonic = "AddG3ITRStarlark", + progress_message = "Adding G3ITR to test manifest for %s" % ctx.label, + ) + +def _get_legacy_mergee_manifests(resources_infos): + all_dependencies = depset( + transitive = [ + ri.direct_android_resources + for ri in resources_infos + ] + [ + ri.transitive_android_resources + for ri in resources_infos + ], + ) + + mergee_manifests = [] + for dep in all_dependencies.to_list(): + if dep.to_provider.manifest.exports_manifest: + mergee_manifests.append(dep.to_provider.manifest.manifest) + + return depset(mergee_manifests) + +def _legacy_mergee_manifest(manifest): + sort_key = manifest.short_path + "#" + return sort_key + "--mergee=" + manifest.path + +def _legacy_merge_manifests( + ctx, + out_merged_manifest = None, + manifest = None, + mergee_manifests = None, + legacy_merger = None): + """Merges manifests with the legacy manifest merger." + + This should not be called with empty mergee_manifests. + + Args: + ctx: The context. + out_merged_manifest: File. The merged AndroidManifest.xml. + manifest: File. The AndroidManifest.xml. + mergee_manifests: A sequence of Files. All transitive manifests to be merged. + legacy_merger: A FilesToRunProvider. The legacy manifest merger executable. + """ + args = ctx.actions.args() + args.use_param_file("%s", use_always = True) + args.set_param_file_format("multiline") + args.add("--merger=%s" % manifest.path) + args.add("--exclude_permission=all") + args.add("--output=%s" % out_merged_manifest.path) + + manifest_params = ctx.actions.declare_file(ctx.label.name + "/legacy_merger.params") + manifest_args = ctx.actions.args() + manifest_args.use_param_file("%s", use_always = True) + manifest_args.set_param_file_format("multiline") + manifest_args.add_joined(mergee_manifests, map_each = _legacy_mergee_manifest, join_with = "\n") + ctx.actions.run_shell( + command = """ +# Sorts the mergee manifests by path and combines with other busybox args. +set -e +SORTED=`sort $1 | sed 's/^.*#//'` +cat $2 > $3 +echo "$SORTED" >> $3 +""", + arguments = [manifest_args, args, manifest_params.path], + outputs = [manifest_params], + ) + args = ctx.actions.args() + args.add(manifest_params, format = "--flagfile=%s") + + ctx.actions.run( + executable = legacy_merger, + arguments = [args], + inputs = depset([manifest, manifest_params], transitive = [mergee_manifests]), + outputs = [out_merged_manifest], + mnemonic = "StarlarkLegacyAndroidManifestMerger", + progress_message = "Merging Android Manifests", + ) + +def _make_databinding_outputs( + ctx, + resource_files): + """Helper method to create arguments for the process_databinding busybox tool. + + Declares databinding-processed resource files that are generated by the + PROCESS_DATABINDING busybox tool, which must be declared underneath an output + resources directory and namespaced by their paths. The busybox takes the + output directory exec path and generates the underlying resource files. + + Args: + ctx: The context. + resource_files: List of Files. The android resource files to be processed by + _process_databinding. + + Returns: + A tuple containing the list of declared databinding processed resource files and the + output resource directory path expected by the busybox. The path is a full path. + """ + + # TODO(b/160907203): Clean up databinding_rel_path. We capitalize "Databinding" here to avoid + # conflicting with native artifact file names. This is changed back to "databinding" during + # process_starlark so that compiled resources exactly match those of the native resource + # processing pipeline. Even a single character mismatch in the file names causes selected + # resources to differ in the final APK. + databinding_rel_path = _path.join(["Databinding-processed-resources", ctx.label.name]) + databinding_processed_resources = [ + ctx.actions.declare_file(_path.join([databinding_rel_path, f.path])) + for f in resource_files + ] + databinding_resources_dirname = _path.join([ + ctx.bin_dir.path, + ctx.label.package, + databinding_rel_path, + ]) + return databinding_processed_resources, databinding_resources_dirname + +def _fix_databinding_compiled_resources( + ctx, + out_compiled_resources = None, + compiled_resources = None, + zip_tool = None): + """Fix compiled resources to match those produced by the native pipeline. + + Changes "Databinding" to "databinding" in each compiled resource .flat file name and header. + + Args: + ctx: The context. + out_compiled_resources: File. The modified compiled_resources output. + compiled_resources: File. The compiled_resources zip. + """ + ctx.actions.run_shell( + outputs = [out_compiled_resources], + inputs = [compiled_resources], + tools = [zip_tool], + arguments = [compiled_resources.path, out_compiled_resources.path, zip_tool.executable.path], + command = """#!/bin/bash +set -e + +IN_DIR=$(mktemp -d) +OUT_DIR=$(mktemp -d) +CUR_PWD=$(pwd) + +if zipinfo -t "$1"; then + ORDERED_LIST=`(unzip -l "$1" | sed -e '1,3d' | head -n -2 | tr -s " " | cut -d " " -f5)` + + unzip -q "$1" -d "$IN_DIR" + + # Iterate through the ordered list, change "Databinding" to "databinding" in the file header + # and file name and zip the files with the right comment + for FILE in $ORDERED_LIST; do + cd "$IN_DIR" + if [ -f "$FILE" ]; then + sed -i 's/Databinding\\-processed\\-resources/databinding\\-processed\\-resources/g' "$FILE" + NEW_NAME=`echo "$FILE" | sed 's/Databinding\\-processed\\-resources/databinding\\-processed\\-resources/g' | sed 's#'"$IN_DIR"'/##g'` + mkdir -p `dirname "$OUT_DIR/$NEW_NAME"` && touch "$OUT_DIR/$NEW_NAME" + cp -p "$FILE" "$OUT_DIR/$NEW_NAME" + + PATH_SEGMENTS=(`echo ${FILE} | tr '/' ' '`) + BASE_PATH_SEGMENT="${PATH_SEGMENTS[0]}" + COMMENT= + if [ "${BASE_PATH_SEGMENT}" == "generated" ]; then + COMMENT="generated" + elif [ "${BASE_PATH_SEGMENT}" == "default" ]; then + COMMENT="default" + fi + + cd "$OUT_DIR" + "$CUR_PWD/$3" -jt -X -0 -q -r -c "$CUR_PWD/$2" $NEW_NAME < 127. + Shared libraries are also assigned monotonically increasing IDs in + [2,126], so care should be taken that there is room at the lower end. + compilation_mode: String. A string that represents compilation mode. The + list of expected values are as follows: dbg, fastbuild, opt. + shrink_resources: Tristate. Whether resource shrinking is enabled by the rule. + use_android_resource_shrinking: Bool. Flag that controls the default value for + shrink_resources if the tristate value is auto (-1). + use_android_resource_cycle_shrinking: Bool. Flag that enables more shrinking of + code and resources by instructing AAPT2 to emit conditional Proguard keep rules. + use_legacy_manifest_merger: A boolean. Whether to use the legacy manifest merger + instead of the android manifest merger. + should_throw_on_conflict: A boolean. Determines whether an error should be thrown + when a resource conflict occurs. + enable_data_binding: boolean. If true, processesing the data binding + expressions in layout resources included through the resource_files + parameter is enabled. Without this setting, data binding expressions + produce build failures. + enable_manifest_merging: boolean. If true, manifest merging will be performed. + aapt: FilesToRunProvider. The aapt executable or FilesToRunProvider. + android_jar: File. The Android jar. + legacy_merger: FilesToRunProvider. The legacy manifest merger executable. + xsltproc: FilesToRunProvider. The xsltproc executable or + FilesToRunProvider. + instrument_xslt: File. The add_g3itr.xslt file describing the xslt + transformation to apply. + busybox: FilesToRunProvider. The ResourceBusyBox executable or + FilesToRunprovider + host_javabase: A Target. The host javabase. + + Returns: + A ResourcesPackageContextInfo containing packaged resource artifacts and + providers. + """ + _validate_resources(resource_files) + + packaged_resources_ctx = { + _PROVIDERS: [], + } + + g3itr_manifest = manifest + + if xsltproc or instrument_xslt: + g3itr_manifest = ctx.actions.declare_file( + "_migrated/" + ctx.label.name + "add_g3itr/AndroidManifest.xml", + ) + _add_g3itr( + ctx, + out_manifest = g3itr_manifest, + manifest = manifest, + xsltproc = xsltproc, + instrument_xslt = instrument_xslt, + ) + + direct_resources_nodes = [] + transitive_resources_nodes = [] + transitive_assets = [] + transitive_assets_symbols = [] + transitive_compiled_assets = [] + transitive_resource_files = [] + transitive_compiled_resources = [] + transitive_manifests = [] + transitive_r_txts = [] + packages_to_r_txts_depset = dict() + for dep in utils.collect_providers(StarlarkAndroidResourcesInfo, deps): + direct_resources_nodes.append(dep.direct_resources_nodes) + transitive_resources_nodes.append(dep.transitive_resources_nodes) + transitive_assets.append(dep.transitive_assets) + transitive_assets_symbols.append(dep.transitive_assets_symbols) + transitive_compiled_assets.append(dep.transitive_compiled_assets) + transitive_resource_files.append(dep.transitive_resource_files) + transitive_compiled_resources.append(dep.transitive_compiled_resources) + transitive_manifests.append(dep.transitive_manifests) + transitive_r_txts.append(dep.transitive_r_txts) + for pkg, r_txts in dep.packages_to_r_txts.items(): + packages_to_r_txts_depset.setdefault(pkg, []).append(r_txts) + + mergee_manifests = depset([ + node_info.manifest + for node_info in depset(transitive = transitive_resources_nodes + direct_resources_nodes).to_list() + if node_info.exports_manifest + ]) + + # TODO(b/156763506): Add analysis tests to verify logic around when manifest merging is configured. + # TODO(b/154153771): Run the android merger if mergee_manifests or manifest values are present. + merged_manifest = g3itr_manifest + if enable_manifest_merging and (manifest_values or mergee_manifests): + if use_legacy_manifest_merger: + # Legacy manifest merger only runs if mergee manifests are present + if mergee_manifests: + merged_manifest = ctx.actions.declare_file( + "_migrated/_merged/" + ctx.label.name + "/AndroidManifest.xml", + ) + _legacy_merge_manifests( + ctx, + out_merged_manifest = merged_manifest, + manifest = g3itr_manifest, + mergee_manifests = mergee_manifests, + legacy_merger = legacy_merger, + ) + else: + merged_manifest = ctx.actions.declare_file( + "_migrated/_merged/" + ctx.label.name + "/AndroidManifest.xml", + ) + _busybox.merge_manifests( + ctx, + out_file = merged_manifest, + out_log_file = ctx.actions.declare_file( + "_migrated/_merged/" + ctx.label.name + "/manifest_merger_log.txt", + ), + manifest = g3itr_manifest, + mergee_manifests = mergee_manifests, + manifest_values = manifest_values, + merge_type = "APPLICATION", + java_package = java_package, + busybox = busybox, + host_javabase = host_javabase, + ) + + processed_resources = resource_files + data_binding_layout_info = None + if enable_data_binding: + data_binding_layout_info = ctx.actions.declare_file("_migrated/" + ctx.label.name + "/layout-info.zip") + processed_resources, resources_dirname = _make_databinding_outputs( + ctx, + resource_files, + ) + _busybox.process_databinding( + ctx, + out_databinding_info = data_binding_layout_info, + out_databinding_processed_resources = processed_resources, + databinding_resources_dirname = resources_dirname, + resource_files = resource_files, + java_package = java_package, + busybox = busybox, + host_javabase = host_javabase, + ) + + resource_apk = ctx.actions.declare_file(ctx.label.name + "_migrated/.ap_") + r_java = ctx.actions.declare_file("_migrated/" + ctx.label.name + ".srcjar") + r_txt = ctx.actions.declare_file(ctx.label.name + "_migrated/_symbols/R.txt") + processed_manifest = ctx.actions.declare_file(ctx.label.name + "_migrated/_processed_manifest/AndroidManifest.xml") + proguard_cfg = ctx.actions.declare_file( + "_migrated/proguard/%s/_%s_proguard.cfg" % (ctx.label.name, ctx.label.name), + ) + main_dex_proguard_cfg = ctx.actions.declare_file( + "_migrated/proguard/%s/main_dex_%s_proguard.cfg" % + (ctx.label.name, ctx.label.name), + ) + resource_files_zip = ctx.actions.declare_file( + "_migrated/" + ctx.label.name + "_files/resource_files.zip", + ) + _busybox.package( + ctx, + out_file = resource_apk, + out_r_src_jar = r_java, + out_r_txt = r_txt, + out_symbols = ctx.actions.declare_file("_migrated/" + ctx.label.name + "_symbols/merged.bin"), + out_manifest = processed_manifest, + out_proguard_cfg = proguard_cfg, + out_main_dex_proguard_cfg = main_dex_proguard_cfg, + out_resource_files_zip = resource_files_zip, + application_id = manifest_values.get("applicationId", None), + package_id = package_id, + manifest = merged_manifest, + assets = assets, + assets_dir = assets_dir, + resource_files = processed_resources, + direct_resources_nodes = + depset(transitive = direct_resources_nodes, order = "preorder"), + transitive_resources_nodes = + depset(transitive = transitive_resources_nodes, order = "preorder"), + transitive_assets = transitive_assets, + transitive_compiled_assets = transitive_compiled_assets, + transitive_resource_files = transitive_resource_files, + transitive_compiled_resources = transitive_compiled_resources, + transitive_manifests = transitive_manifests, + transitive_r_txts = transitive_r_txts, + resource_configs = resource_configs, + densities = densities, + nocompress_extensions = nocompress_extensions, + java_package = java_package, + version_name = manifest_values[_VERSION_NAME] if _VERSION_NAME in manifest_values else None, + version_code = manifest_values[_VERSION_CODE] if _VERSION_CODE in manifest_values else None, + android_jar = android_jar, + aapt = aapt, + busybox = busybox, + host_javabase = host_javabase, + debug = compilation_mode != _compilation_mode.OPT, + should_throw_on_conflict = should_throw_on_conflict, + ) + packaged_resources_ctx[_PACKAGED_FINAL_MANIFEST] = processed_manifest + packaged_resources_ctx[_PACKAGED_RESOURCE_APK] = resource_apk + packaged_resources_ctx[_PACKAGED_VALIDATION_RESULT] = resource_files_zip + + resource_shrinking_enabled = _is_resource_shrinking_enabled( + shrink_resources, + use_android_resource_shrinking, + ) + shrink_resource_cycles = _should_shrink_resource_cycles( + use_android_resource_cycle_shrinking, + resource_shrinking_enabled, + ) + + # Fix class jar name because some tests depend on {label_name}_resources.jar being the suffix of + # the path, with _RESOURCES_DO_NOT_USE removed from the label name. + _RESOURCES_SUFFIX = "_RESOURCES_DO_NOT_USE" + class_jar_name = ctx.label.name + "_migrated/_resources.jar" + if ctx.label.name.endswith(_RESOURCES_SUFFIX): + label_name = ctx.label.name[:-len(_RESOURCES_SUFFIX)] + class_jar_name = ctx.label.name + "_migrated/" + label_name + "_resources.jar" + + class_jar = ctx.actions.declare_file(class_jar_name) + _busybox.generate_binary_r( + ctx, + out_class_jar = class_jar, + r_txt = r_txt, + manifest = processed_manifest, + package_for_r = java_package, + final_fields = not shrink_resource_cycles and not instruments, + resources_nodes = depset(transitive = direct_resources_nodes + transitive_resources_nodes), + transitive_r_txts = transitive_r_txts, + transitive_manifests = transitive_manifests, + busybox = busybox, + host_javabase = host_javabase, + ) + packaged_resources_ctx[_PACKAGED_CLASS_JAR] = class_jar + + java_info = JavaInfo( + output_jar = class_jar, + compile_jar = class_jar, + source_jar = r_java, + ) + + packaged_resources_ctx[_R_JAVA] = java_info + packaged_resources_ctx[_DATA_BINDING_LAYOUT_INFO] = data_binding_layout_info + + packages_to_r_txts_depset.setdefault(java_package, []).append(depset([r_txt])) + + packages_to_r_txts = dict() + for pkg, depsets in packages_to_r_txts_depset.items(): + packages_to_r_txts[pkg] = depset(transitive = depsets) + + # Adding empty depsets to unused fields of StarlarkAndroidResourcesInfo. + # Some root targets may depends on other root targets and try to access those fields. + packaged_resources_ctx[_PROVIDERS].append(StarlarkAndroidResourcesInfo( + direct_resources_nodes = depset(), + transitive_resources_nodes = depset(), + transitive_assets = depset(), + transitive_assets_symbols = depset(), + transitive_compiled_assets = depset(), + transitive_resource_files = depset(), + direct_compiled_resources = depset(), + transitive_compiled_resources = depset(), + transitive_manifests = depset(), + transitive_r_txts = depset(), + packages_to_r_txts = packages_to_r_txts, + )) + + packaged_resources_ctx[_PROVIDERS].append(AndroidApplicationResourceInfo( + resource_apk = resource_apk, + resource_java_src_jar = r_java, + resource_java_class_jar = class_jar, + manifest = processed_manifest, + resource_proguard_config = proguard_cfg, + main_dex_proguard_config = main_dex_proguard_cfg, + r_txt = r_txt, + resources_zip = resource_files_zip, + databinding_info = data_binding_layout_info, + )) + return _ResourcesPackageContextInfo(**packaged_resources_ctx) + +def _liteparse(ctx, out_r_pb, resource_files, android_kit): + """Creates an R.pb which contains the resource ids gotten from a light parse. + + Args: + ctx: The context. + out_r_pb: File. The R.pb output file. + resource_files: List of Files. The list of resource files. + android_kit: FilesToRunProvider. The Android Kit executable or + FilesToRunProvider. + """ + args = ctx.actions.args() + args.use_param_file(param_file_arg = "--flagfile=%s", use_always = True) + args.set_param_file_format("multiline") + args.add_joined("--res_files", resource_files, join_with = ",") + args.add("--out", out_r_pb) + + ctx.actions.run( + executable = android_kit, + arguments = ["liteparse", args], + inputs = resource_files, + outputs = [out_r_pb], + mnemonic = "ResLiteParse", + progress_message = "Lite parse Android Resources %s" % ctx.label, + ) + +def _fastr(ctx, r_pbs, package, manifest, android_kit): + """Create R.srcjar from the given R.pb files in the transitive closure. + + Args: + ctx: The context. + r_pbs: Transitive set of resource pbs. + package: The package name of the compile-time R.java. + manifest: File. The AndroidManifest.xml file. + android_kit: FilesToRunProvider. The Android Kit executable or + FilesToRunProvider. + + Returns: + The output R source jar artifact. + """ + inputs = r_pbs + r_srcjar = ctx.actions.declare_file(ctx.label.name + "/resources/R-fastr.srcjar") + args = ctx.actions.args() + args.use_param_file(param_file_arg = "--flagfile=%s", use_always = True) + args.set_param_file_format("multiline") + args.add("-rJavaOutput", r_srcjar) + if package: + args.add("-packageForR", package) + else: + args.add("-manifest", manifest) + inputs = depset([manifest], transitive = [inputs]) + args.add_joined("-resourcePbs", r_pbs, join_with = ",") + + ctx.actions.run( + executable = android_kit, + arguments = ["rstub", args], + inputs = inputs, + outputs = [r_srcjar], + mnemonic = "CompileTimeR", + progress_message = "Generating compile-time R %s" % r_srcjar.short_path, + ) + return r_srcjar + +def _compile( + ctx, + out_compiled_resources = None, + out_r_pb = None, + resource_files = [], + aapt = None, + android_kit = None, + busybox = None, + host_javabase = None): + """Compile Android Resources processing pipeline. + + Args: + ctx: The context. + out_compiled_resources: File. The compiled resources output file. + out_r_pb: File. The R.pb output file. + resource_files: A list of Files. The resource files can be directories. + aapt: FilesToRunProvider. The aapt executable or FilesToRunProvider. + android_kit: FilesToRunProvider. The android_kit executable or + FilesToRunProvider. + busybox: FilesToRunProvider. The ResourceBusyBox executable or + FilesToRunprovider + host_javabase: A Target. The host javabase. + """ + _liteparse(ctx, out_r_pb, resource_files, android_kit) + _busybox.compile( + ctx, + out_file = out_compiled_resources, + resource_files = resource_files, + aapt = aapt, + busybox = busybox, + host_javabase = host_javabase, + ) + +def _make_aar( + ctx, + assets = [], + assets_dir = None, + resource_files = [], + class_jar = None, + r_txt = None, + manifest = None, + proguard_specs = [], + busybox = None, + host_javabase = None): + """Generate an android archive file. + + Args: + ctx: The context. + assets: sequence of Files. A list of Android assets files to be processed. + assets_dir: String. The name of the assets directory. + resource_files: A list of Files. The resource files. + class_jar: File. The class jar file. + r_txt: File. The resource IDs outputted by linking resources in text. + manifest: File. The primary AndroidManifest.xml. + proguard_specs: List of File. The proguard spec files. + busybox: FilesToRunProvider. The ResourceBusyBox executable or + FilesToRunprovider + host_javabase: A Target. The host javabase. + + Returns: + The output aar artifact. + """ + aar = ctx.actions.declare_file(ctx.label.name + ".aar") + _busybox.make_aar( + ctx, + out_aar = aar, + assets = assets, + assets_dir = assets_dir, + resource_files = resource_files, + class_jar = class_jar, + r_txt = r_txt, + manifest = manifest, + proguard_specs = proguard_specs, + busybox = busybox, + host_javabase = host_javabase, + ) + return aar + +def _validate(ctx, manifest, defined_assets, defined_assets_dir): + if ((defined_assets and not defined_assets_dir) or + (not defined_assets and defined_assets_dir)): + _log.error(_ASSET_DEFINITION_ERROR % ctx.label) + + if not manifest: + _log.error(_MANIFEST_MISSING_ERROR % ctx.label) + +def _make_direct_assets_transitive(assets_info): + return AndroidAssetsInfo( + assets_info.label, + assets_info.validation_result, + depset([]), # direct_parsed_assets + depset( + transitive = [ + assets_info.direct_parsed_assets, + assets_info.transitive_parsed_assets, + ], + order = "preorder", + ), + assets_info.assets, + assets_info.symbols, + assets_info.compiled_symbols, + ) + +def _make_direct_resources_transitive(resources_info): + return AndroidResourcesInfo( + resources_info.label, + resources_info.manifest, + resources_info.compiletime_r_txt, + # NB: the ordering of "direct" and "transitive" is inconsistent with that used for + # AndroidAssetsInfo. + depset( + transitive = [ + # Ordering is inconsistent here too: + # https://github.com/bazelbuild/bazel/blob/82c7f48b4628ebbec18123afdbed701bbaa605e2/src/tools/android/java/com/google/devtools/build/android/Aapt2ResourcePackagingAction.java#L158 + resources_info.transitive_android_resources, + resources_info.direct_android_resources, + ], + order = "preorder", + ), + depset([]), # direct_android_resources + resources_info.transitive_resources, + resources_info.transitive_manifests, + resources_info.transitive_aapt2_r_txt, + resources_info.transitive_symbols_bin, + resources_info.transitive_compiled_symbols, + resources_info.transitive_static_lib, + resources_info.transitive_r_txt, + validation_artifacts = resources_info.validation_artifacts, + ) + +def _export_assets(assets_info, exports): + all_providers = [assets_info] + utils.collect_providers(AndroidAssetsInfo, exports) + return AndroidAssetsInfo( + assets_info.label, + assets_info.validation_result, + direct_parsed_assets = utils.join_depsets(all_providers, "direct_parsed_assets", order = "preorder"), + transitive_parsed_assets = utils.join_depsets(all_providers, "transitive_parsed_assets", order = "preorder"), + transitive_assets = utils.join_depsets(all_providers, "assets", order = "preorder"), + transitive_symbols = utils.join_depsets(all_providers, "symbols", order = "preorder"), + transitive_compiled_symbols = utils.join_depsets(all_providers, "compiled_symbols", order = "preorder"), + ) + +def _export_resources(resources_info, exports): + all_providers = [resources_info] + utils.collect_providers(AndroidResourcesInfo, exports) + return AndroidResourcesInfo( + resources_info.label, + resources_info.manifest, + resources_info.compiletime_r_txt, + **{attr: utils.join_depsets(all_providers, attr, order = "preorder") for attr in [ + "transitive_android_resources", + "direct_android_resources", + "transitive_resources", + "transitive_manifests", + "transitive_aapt2_r_txt", + "transitive_symbols_bin", + "transitive_compiled_symbols", + "transitive_static_lib", + "transitive_r_txt", + "validation_artifacts", + ]} + ) + +def _validate_resources(resource_files = None): + for resource_file in resource_files: + path_segments = resource_file.path.split("/") + if len(path_segments) < 3: + fail(_INCORRECT_RESOURCE_LAYOUT_ERROR % resource_file) + + # Check the resource directory type if the resource file is not a Fileset. + if not resource_file.is_directory: + # The resource directory is presumed to be the second directory from the end. + # Resource directories can have multiple qualifiers, each one separated with a dash. + res_type = path_segments[-2].partition(_RESOURCE_QUALIFIER_SEP)[0] + if res_type not in _RESOURCE_FOLDER_TYPES: + fail(_INCORRECT_RESOURCE_LAYOUT_ERROR % resource_file) + +def _process_starlark( + ctx, + java_package = None, + manifest = None, + defined_assets = False, + assets = None, + defined_assets_dir = False, + assets_dir = None, + exports_manifest = False, + stamp_manifest = True, + deps = [], + exports = [], + resource_files = None, + neverlink = False, + enable_data_binding = False, + propagate_resources = True, + fix_resource_transitivity = False, + aapt = None, + android_jar = None, + android_kit = None, + busybox = None, + java_toolchain = None, + host_javabase = None, + instrument_xslt = None, + xsltproc = None, + zip_tool = None): + """Processes Android Resources. + + Args: + ctx: The rules context. + java_package: string. Java package for which java sources will be + generated. By default the package is inferred from the directory where + the BUILD file containing the rule is. + manifest: File. The AndroidManifest.xml file. + defined_assets: Bool. Signifies that the assets attribute was set, even + if the value is an empty list. + assets: sequence of Files. A list of Android assets files to be processed. + defined_assets_dir: Bool. Signifies that the assets dir attribute was set, + even if the value is an empty string. + assets_dir: String. The name of the assets directory. + exports_manifest: boolean. Whether to export manifest entries to the + android_binary targets that depend on this target. + NOTE: "uses-permissions" attributes are never exported. + stamp_manifest: boolean. Whether to stamp the manifest with the java + package of the target. If True, java_package needs to be passed to + the function. + deps: sequence of Targets. The list of other libraries targets to link + against. + exports: sequence of Targets. The closure of all rules reached via exports + attributes are considered direct dependencies of any rule that directly + depends on the target with exports. The exports are not direct deps of + the rule they belong to (TODO(b/144134042): make this so). + resource_files: sequence of Files. A list of Android resource files to be + processed. + neverlink: boolean. Only use this library for compilation and not runtime. + The outputs of a rule marked as neverlink will not be used in .apk + creation. Useful if the library will be provided by the runtime + environment during execution. + enable_data_binding: boolean. If true, processesing the data binding + expressions in layout resources included through the resource_files + parameter is enabled. Without this setting, data binding expressions + produce build failures. + propagate_resources: boolean. If false, the target will no longer propagate + providers required for Android Resource processing/packaging. But will + continue to propagate others (AndroidLibraryResourceClassJarProvider). + fix_resource_transitivity: Whether to ensure that transitive resources are + correctly marked as transitive. + aapt: FilesToRunProvider. The aapt executable or FilesToRunProvider. + android_jar: File. The android Jar. + android_kit: FilesToRunProvider. The android_kit executable or + FilesToRunProvider. + busybox: FilesToRunProvider. The ResourceBusyBox executable or + FilesToRunprovider + java_toolchain: The java_toolchain Target. + host_javabase: Target. The host javabase. + instrument_xslt: File. The xslt transform to apply g3itr. + xsltproc: FilesToRunProvider. The xsltproc executable or FilesToRunProvider. + zip_tool: FilesToRunProvider. The zip tool executable or FilesToRunProvider. + + Returns: + A dict containing _ResourcesProcessContextInfo provider fields. + """ + if (xsltproc and not instrument_xslt) or (not xsltproc and instrument_xslt): + fail( + "Error, both instrument_xslt and xsltproc need to be " + + "specified or not, got:\nxlstproc = %s\ninstrument_xslt = %s" % + (xsltproc, instrument_xslt), + ) + + _validate_resources(resource_files) + + defines_resources = bool( + manifest or + resource_files or + defined_assets or + defined_assets_dir or + exports_manifest, + ) + + # TODO(djwhang): Clean up the difference between neverlink the attribute used + # by Java compilation and resources neverlink. + resources_neverlink = ( + neverlink and ( + defines_resources or + ctx.fragments.android.fixed_resource_neverlinking + ) + ) + + resources_ctx = { + _RESOURCES_APK: None, + _PROVIDERS: [], + # TODO(b/156530953): Move the validation result to the validation_outputs list when we are + # done rolling out Starlark resources processing + _VALIDATION_RESULTS: [], + _DEFINES_RESOURCES: defines_resources, + _R_JAVA: None, + _DATA_BINDING_LAYOUT_INFO: None, + _MERGED_MANIFEST: None, + _STARLARK_PROCESSED_MANIFEST: None, + _STARLARK_R_TXT: None, + _STARLARK_PROCESSED_RESOURCES: [], + } + + if resource_files and not manifest: + _log.error(_MANIFEST_MISSING_ERROR % ctx.label) + + if stamp_manifest and not java_package: + _log.error(_JAVA_PACKAGE_MISSING_ERROR % ctx.label) + + direct_resources_nodes = [] + transitive_resources_nodes = [] + transitive_assets = [] + transitive_assets_symbols = [] + transitive_compiled_assets = [] + direct_compiled_resources = [] + transitive_compiled_resources = [] + transitive_resources_files = [] + transitive_manifests = [] + transitive_r_txts = [] + packages_to_r_txts_depset = dict() + + for dep in utils.collect_providers(StarlarkAndroidResourcesInfo, deps): + direct_resources_nodes.append(dep.direct_resources_nodes) + transitive_resources_nodes.append(dep.transitive_resources_nodes) + transitive_assets.append(dep.transitive_assets) + transitive_assets_symbols.append(dep.transitive_assets_symbols) + transitive_compiled_assets.append(dep.transitive_compiled_assets) + direct_compiled_resources.append(dep.direct_compiled_resources) + transitive_compiled_resources.append(dep.transitive_compiled_resources) + transitive_resources_files.append(dep.transitive_resource_files) + transitive_manifests.append(dep.transitive_manifests) + transitive_r_txts.append(dep.transitive_r_txts) + for pkg, r_txts in dep.packages_to_r_txts.items(): + packages_to_r_txts_depset.setdefault(pkg, []).append(r_txts) + + exports_direct_resources_nodes = [] + exports_transitive_resources_nodes = [] + exports_transitive_assets = [] + exports_transitive_assets_symbols = [] + exports_transitive_compiled_assets = [] + exports_direct_compiled_resources = [] + exports_transitive_compiled_resources = [] + exports_transitive_resources_files = [] + exports_transitive_manifests = [] + exports_transitive_r_txts = [] + for dep in utils.collect_providers(StarlarkAndroidResourcesInfo, exports): + exports_direct_resources_nodes.append(dep.direct_resources_nodes) + exports_transitive_resources_nodes.append(dep.transitive_resources_nodes) + exports_transitive_assets.append(dep.transitive_assets) + exports_transitive_assets_symbols.append(dep.transitive_assets_symbols) + exports_transitive_compiled_assets.append(dep.transitive_compiled_assets) + exports_direct_compiled_resources.append(dep.direct_compiled_resources) + exports_transitive_compiled_resources.append(dep.transitive_compiled_resources) + exports_transitive_resources_files.append(dep.transitive_resource_files) + exports_transitive_manifests.append(dep.transitive_manifests) + exports_transitive_r_txts.append(dep.transitive_r_txts) + for pkg, r_txts in dep.packages_to_r_txts.items(): + packages_to_r_txts_depset.setdefault(pkg, []).append(r_txts) + + # TODO(b/144134042): Don't merge exports; exports are not deps. + direct_resources_nodes.extend(exports_direct_resources_nodes) + transitive_resources_nodes.extend(exports_transitive_resources_nodes) + transitive_assets.extend(exports_transitive_assets) + transitive_assets_symbols.extend(exports_transitive_assets_symbols) + transitive_compiled_assets.extend(exports_transitive_compiled_assets) + direct_compiled_resources.extend(exports_direct_compiled_resources) + transitive_compiled_resources.extend(exports_transitive_compiled_resources) + transitive_resources_files.extend(exports_transitive_resources_files) + transitive_manifests.extend(exports_transitive_manifests) + transitive_r_txts.extend(exports_transitive_r_txts) + + compiled_assets = None + parsed_assets = None + compiled_resources = None + out_aapt2_r_txt = None + r_txt = None + data_binding_layout_info = None + processed_resources = resource_files + processed_manifest = None + if not defines_resources: + if aapt: + # Generate an empty manifest with the right package + generated_manifest = ctx.actions.declare_file( + "_migrated/_generated/" + ctx.label.name + "/AndroidManifest.xml", + ) + _generate_dummy_manifest( + ctx, + out_manifest = generated_manifest, + java_package = java_package if java_package else ctx.label.package.replace("/", "."), + min_sdk_version = 14, + ) + r_txt = ctx.actions.declare_file( + "_migrated/" + ctx.label.name + "_symbols/R.txt", + ) + out_manifest = ctx.actions.declare_file( + "_migrated/" + ctx.label.name + "_processed_manifest/AndroidManifest.xml", + ) + _busybox.package( + ctx, + out_r_src_jar = ctx.actions.declare_file( + "_migrated/" + ctx.label.name + ".srcjar", + ), + out_r_txt = r_txt, + out_manifest = out_manifest, + manifest = generated_manifest, + assets = assets, + assets_dir = assets_dir, + resource_files = resource_files, + direct_resources_nodes = + depset(transitive = direct_resources_nodes, order = "preorder"), + transitive_resources_nodes = + depset(transitive = transitive_resources_nodes, order = "preorder"), + transitive_assets = transitive_assets, + transitive_compiled_assets = transitive_compiled_assets, + transitive_resource_files = transitive_resources_files, + transitive_compiled_resources = transitive_compiled_resources, + transitive_manifests = transitive_manifests, + transitive_r_txts = transitive_r_txts, + package_type = "LIBRARY", + java_package = java_package, + android_jar = android_jar, + aapt = aapt, + busybox = busybox, + host_javabase = host_javabase, + should_throw_on_conflict = False, + ) + resources_ctx[_STARLARK_PROCESSED_MANIFEST] = out_manifest + resources_ctx[_STARLARK_R_TXT] = r_txt + resources_ctx[_STARLARK_PROCESSED_RESOURCES] = resource_files + + else: + if stamp_manifest: + stamped_manifest = ctx.actions.declare_file( + "_migrated/_renamed/" + ctx.label.name + "/AndroidManifest.xml", + ) + _busybox.merge_manifests( + ctx, + out_file = stamped_manifest, + manifest = manifest, + merge_type = "LIBRARY", + java_package = java_package, + busybox = busybox, + host_javabase = host_javabase, + ) + manifest = stamped_manifest + + if instrument_xslt: + g3itr_manifest = ctx.actions.declare_file( + "_migrated/" + ctx.label.name + "_g3itr_manifest/AndroidManifest.xml", + ) + _add_g3itr( + ctx, + out_manifest = g3itr_manifest, + manifest = manifest, + xsltproc = xsltproc, + instrument_xslt = instrument_xslt, + ) + manifest = g3itr_manifest + + parsed_assets = ctx.actions.declare_file( + "_migrated/" + ctx.label.name + "_symbols/assets.bin", + ) + _busybox.parse( + ctx, + out_symbols = parsed_assets, + assets = assets, + assets_dir = assets_dir, + busybox = busybox, + host_javabase = host_javabase, + ) + merged_assets = ctx.actions.declare_file( + "_migrated/" + ctx.label.name + "_files/assets.zip", + ) + _busybox.merge_assets( + ctx, + out_assets_zip = merged_assets, + assets = assets, + assets_dir = assets_dir, + symbols = parsed_assets, + direct_resources_nodes = depset( + transitive = direct_resources_nodes, + order = "preorder", + ), + transitive_resources_nodes = depset( + transitive = transitive_resources_nodes, + order = "preorder", + ), + transitive_assets = transitive_assets, + transitive_assets_symbols = transitive_assets_symbols, + busybox = busybox, + host_javabase = host_javabase, + ) + resources_ctx[_VALIDATION_RESULTS].append(merged_assets) + + if assets: + compiled_assets = ctx.actions.declare_file( + "_migrated/" + ctx.label.name + "_symbols/assets.zip", + ) + _busybox.compile( + ctx, + out_file = compiled_assets, + assets = assets, + assets_dir = assets_dir, + aapt = aapt, + busybox = busybox, + host_javabase = host_javabase, + ) + + if enable_data_binding: + data_binding_layout_info = ctx.actions.declare_file( + "_migrated/databinding/" + ctx.label.name + "/layout-info.zip", + ) + processed_resources, resources_dirname = _make_databinding_outputs( + ctx, + resource_files, + ) + _busybox.process_databinding( + ctx, + out_databinding_info = data_binding_layout_info, + out_databinding_processed_resources = processed_resources, + databinding_resources_dirname = resources_dirname, + resource_files = resource_files, + java_package = java_package, + busybox = busybox, + host_javabase = host_javabase, + ) + + compiled_resources = ctx.actions.declare_file( + "_migrated/" + ctx.label.name + "_symbols/symbols.zip", + ) + _busybox.compile( + ctx, + out_file = compiled_resources, + resource_files = processed_resources, + aapt = aapt, + busybox = busybox, + host_javabase = host_javabase, + ) + + # TODO(b/160907203): Remove this fix once the native resource processing pipeline is turned off. + if enable_data_binding: + fixed_compiled_resources = ctx.actions.declare_file( + "_migrated/fixed/" + ctx.label.name + "_symbols/symbols.zip", + ) + _fix_databinding_compiled_resources( + ctx, + out_compiled_resources = fixed_compiled_resources, + compiled_resources = compiled_resources, + zip_tool = zip_tool, + ) + compiled_resources = fixed_compiled_resources + + out_class_jar = ctx.actions.declare_file( + "_migrated/" + ctx.label.name + "_resources.jar", + ) + processed_manifest = ctx.actions.declare_file( + "_migrated/" + ctx.label.name + "_processed_manifest/AndroidManifest.xml", + ) + out_aapt2_r_txt = ctx.actions.declare_file( + "_migrated/" + ctx.label.name + "_symbols/R.aapt2.txt", + ) + _busybox.merge_compiled( + ctx, + out_class_jar = out_class_jar, + out_manifest = processed_manifest, + out_aapt2_r_txt = out_aapt2_r_txt, + java_package = java_package, + manifest = manifest, + compiled_resources = compiled_resources, + direct_resources_nodes = + depset(transitive = direct_resources_nodes, order = "preorder"), + transitive_resources_nodes = depset( + transitive = transitive_resources_nodes, + order = "preorder", + ), + direct_compiled_resources = depset( + transitive = direct_compiled_resources, + order = "preorder", + ), + transitive_compiled_resources = depset( + transitive = transitive_compiled_resources, + order = "preorder", + ), + android_jar = android_jar, + busybox = busybox, + host_javabase = host_javabase, + ) + resources_ctx[_MERGED_MANIFEST] = processed_manifest + + apk = ctx.actions.declare_file( + "_migrated/" + ctx.label.name + "_files/library.ap_", + ) + r_java = ctx.actions.declare_file( + "_migrated/" + ctx.label.name + ".srcjar", + ) + r_txt = ctx.actions.declare_file( + "_migrated/" + ctx.label.name + "_symbols/R.txt", + ) + _busybox.validate_and_link( + ctx, + out_r_src_jar = r_java, + out_r_txt = r_txt, + out_file = apk, + compiled_resources = compiled_resources, + transitive_compiled_resources = depset( + transitive = transitive_compiled_resources, + order = "preorder", + ), + java_package = java_package, + manifest = processed_manifest, + android_jar = android_jar, + aapt = aapt, + busybox = busybox, + host_javabase = host_javabase, + ) + resources_ctx[_RESOURCES_APK] = apk + + java_info = JavaInfo( + output_jar = out_class_jar, + compile_jar = out_class_jar, + source_jar = r_java, + ) + + packages_to_r_txts_depset.setdefault(java_package, []).append(depset([out_aapt2_r_txt])) + + resources_ctx[_R_JAVA] = java_info + resources_ctx[_DATA_BINDING_LAYOUT_INFO] = data_binding_layout_info + + # In a normal build, the outputs of _busybox.validate_and_link are unused. However we need + # this action to run to support resource visibility checks. + resources_ctx[_VALIDATION_RESULTS].append(r_txt) + + # Needed for AAR generation. The Starlark resource processing pipeline uses the aapt2_r_txt file, + # which is why we can't use the StarlarkAndroidResourcesInfo provider when generating the aar. + resources_ctx[_STARLARK_PROCESSED_MANIFEST] = processed_manifest + resources_ctx[_STARLARK_R_TXT] = r_txt + resources_ctx[_STARLARK_PROCESSED_RESOURCES] = processed_resources + + # TODO(b/117338320): Transitive lists defined here are incorrect; direct should come + # before transitive, and the order should be topological order instead of preorder. + # However, some applications may depend on this incorrect order. + if defines_resources: + transitive_resources_nodes = transitive_resources_nodes + direct_resources_nodes + direct_resources_nodes = [] + transitive_compiled_resources = transitive_compiled_resources + direct_compiled_resources + direct_compiled_resources = [] + else: + if fix_resource_transitivity: + transitive_resources_nodes = transitive_resources_nodes + direct_resources_nodes + direct_resources_nodes = [] + transitive_compiled_resources = transitive_compiled_resources + direct_compiled_resources + direct_compiled_resources = [] + + # TODO(b/144163743): If the resource transitivity fix is disabled and resources-related + # inputs are missing, we implicitly export deps here. This legacy behavior must exist in the + # Starlark resource processing pipeline until we can clean up the depot. + + packages_to_r_txts = dict() + for pkg, depsets in packages_to_r_txts_depset.items(): + packages_to_r_txts[pkg] = depset(transitive = depsets) + + # TODO(b/159916013): Audit neverlink behavior. Some processing can likely be skipped if the target is neverlink. + # TODO(b/69668042): Don't propagate exported providers/artifacts. Exports should respect neverlink. + if resources_neverlink: + resources_ctx[_PROVIDERS].append(StarlarkAndroidResourcesInfo( + direct_resources_nodes = depset( + transitive = exports_direct_resources_nodes, + order = "preorder", + ), + transitive_resources_nodes = depset( + transitive = exports_transitive_resources_nodes, + order = "preorder", + ), + transitive_assets = depset( + transitive = exports_transitive_assets, + order = "preorder", + ), + transitive_assets_symbols = depset( + transitive = exports_transitive_assets_symbols, + order = "preorder", + ), + transitive_compiled_assets = depset( + transitive = exports_transitive_compiled_assets, + order = "preorder", + ), + transitive_resource_files = depset( + transitive = exports_transitive_resources_files, + order = "preorder", + ), + direct_compiled_resources = depset( + transitive = exports_direct_compiled_resources, + order = "preorder", + ), + transitive_compiled_resources = depset( + transitive = exports_transitive_compiled_resources, + order = "preorder", + ), + transitive_manifests = depset( + [processed_manifest] if processed_manifest else [], + transitive = exports_transitive_manifests, + order = "preorder", + ), + transitive_r_txts = depset( + [out_aapt2_r_txt] if out_aapt2_r_txt else [], + transitive = exports_transitive_r_txts, + order = "preorder", + ), + packages_to_r_txts = packages_to_r_txts, + )) + else: + # Depsets are ordered below to match the order in the legacy native rules. + resources_ctx[_PROVIDERS].append(StarlarkAndroidResourcesInfo( + direct_resources_nodes = depset( + [ResourcesNodeInfo( + label = ctx.label, + assets = depset(assets), + assets_dir = assets_dir, + assets_symbols = parsed_assets, + compiled_assets = compiled_assets, + resource_files = depset(processed_resources), + compiled_resources = compiled_resources, + r_txt = out_aapt2_r_txt, + manifest = processed_manifest, + exports_manifest = exports_manifest, + )] if defines_resources else [], + transitive = direct_resources_nodes + exports_direct_resources_nodes, + order = "preorder", + ), + transitive_resources_nodes = depset( + transitive = transitive_resources_nodes + exports_transitive_resources_nodes, + order = "preorder", + ), + transitive_assets = depset( + assets, + transitive = transitive_assets + exports_transitive_assets, + order = "preorder", + ), + transitive_assets_symbols = depset( + [parsed_assets] if parsed_assets else [], + transitive = transitive_assets_symbols + exports_transitive_assets_symbols, + order = "preorder", + ), + transitive_compiled_assets = depset( + [compiled_assets] if compiled_assets else [], + transitive = transitive_compiled_assets + exports_transitive_compiled_assets, + order = "preorder", + ), + transitive_resource_files = depset( + processed_resources, + transitive = transitive_resources_files + exports_transitive_resources_files, + order = "preorder", + ), + direct_compiled_resources = depset( + [compiled_resources] if compiled_resources else [], + transitive = direct_compiled_resources + exports_direct_compiled_resources, + order = "preorder", + ), + transitive_compiled_resources = depset( + [compiled_resources] if compiled_resources else [], + transitive = transitive_compiled_resources + exports_transitive_compiled_resources, + order = "preorder", + ), + transitive_manifests = depset( + [processed_manifest] if processed_manifest else [], + transitive = transitive_manifests + exports_transitive_manifests, + order = "preorder", + ), + transitive_r_txts = depset( + [out_aapt2_r_txt] if out_aapt2_r_txt else [], + transitive = transitive_r_txts + exports_transitive_r_txts, + order = "preorder", + ), + packages_to_r_txts = packages_to_r_txts, + )) + + if not propagate_resources: + resources_ctx[_R_JAVA] = None + resources_ctx[_PROVIDERS] = [] + + # TODO(b/69552500): In the Starlark Android Rules, the R compile time + # JavaInfo is added as a runtime dependency to the JavaInfo. Stop + # adding the R.jar as a runtime dependency. + resources_ctx[_PROVIDERS].append( + AndroidLibraryResourceClassJarProvider( + depset( + (resources_ctx[_R_JAVA].runtime_output_jars if resources_ctx[_R_JAVA] else []), + transitive = [ + p.jars + for p in utils.collect_providers( + AndroidLibraryResourceClassJarProvider, + deps, + exports, + ) + ], + order = "preorder", + ), + ), + ) + + return resources_ctx + + +def _process( + ctx, + manifest = None, + resource_files = None, + defined_assets = False, + assets = None, + defined_assets_dir = False, + assets_dir = None, + exports_manifest = False, + java_package = None, + custom_package = None, + neverlink = False, + enable_data_binding = False, + deps = [], + exports = [], + android_jar = None, + android_kit = None, + aapt = None, + busybox = None, + xsltproc = None, + instrument_xslt = None, + java_toolchain = None, + host_javabase = None, + enable_res_v3 = False, + res_v3_dummy_manifest = None, + res_v3_dummy_r_txt = None, + fix_resource_transitivity = False, + fix_export_exporting = False, + propagate_resources = True, + zip_tool = None): + out_ctx = _process_starlark( + ctx, + java_package = java_package, + manifest = manifest, + defined_assets = defined_assets, + # TODO(b/159937795): When the Starlark Resources Processing pipeline is + # default and the native version is no longer used, remove the depset + # creation and directly pass through ctx.files.assets to this method. + assets = + depset(transitive = [target.files for target in assets]).to_list(), + defined_assets_dir = defined_assets_dir, + assets_dir = assets_dir, + exports_manifest = exports_manifest, + stamp_manifest = True if java_package else False, + deps = deps, + exports = exports, + resource_files = depset(transitive = [target.files for target in resource_files]).to_list(), + enable_data_binding = enable_data_binding, + fix_resource_transitivity = fix_resource_transitivity, + neverlink = neverlink, + propagate_resources = propagate_resources, + android_jar = android_jar, + aapt = aapt, + android_kit = android_kit, + busybox = busybox, + instrument_xslt = instrument_xslt, + xsltproc = xsltproc, + java_toolchain = java_toolchain, + host_javabase = host_javabase, + zip_tool = zip_tool, + ) + + + if _VALIDATION_OUTPUTS not in out_ctx: + out_ctx[_VALIDATION_OUTPUTS] = [] + + return _ResourcesProcessContextInfo(**out_ctx) + +resources = struct( + process = _process, + process_starlark = _process_starlark, + package = _package, + make_aar = _make_aar, + + # Exposed for mobile-install + compile = _compile, + legacy_merge_manifests = _legacy_merge_manifests, + + # Exposed for android_local_test and android_library + generate_dummy_manifest = _generate_dummy_manifest, +) + +testing = struct( + add_g3itr = _add_g3itr, + filter_multi_cpu_configuration_targets = _filter_multi_cpu_configuration_targets, + get_legacy_mergee_manifests = _get_legacy_mergee_manifests, + make_databinding_outputs = _make_databinding_outputs, + ResourcesPackageContextInfo = _ResourcesPackageContextInfo, + ResourcesProcessContextInfo = _ResourcesProcessContextInfo, +) diff --git a/rules/robolectric_properties_template.txt b/rules/robolectric_properties_template.txt new file mode 100644 index 0000000..d05a28c --- /dev/null +++ b/rules/robolectric_properties_template.txt @@ -0,0 +1,5 @@ +android_merged_manifest=%android_merged_manifest% +android_merged_resources=%android_merged_resources% +android_merged_assets=%android_merged_assets% +android_custom_package=%android_custom_package% +android_resource_apk=%android_resource_apk% diff --git a/rules/rules.bzl b/rules/rules.bzl new file mode 100644 index 0000000..0d416ec --- /dev/null +++ b/rules/rules.bzl @@ -0,0 +1,60 @@ +# Copyright 2019 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Skylark rules for building Android apps.""" + +load( + "//rules/aar_import:rule.bzl", + _aar_import = "aar_import", +) +load( + "//rules/android_application:android_application.bzl", + _android_application = "android_application", +) +load( + ":android_binary.bzl", + _android_binary = "android_binary", +) +load( + "//rules/android_library:rule.bzl", + _android_library = "android_library_macro", +) +load( + ":android_ndk_repository.bzl", + _android_ndk_repository = "android_ndk_repository", +) +load( + ":android_sdk.bzl", + _android_sdk = "android_sdk", +) +load( + ":android_sdk_repository.bzl", + _android_sdk_repository = "android_sdk_repository", +) +load( + ":android_tools_defaults_jar.bzl", + _android_tools_defaults_jar = "android_tools_defaults_jar", +) + +# Current version. Tools may check this to determine compatibility. +RULES_ANDROID_VERSION = "0.1.0" + +aar_import = _aar_import +android_application = _android_application +android_binary = _android_binary +android_library = _android_library +android_ndk_repository = _android_ndk_repository +android_sdk = _android_sdk +android_sdk_repository = _android_sdk_repository +android_tools_defaults_jar = _android_tools_defaults_jar diff --git a/rules/toolchains/emulator/toolchain.bzl b/rules/toolchains/emulator/toolchain.bzl new file mode 100644 index 0000000..61bfb77 --- /dev/null +++ b/rules/toolchains/emulator/toolchain.bzl @@ -0,0 +1,59 @@ +# Copyright 2019 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Defines the emulator_toolchain rule to allow configuring emulator binaries to use.""" + +EmulatorInfo = provider( + doc = "Information used to launch a specific version of the emulator.", + fields = { + "emulator": "A label for the emulator launcher executable at stable version.", + "emulator_deps": "Additional files required to launch the stable version of emulator.", + "emulator_suffix": "An optional path suffix used to find emulator binary under the emulator label path", + }, +) + +def _emulator_toolchain_impl(ctx): + toolchain_info = platform_common.ToolchainInfo( + info = EmulatorInfo( + emulator = ctx.attr.emulator, + emulator_deps = ctx.attr.emulator_deps, + emulator_suffix = ctx.attr.emulator_suffix, + ), + ) + return [toolchain_info] + +emulator_toolchain = rule( + implementation = _emulator_toolchain_impl, + attrs = { + "emulator": attr.label( + allow_files = True, + cfg = "host", + mandatory = True, + ), + "emulator_deps": attr.label_list( + allow_files = True, + cfg = "host", + ), + "emulator_head": attr.label( + allow_files = True, + cfg = "host", + ), + "emulator_head_deps": attr.label_list( + allow_files = True, + cfg = "host", + ), + "emulator_suffix": attr.string(default = ""), + "emulator_head_suffix": attr.string(default = ""), + }, +) diff --git a/rules/utils.bzl b/rules/utils.bzl new file mode 100644 index 0000000..d796371 --- /dev/null +++ b/rules/utils.bzl @@ -0,0 +1,467 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Utilities for the Android rules.""" + +load(":providers.bzl", "FailureInfo") + +_CUU = "\033[A" +_EL = "\033[K" +_DEFAULT = "\033[0m" +_BOLD = "\033[1m" +_RED = "\033[31m" +_GREEN = "\033[32m" +_MAGENTA = "\033[35m" +_ERASE_PREV_LINE = "\n" + _CUU + _EL + +_INFO = _ERASE_PREV_LINE + _GREEN + "INFO: " + _DEFAULT + "%s" +_WARNING = _ERASE_PREV_LINE + _MAGENTA + "WARNING: " + _DEFAULT + "%s" +_ERROR = _ERASE_PREV_LINE + _BOLD + _RED + "ERROR: " + _DEFAULT + "%s" + +_WORD_CHARS = { + "A": True, + "B": True, + "C": True, + "D": True, + "E": True, + "F": True, + "G": True, + "H": True, + "I": True, + "J": True, + "K": True, + "L": True, + "M": True, + "N": True, + "O": True, + "P": True, + "Q": True, + "R": True, + "S": True, + "T": True, + "U": True, + "V": True, + "W": True, + "X": True, + "Y": True, + "Z": True, + "a": True, + "b": True, + "c": True, + "d": True, + "e": True, + "f": True, + "g": True, + "h": True, + "i": True, + "j": True, + "k": True, + "l": True, + "m": True, + "n": True, + "o": True, + "p": True, + "q": True, + "r": True, + "s": True, + "t": True, + "u": True, + "v": True, + "w": True, + "x": True, + "y": True, + "z": True, + "0": True, + "1": True, + "2": True, + "3": True, + "4": True, + "5": True, + "6": True, + "7": True, + "8": True, + "9": True, + "_": True, +} + +_HEX_CHAR = { + 0x0: "0", + 0x1: "1", + 0x2: "2", + 0x3: "3", + 0x4: "4", + 0x5: "5", + 0x6: "6", + 0x7: "7", + 0x8: "8", + 0x9: "9", + 0xA: "A", + 0xB: "B", + 0xC: "C", + 0xD: "D", + 0xE: "E", + 0xF: "F", +} + +_JAVA_RESERVED = { + "abstract": True, + "assert": True, + "boolean": True, + "break": True, + "byte": True, + "case": True, + "catch": True, + "char": True, + "class": True, + "const": True, + "continue": True, + "default": True, + "do": True, + "double": True, + "else": True, + "enum": True, + "extends": True, + "final": True, + "finally": True, + "float": True, + "for": True, + "goto": True, + "if": True, + "implements": True, + "import": True, + "instanceof": True, + "int": True, + "interface": True, + "long": True, + "native": True, + "new": True, + "package": True, + "private": True, + "protected": True, + "public": True, + "return": True, + "short": True, + "static": True, + "strictfp": True, + "super": True, + "switch": True, + "synchronized": True, + "this": True, + "throw": True, + "throws": True, + "transient": True, + "try": True, + "void": True, + "volatile": True, + "while": True, + "true": True, + "false": True, + "null": True, +} + +def _collect_providers(provider, *all_deps): + """Collects the requested providers from the given list of deps.""" + providers = [] + for deps in all_deps: + for dep in deps: + if provider in dep: + providers.append(dep[provider]) + return providers + +def _join_depsets(providers, attr, order = "default"): + """Returns a merged depset using 'attr' from each provider in 'providers'.""" + return depset(transitive = [getattr(p, attr) for p in providers], order = order) + +def _first(collection): + """Returns the first item in the collection.""" + for i in collection: + return i + return _error("The collection is empty.") + +def _only(collection): + """Returns the only item in the collection.""" + if len(collection) != 1: + _error("Expected one element, has %s." % len(collection)) + return _first(collection) + +def _list_or_depset_to_list(list_or_depset): + if type(list_or_depset) == "list": + return list_or_depset + elif type(list_or_depset) == "depset": + return list_or_depset.to_list() + else: + return _error("Expected a list or a depset. Got %s" % type(list_or_depset)) + +def _copy_file(ctx, src, dest): + if src.is_directory or dest.is_directory: + fail("Cannot use copy_file with directories") + ctx.actions.run_shell( + command = "cp --reflink=auto $1 $2", + arguments = [src.path, dest.path], + inputs = [src], + outputs = [dest], + mnemonic = "CopyFile", + progress_message = "Copy %s to %s" % (src.short_path, dest.short_path), + ) + +def _copy_dir(ctx, src, dest): + if not src.is_directory: + fail("copy_dir src must be a directory") + ctx.actions.run_shell( + command = "cp -r --reflink=auto $1 $2", + arguments = [src.path, dest.path], + inputs = [src], + outputs = [dest], + mnemonic = "CopyDir", + progress_message = "Copy %s to %s" % (src.short_path, dest.short_path), + ) + +def _info(msg): + """Print info.""" + print(_INFO % msg) + +def _warn(msg): + """Print warning.""" + print(_WARNING % msg) + +def _debug(msg): + """Print debug.""" + print("\n%s" % msg) + +def _error(msg): + """Print error and fail.""" + fail(_ERASE_PREV_LINE + _CUU + _ERASE_PREV_LINE + _CUU + _ERROR % msg) + +def _expand_var(config_vars, value): + """Expands make variables of the form $(SOME_VAR_NAME) for a single value. + + "$$(SOME_VAR_NAME)" is escaped to a literal value of "$(SOME_VAR_NAME)" instead of being + expanded. + + Args: + config_vars: String dictionary which maps config variables to their expanded values. + value: The string to apply substitutions to. + + Returns: + The string value with substitutions applied. + """ + parts = value.split("$(") + replacement = parts[0] + last_char = replacement[-1] if replacement else "" + for part in parts[1:]: + var_end = part.find(")") + if last_char == "$": + # If "$$(..." is found, treat it as "$(..." + replacement += "(" + part + elif var_end == -1 or part[:var_end] not in config_vars: + replacement += "$(" + part + else: + replacement += config_vars[part[:var_end]] + part[var_end + 1:] + last_char = replacement[-1] if replacement else "" + return replacement + +def _expand_make_vars(ctx, vals): + """Expands make variables of the form $(SOME_VAR_NAME). + + Args: + ctx: The rules context. + vals: Dictionary. Values of the form $(...) will be replaced. + + Returns: + A dictionary containing vals.keys() and the expanded values. + """ + res = {} + for k, v in vals.items(): + res[k] = _expand_var(ctx.var, v) + return res + +def _dedupe_split_attr(attr): + if not attr: + return [] + arch = _first(sorted(attr.keys())) + return attr[arch] + +def _get_runfiles(ctx, attrs): + runfiles = ctx.runfiles() + for attr in attrs: + executable = attr[DefaultInfo].files_to_run.executable + if executable: + runfiles = runfiles.merge(ctx.runfiles([executable])) + runfiles = runfiles.merge( + ctx.runfiles( + # Wrap DefaultInfo.files in depset to strip ordering. + transitive_files = depset( + transitive = [attr[DefaultInfo].files], + ), + ), + ) + runfiles = runfiles.merge(attr[DefaultInfo].default_runfiles) + return runfiles + +def _sanitize_string(s, replacement = ""): + """Sanitizes a string by replacing all non-word characters. + + This matches the \\w regex character class [A_Za-z0-9_]. + + Args: + s: String to sanitize. + replacement: Replacement for all non-word characters. Optional. + + Returns: + The original string with all non-word characters replaced. + """ + return "".join([s[i] if s[i] in _WORD_CHARS else replacement for i in range(len(s))]) + +def _hex(n, pad = True): + """Convert an integer number to an uppercase hexadecimal string. + + Args: + n: Integer number. + pad: Optional. Pad the result to 8 characters with leading zeroes. Default = True. + + Returns: + Return a representation of an integer number as a hexadecimal string. + """ + hex_str = "" + for _ in range(8): + r = n % 16 + n = n // 16 + hex_str = _HEX_CHAR[r] + hex_str + if pad: + return hex_str + else: + return hex_str.lstrip("0") + +def _sanitize_java_package(pkg): + return ".".join(["xxx" if p in _JAVA_RESERVED else p for p in pkg.split(".")]) + +def _check_for_failures(label, *all_deps): + """Collects FailureInfo providers from the given list of deps and fails if there's at least one.""" + failure_infos = _collect_providers(FailureInfo, *all_deps) + if failure_infos: + error = "in label '%s':" % label + for failure_info in failure_infos: + error += "\n\t" + failure_info.error + _error(error) + +def _run_validation( + ctx, + validation_out, + executable, + outputs = [], + tools = [], + **args): + """Creates an action that runs an executable as a validation. + + Note: When the validation executable fails, it should return a non-zero + value to signify a validation failure. + + Args: + ctx: The context. + validation_out: A File. The output of the executable is piped to the + file. This artifact should then be propagated to "validations" in the + OutputGroupInfo. + executable: See ctx.actions.run#executable. + outputs: See ctx.actions.run#outputs. + tools: See ctx.actions.run#tools. + **args: Remaining args are directly propagated to ctx.actions.run_shell. + See ctx.actions.run_shell for further documentation. + """ + exec_type = type(executable) + exec_bin = None + exec_bin_path = None + if exec_type == "FilesToRunProvider": + exec_bin = executable.executable + exec_bin_path = exec_bin.path + elif exec_type == "File": + exec_bin = executable + exec_bin_path = exec_bin.path + elif exec_type == type(""): + exec_bin_path = executable + else: + fail( + "Error, executable should be a File, FilesToRunProvider or a " + + "string that represents a path to a tool, got: %s" % exec_type, + ) + + ctx.actions.run_shell( + command = """#!/bin/bash +set -eu +set -o pipefail # Returns the executables failure code, if it fails. + +EXECUTABLE={executable} +VALIDATION_OUT={validation_out} + +"${{EXECUTABLE}}" $@ 2>&1 | tee -a "${{VALIDATION_OUT}}" +""".format( + executable = exec_bin_path, + validation_out = validation_out.path, + ), + tools = tools + ([exec_bin] if exec_bin else []), + outputs = [validation_out] + outputs, + **args + ) + +def get_android_toolchain(ctx): + return ctx.toolchains["@rules_android//toolchains/android:toolchain_type"] + +def get_android_sdk(ctx): + if hasattr(ctx.fragments.android, "incompatible_use_toolchain_resolution") and ctx.fragments.android.incompatible_use_toolchain_resolution: + return ctx.toolchains["@rules_android//toolchains/android_sdk:toolchain_type"].android_sdk_info + else: + return ctx.attr._android_sdk[AndroidSdkInfo] + +def _get_compilation_mode(ctx): + """Retrieves the compilation mode from the context. + + Returns: + A string that represents the compilation mode. + """ + return ctx.var["COMPILATION_MODE"] + +compilation_mode = struct( + DBG = "dbg", + FASTBUILD = "fastbuild", + OPT = "opt", + get = _get_compilation_mode, +) + +utils = struct( + check_for_failures = _check_for_failures, + collect_providers = _collect_providers, + copy_file = _copy_file, + copy_dir = _copy_dir, + expand_make_vars = _expand_make_vars, + first = _first, + dedupe_split_attr = _dedupe_split_attr, + get_runfiles = _get_runfiles, + join_depsets = _join_depsets, + only = _only, + run_validation = _run_validation, + sanitize_string = _sanitize_string, + sanitize_java_package = _sanitize_java_package, + hex = _hex, + list_or_depset_to_list = _list_or_depset_to_list, +) + +log = struct( + debug = _debug, + error = _error, + info = _info, + warn = _warn, +) + +testing = struct( + expand_var = _expand_var, +) diff --git a/src/validations/aar_import_checks/BUILD b/src/validations/aar_import_checks/BUILD new file mode 100644 index 0000000..8f7bb2a --- /dev/null +++ b/src/validations/aar_import_checks/BUILD @@ -0,0 +1,27 @@ +# Description: +# Package for aar_import validation checks +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +genrule( + name = "gen_aar_import_checks", + outs = ["aar_import_checks"], + cmd = """ +cat > $@ <<"EOF" +#!/bin/bash +while [[ $$# -gt 0 ]] +do + case $$1 in + -output) + out="$$2" + ;; + esac + shift # past argument + shift # past value +done +touch $$out +EOF +""", + executable = True, +) diff --git a/test/rules/resources/BUILD b/test/rules/resources/BUILD new file mode 100644 index 0000000..e9ef118 --- /dev/null +++ b/test/rules/resources/BUILD @@ -0,0 +1,4 @@ +# Used in android_binary_internal resources diff test +exports_files( + ["test_stub_script.sh"], +) diff --git a/toolchains/android/BUILD b/toolchains/android/BUILD new file mode 100644 index 0000000..3741ee8 --- /dev/null +++ b/toolchains/android/BUILD @@ -0,0 +1,57 @@ +# Description: +# Defines the Android toolchain. + +load(":toolchain.bzl", "android_toolchain") +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") + +licenses(["notice"]) + +filegroup( + name = "all_files", + srcs = glob(["**"]), +) + +# Android Toolchain Type +toolchain_type( + name = "toolchain_type", + visibility = ["//visibility:public"], +) + +# Default Android Toolchain +android_toolchain( + name = "android_default", + visibility = ["//visibility:public"], +) + +toolchain( + name = "android_default_toolchain", + toolchain = ":android_default", + toolchain_type = ":toolchain_type", +) + +bzl_library( + name = "bzl", + srcs = glob(["*.bzl"]), +) + +genrule( + name = "gen_unzip", + outs = ["unzip.sh"], + cmd = """cat > $@ < + + + + + + \ No newline at end of file diff --git a/tools/android/xslt/xslt.sh b/tools/android/xslt/xslt.sh new file mode 100755 index 0000000..fef6235 --- /dev/null +++ b/tools/android/xslt/xslt.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Copyright 2020 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# noop +cp $6 $4 diff --git a/tools/jdk/BUILD b/tools/jdk/BUILD new file mode 100644 index 0000000..d83484b --- /dev/null +++ b/tools/jdk/BUILD @@ -0,0 +1,17 @@ +load("@bazel_tools//tools/jdk:default_java_toolchain.bzl", "default_java_toolchain") + +default_java_toolchain( + name = "toolchain_android_only", + bootclasspath = [ + "//tools/android:android_jar", + # TODO(b/175805830): Add this only when desugaring is enabled. + "@bazel_tools//tools/android:desugar_java8_extra_bootclasspath", + ], + visibility = ["//visibility:public"], +) + +alias( + name = "current_java_runtime", + actual = "@bazel_tools//tools/jdk:current_java_runtime", + visibility = ["//visibility:public"], +) -- cgit v1.2.3 From d2770f49530d1d97be13ee13ee1d2fef8ddc7d35 Mon Sep 17 00:00:00 2001 From: A Googler Date: Tue, 28 Sep 2021 18:16:34 -0400 Subject: Document attributes of make_rule of android_library's rule.bzl. PiperOrigin-RevId: 399540836 --- rules/android_library/rule.bzl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rules/android_library/rule.bzl b/rules/android_library/rule.bzl index 91e7c33..02cc1fc 100644 --- a/rules/android_library/rule.bzl +++ b/rules/android_library/rule.bzl @@ -57,6 +57,8 @@ def make_rule( Args: attrs: A dict. The attributes for the rule. implementation: A function. The rule's implementation method. + outputs: A dict, function, or None. The rule's outputs. + additional_toolchains: A list. Additional toolchains passed to pass to rule(toolchains). Returns: A rule. -- cgit v1.2.3 From 3134be983780a93c9b9fb655f5b3aa50e6000ac4 Mon Sep 17 00:00:00 2001 From: A Googler Date: Tue, 28 Sep 2021 20:50:09 -0400 Subject: Document zip_tool parameter in resources.bzl. PiperOrigin-RevId: 399570068 --- rules/resources.bzl | 1 + 1 file changed, 1 insertion(+) diff --git a/rules/resources.bzl b/rules/resources.bzl index a08b6df..a6578da 100644 --- a/rules/resources.bzl +++ b/rules/resources.bzl @@ -316,6 +316,7 @@ def _fix_databinding_compiled_resources( ctx: The context. out_compiled_resources: File. The modified compiled_resources output. compiled_resources: File. The compiled_resources zip. + zip_tool: FilesToRunProvider. The zip tool executable or FilesToRunProvider """ ctx.actions.run_shell( outputs = [out_compiled_resources], -- cgit v1.2.3 From 66a91ef2c38d4477d1f24eac3f92adc3a8bf611c Mon Sep 17 00:00:00 2001 From: A Googler Date: Wed, 29 Sep 2021 14:32:17 -0700 Subject: Update Skylark -> Starlark. PiperOrigin-RevId: 399777947 --- rules/rules.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/rules.bzl b/rules/rules.bzl index 0d416ec..315fd4f 100644 --- a/rules/rules.bzl +++ b/rules/rules.bzl @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Skylark rules for building Android apps.""" +"""Starlark rules for building Android apps.""" load( "//rules/aar_import:rule.bzl", -- cgit v1.2.3 From 4c607e34da61e3b55c0b90f3b8bad627a1c44831 Mon Sep 17 00:00:00 2001 From: A Googler Date: Wed, 29 Sep 2021 17:49:59 -0700 Subject: Internal PiperOrigin-RevId: 399818465 --- rules/busybox.bzl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rules/busybox.bzl b/rules/busybox.bzl index ff64d0c..26d76a1 100644 --- a/rules/busybox.bzl +++ b/rules/busybox.bzl @@ -800,7 +800,7 @@ def _merge_manifests( fail("Unexpected manifest merge type: " + merge_type) outputs = [out_file] - directs = [manifest] + directs = [manifest] if manifest else [] transitives = [mergee_manifests] # Args for busybox @@ -808,7 +808,8 @@ def _merge_manifests( args.use_param_file("@%s", use_always = True) args.add("--tool", "MERGE_MANIFEST") args.add("--") - args.add("--manifest", manifest) + if manifest: + args.add("--manifest", manifest) args.add_all( "--mergeeManifests", [mergee_manifests], -- cgit v1.2.3 From 075967864a4a185d9544867b4b753fd3b6b7dd3e Mon Sep 17 00:00:00 2001 From: A Googler Date: Thu, 28 Oct 2021 15:39:06 -0700 Subject: Add a test_class attribute to android_instrumentation_test. Similiar to android_local_test and java_test, test_class is an optional attribute which specifies the test_class to execute. If unspecified, it will be derived based on current java package name and rule name. PiperOrigin-RevId: 406234187 --- rules/acls.bzl | 7 +++++++ ..._instrumentation_derived_test_class_rollout.bzl | 23 ++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 rules/acls/android_instrumentation_derived_test_class_rollout.bzl diff --git a/rules/acls.bzl b/rules/acls.bzl index 96dd8e4..ffa1377 100644 --- a/rules/acls.bzl +++ b/rules/acls.bzl @@ -67,6 +67,7 @@ load( ) load("@rules_android//rules/acls:kt_android_library_rollout.bzl", "KT_ANDROID_LIBRARY_FALLBACK", "KT_ANDROID_LIBRARY_ROLLOUT") load("@rules_android//rules/acls:android_instrumentation_test_manifest_check_rollout.bzl", "ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK", "ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT") +load("@rules_android//rules/acls:android_instrumentation_derived_test_class_rollout.bzl", "ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_FALLBACK", "ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_ROLLOUT") def _in_aar_import_deps_checker(fqn): return not _matches(fqn, AAR_IMPORT_DEPS_CHECKER_FALLBACK_DICT) and _matches(fqn, AAR_IMPORT_DEPS_CHECKER_ROLLOUT_DICT) @@ -184,6 +185,9 @@ def _in_kt_android_library_rollout(fqn): def _in_android_instrumentation_test_manifest_check_rollout(fqn): return not _matches(fqn, ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK_DICT) and _matches(fqn, ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT_DICT) +def _in_android_instrumentation_test_derived_test_class_rollout(fqn): + return not _matches(fqn, ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_FALLBACK_DICT) and _matches(fqn, ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_ROLLOUT_DICT) + def _make_dict(lst): """Do not use this method outside of this file.""" return {t: True for t in lst} @@ -249,6 +253,8 @@ KT_ANDROID_LIBRARY_ROLLOUT_DICT = _make_dict(KT_ANDROID_LIBRARY_ROLLOUT) KT_ANDROID_LIBRARY_FALLBACK_DICT = _make_dict(KT_ANDROID_LIBRARY_FALLBACK) ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT_DICT = _make_dict(ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT) ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK_DICT = _make_dict(ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK) +ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_ROLLOUT_DICT = _make_dict(ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_ROLLOUT) +ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_FALLBACK_DICT = _make_dict(ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_FALLBACK) def _matches(fqn, dct): # Labels with workspace names ("@workspace//pkg:target") are not supported. @@ -324,6 +330,7 @@ acls = struct( in_partial_jetification_targets = _in_partial_jetification_targets, in_kt_android_library_rollout = _in_kt_android_library_rollout, in_android_instrumentation_test_manifest_check_rollout = _in_android_instrumentation_test_manifest_check_rollout, + in_android_instrumentation_test_derived_test_class_rollout = _in_android_instrumentation_test_derived_test_class_rollout, ) # Visible for testing diff --git a/rules/acls/android_instrumentation_derived_test_class_rollout.bzl b/rules/acls/android_instrumentation_derived_test_class_rollout.bzl new file mode 100644 index 0000000..782beb8 --- /dev/null +++ b/rules/acls/android_instrumentation_derived_test_class_rollout.bzl @@ -0,0 +1,23 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Rollout list for enabling test class derivation in android_instrumentation_test,""" + +ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_ROLLOUT = [ + "//:__subpackages__", +] + +ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_FALLBACK = [ + "//javatests/notinacl:__subpackages__", +] -- cgit v1.2.3 From 9720527a9c290b185a5760ffa3dea5b3ef2ab826 Mon Sep 17 00:00:00 2001 From: A Googler Date: Fri, 29 Oct 2021 10:02:46 -0700 Subject: Android toolchain needs to use the correct transition for binary dependencies. PiperOrigin-RevId: 406382920 --- toolchains/android/toolchain.bzl | 50 ++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/toolchains/android/toolchain.bzl b/toolchains/android/toolchain.bzl index a63fe63..42cdf9d 100644 --- a/toolchains/android/toolchain.bzl +++ b/toolchains/android/toolchain.bzl @@ -21,89 +21,89 @@ _ATTRS = dict( ), aar_import_checks = attr.label( allow_single_file = True, - cfg = "host", + cfg = "exec", default = "//src/validations/aar_import_checks", executable = True, ), aar_embedded_jars_extractor = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", default = "@bazel_tools//tools/android:aar_embedded_jars_extractor", executable = True, ), aar_embedded_proguard_extractor = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", default = "@bazel_tools//tools/android:aar_embedded_proguard_extractor", executable = True, ), aar_native_libs_zip_creator = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", default = "@bazel_tools//tools/android:aar_native_libs_zip_creator", executable = True, ), aar_resources_extractor = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", default = "@bazel_tools//tools/android:aar_resources_extractor", executable = True, ), adb = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", default = "@androidsdk//:platform-tools/adb", executable = True, ), add_g3itr_xslt = attr.label( - cfg = "host", + cfg = "exec", default = Label("//tools/android/xslt:add_g3itr.xslt"), allow_files = True, ), android_archive_jar_optimization_inputs_validator = attr.label( allow_files = True, default = "@androidsdk//:fail", - cfg = "host", + cfg = "exec", executable = True, ), android_archive_manifest_package_validator = attr.label( allow_files = True, default = "@androidsdk//:fail", - cfg = "host", + cfg = "exec", executable = True, ), android_archive_packages_validator = attr.label( allow_files = True, default = "@androidsdk//:fail", - cfg = "host", + cfg = "exec", executable = True, ), android_kit = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", default = "@androidsdk//:fail", # TODO: "//src/tools/ak", needs Go executable = True, ), android_resources_busybox = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", default = "@bazel_tools//src/tools/android/java/com/google/devtools/build/android:ResourceProcessorBusyBox_deploy.jar", executable = True, ), apk_to_bundle_tool = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", default = "@androidsdk//:fail", executable = True, ), bundletool = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", default = "//tools/android:bundletool_deploy.jar", executable = True, ), data_binding_annotation_processor = attr.label( - cfg = "host", + cfg = "exec", default = "@//tools/android:compiler_annotation_processor", # TODO: processor rules should be moved into rules_android ), data_binding_annotation_template = attr.label( @@ -111,25 +111,25 @@ _ATTRS = dict( allow_files = True, ), data_binding_exec = attr.label( - cfg = "host", + cfg = "exec", default = "@bazel_tools//tools/android:databinding_exec", executable = True, ), desugar_java8_extra_bootclasspath = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", default = "@bazel_tools//tools/android:desugar_java8_extra_bootclasspath", executable = True, ), idlclass = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", default = "@bazel_tools//tools/android:IdlClass", # _deploy.jar? executable = True, ), import_deps_checker = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", default = "@android_tools//:ImportDepsChecker_deploy.jar", executable = True, ), @@ -143,18 +143,18 @@ _ATTRS = dict( ), jdeps_tool = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", # used in android_local_test default = "@androidsdk//:fail", # TODO: "//src/tools/jdeps", needs Go executable = True, ), proguard_allowlister = attr.label( - cfg = "host", + cfg = "exec", default = "@bazel_tools//tools/jdk:proguard_whitelister", executable = True, ), proto_map_generator = attr.label( - cfg = "host", + cfg = "exec", default = "@androidsdk//:fail", allow_files = True, executable = True, @@ -175,18 +175,18 @@ _ATTRS = dict( default = "@androidsdk//:fail", ), unzip_tool = attr.label( - cfg = "host", + cfg = "exec", default = "//toolchains/android:unzip", executable = True, ), xsltproc_tool = attr.label( - cfg = "host", + cfg = "exec", default = Label("//tools/android/xslt:xslt"), allow_files = True, executable = True, ), zip_tool = attr.label( - cfg = "host", + cfg = "exec", default = "//toolchains/android:zip", executable = True, ), -- cgit v1.2.3 From ed3dc8e1945d475f4f2c6926f204732c4e4aef7b Mon Sep 17 00:00:00 2001 From: A Googler Date: Mon, 8 Nov 2021 16:18:17 -0800 Subject: Propagates testonly param of android_feature_module bzl macro. PiperOrigin-RevId: 408467852 --- rules/android_application/android_feature_module_rule.bzl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rules/android_application/android_feature_module_rule.bzl b/rules/android_application/android_feature_module_rule.bzl index 87c57b5..3041656 100644 --- a/rules/android_application/android_feature_module_rule.bzl +++ b/rules/android_application/android_feature_module_rule.bzl @@ -121,6 +121,7 @@ def android_feature_module_macro(_android_binary, _android_library, **attrs): tags = getattr(attrs, "tags", []) transitive_configs = getattr(attrs, "transitive_configs", []) visibility = getattr(attrs, "visibility", None) + testonly = getattr(attrs, "testonly", None) # Create strings.xml containing split title title_id = "split_" + str(hash(fqn)).replace("-", "N") @@ -164,6 +165,7 @@ EOF tags = tags, transitive_configs = transitive_configs, visibility = visibility, + testonly = testonly, ) # Wrap any deps in an android_binary. Will be validated to ensure does not contain any dexes @@ -178,6 +180,7 @@ EOF "visibility": visibility, "feature_flags": getattr(attrs, "feature_flags", None), "$enable_manifest_merging": False, + "testonly": testonly, } _android_binary(**binary_attrs) @@ -193,4 +196,5 @@ EOF tags = tags, transitive_configs = transitive_configs, visibility = visibility, + testonly = testonly, ) -- cgit v1.2.3 From 16cf9b4a85a65ace03a008fa8b2248b206f7f467 Mon Sep 17 00:00:00 2001 From: A Googler Date: Thu, 18 Nov 2021 07:12:35 -0800 Subject: Change Starlark custom_package fallback default behavior to false, and allow None value for custom_package. PiperOrigin-RevId: 410794963 --- rules/busybox.bzl | 9 ++++++--- rules/java.bzl | 13 ++----------- rules/resources.bzl | 8 -------- 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/rules/busybox.bzl b/rules/busybox.bzl index 26d76a1..a92fe68 100644 --- a/rules/busybox.bzl +++ b/rules/busybox.bzl @@ -380,7 +380,8 @@ def _package( args.add("--versionName", version_name) if version_code: args.add("--versionCode", version_code) - args.add("--packageForR", java_package) + if java_package: + args.add("--packageForR", java_package) _java.run( ctx = ctx, @@ -821,7 +822,8 @@ def _merge_manifests( ",".join(["%s:%s" % (_escape_mv(k), _escape_mv(v)) for k, v in manifest_values.items()]), ) args.add("--mergeType", merge_type) - args.add("--customPackage", java_package) + if java_package: + args.add("--customPackage", java_package) args.add("--manifestOutput", out_file) if out_log_file: args.add("--log", out_log_file) @@ -928,7 +930,8 @@ def _generate_binary_r( args.add("--") args.add("--primaryRTxt", r_txt) args.add("--primaryManifest", manifest) - args.add("--packageForR", package_for_r) + if package_for_r: + args.add("--packageForR", package_for_r) args.add_all( resources_nodes, map_each = _make_generate_binay_r_flags, diff --git a/rules/java.bzl b/rules/java.bzl index 8193bdf..2e2fc58 100644 --- a/rules/java.bzl +++ b/rules/java.bzl @@ -88,8 +88,7 @@ def _resolve_package(path): def _resolve_package_from_label( label, - custom_package = None, - fallback = True): + custom_package = None): """Resolves the Java package from a Label. When no legal Java package can be resolved from the label, None will be @@ -110,15 +109,7 @@ def _resolve_package_from_label( [label.package] + _path.split(label.name)[:-1], ) - java_package = _resolve_package(label_path) - - if java_package != None: # "" is a valid result. - return java_package - - if fallback: - return label.package.replace("/", ".") - - return None + return _resolve_package(label_path) def _root(path): """Determines the Java root from the given path. diff --git a/rules/resources.bzl b/rules/resources.bzl index a6578da..158a9c0 100644 --- a/rules/resources.bzl +++ b/rules/resources.bzl @@ -60,11 +60,6 @@ _ASSET_DEFINITION_ERROR = ( "both empty or non-empty." ) -_JAVA_PACKAGE_MISSING_ERROR = ( - "In target %s, a java package is required when stamping " + - "the manifest." -) - _INCORRECT_RESOURCE_LAYOUT_ERROR = ( "'%s' is not in the expected resource directory structure of " + "/{%s}/" % (",").join(_RESOURCE_FOLDER_TYPES) @@ -1100,9 +1095,6 @@ def _process_starlark( if resource_files and not manifest: _log.error(_MANIFEST_MISSING_ERROR % ctx.label) - if stamp_manifest and not java_package: - _log.error(_JAVA_PACKAGE_MISSING_ERROR % ctx.label) - direct_resources_nodes = [] transitive_resources_nodes = [] transitive_assets = [] -- cgit v1.2.3 From ae31eaa3e8666a1324377cec4aa90d50900ae846 Mon Sep 17 00:00:00 2001 From: Tim Peut Date: Tue, 23 Nov 2021 15:43:48 -0800 Subject: Use a default package when generating the android_local_test manifest. PiperOrigin-RevId: 411911688 --- rules/resources.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/resources.bzl b/rules/resources.bzl index 158a9c0..2ad89c6 100644 --- a/rules/resources.bzl +++ b/rules/resources.bzl @@ -134,7 +134,7 @@ def _generate_dummy_manifest( min_sdk_version = None): content = """ """ % java_package + package="%s">""" % (java_package or "com.default") if min_sdk_version: content = content + """ -- cgit v1.2.3 From a90db94514fbeeeaba3c08614c62590c1c8957a0 Mon Sep 17 00:00:00 2001 From: Olivier St-Onge Date: Tue, 30 Nov 2021 16:42:03 -0800 Subject: Internal PiperOrigin-RevId: 413274555 --- rules/acls.bzl | 7 +++++++ rules/acls/android_binary_starlark_javac.bzl | 23 +++++++++++++++++++++++ rules/data_binding.bzl | 11 +++++++++-- rules/resources.bzl | 3 +++ 4 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 rules/acls/android_binary_starlark_javac.bzl diff --git a/rules/acls.bzl b/rules/acls.bzl index ffa1377..8d9b467 100644 --- a/rules/acls.bzl +++ b/rules/acls.bzl @@ -39,6 +39,7 @@ load("@rules_android//rules/acls:android_archive_excluded_deps_denylist.bzl", "A load("@rules_android//rules/acls:android_test_lockdown.bzl", "ANDROID_TEST_LOCKDOWN_GENERATOR_FUNCTIONS", "ANDROID_TEST_LOCKDOWN_TARGETS") load("@rules_android//rules/acls:android_device_plugin_rollout.bzl", "ANDROID_DEVICE_PLUGIN_FALLBACK", "ANDROID_DEVICE_PLUGIN_ROLLOUT") load("@rules_android//rules/acls:android_instrumentation_binary_starlark_resources.bzl", "ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK", "ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT") +load("@rules_android//rules/acls:android_binary_starlark_javac.bzl", "ANDROID_BINARY_STARLARK_JAVAC_FALLBACK", "ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT") load("@rules_android//rules/acls:android_feature_splits_dogfood.bzl", "ANDROID_FEATURE_SPLITS_DOGFOOD") load("@rules_android//rules/acls:android_library_implicit_exports.bzl", "ANDROID_LIBRARY_IMPLICIT_EXPORTS", "ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS") load("@rules_android//rules/acls:android_library_resources_without_srcs.bzl", "ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS", "ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS") @@ -96,6 +97,9 @@ def _in_android_device_plugin_rollout(fqn): def _in_android_instrumentation_binary_starlark_resources(fqn): return not _matches(fqn, ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK_DICT) and _matches(fqn, ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT_DICT) +def _in_android_binary_starlark_javac(fqn): + return not _matches(fqn, ANDROID_BINARY_STARLARK_JAVAC_FALLBACK_DICT) and _matches(fqn, ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT_DICT) + def _in_android_feature_splits_dogfood(fqn): return _matches(fqn, ANDROID_FEATURE_SPLITS_DOGFOOD_DICT) @@ -206,6 +210,8 @@ ANDROID_DEVICE_PLUGIN_ROLLOUT_DICT = _make_dict(ANDROID_DEVICE_PLUGIN_ROLLOUT) ANDROID_DEVICE_PLUGIN_FALLBACK_DICT = _make_dict(ANDROID_DEVICE_PLUGIN_FALLBACK) ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT_DICT = _make_dict(ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT) ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK_DICT = _make_dict(ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK) +ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT_DICT = _make_dict(ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT) +ANDROID_BINARY_STARLARK_JAVAC_FALLBACK_DICT = _make_dict(ANDROID_BINARY_STARLARK_JAVAC_FALLBACK) ANDROID_FEATURE_SPLITS_DOGFOOD_DICT = _make_dict(ANDROID_FEATURE_SPLITS_DOGFOOD) ANDROID_LIBRARY_IMPLICIT_EXPORTS_DICT = _make_dict(ANDROID_LIBRARY_IMPLICIT_EXPORTS) ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS_DICT = _make_dict(ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS) @@ -303,6 +309,7 @@ acls = struct( in_android_archive_excluded_deps_denylist = _in_android_archive_excluded_deps_denylist, in_android_device_plugin_rollout = _in_android_device_plugin_rollout, in_android_instrumentation_binary_starlark_resources = _in_android_instrumentation_binary_starlark_resources, + in_android_binary_starlark_javac = _in_android_binary_starlark_javac, in_android_feature_splits_dogfood = _in_android_feature_splits_dogfood, in_android_library_implicit_exports = _in_android_library_implicit_exports, in_android_library_implicit_exports_generator_functions = _in_android_library_implicit_exports_generator_functions, diff --git a/rules/acls/android_binary_starlark_javac.bzl b/rules/acls/android_binary_starlark_javac.bzl new file mode 100644 index 0000000..54f6058 --- /dev/null +++ b/rules/acls/android_binary_starlark_javac.bzl @@ -0,0 +1,23 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for rollout of Starlark java compilation in android_binary_internal.""" + +# keep sorted +ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT = [ +] + +# keep sorted +ANDROID_BINARY_STARLARK_JAVAC_FALLBACK = [ +] diff --git a/rules/data_binding.bzl b/rules/data_binding.bzl index 9243673..e853058 100644 --- a/rules/data_binding.bzl +++ b/rules/data_binding.bzl @@ -130,6 +130,7 @@ def _setup_dependent_lib_artifacts(ctx, output_dir, deps): def _get_javac_opts( ctx, java_package, + artifact_type, dependency_artifacts_dir, aar_out_dir, class_info_path, @@ -145,7 +146,7 @@ def _get_javac_opts( dependency_artifacts_dir) javac_opts.append("-Aandroid.databinding.aarOutDir=" + aar_out_dir) javac_opts.append("-Aandroid.databinding.sdkDir=/not/used") - javac_opts.append("-Aandroid.databinding.artifactType=LIBRARY") + javac_opts.append("-Aandroid.databinding.artifactType=" + artifact_type) javac_opts.append("-Aandroid.databinding.exportClassListOutFile=" + "/tmp/exported_classes") javac_opts.append("-Aandroid.databinding.modulePackage=" + java_package) @@ -169,6 +170,7 @@ def _process( enable_data_binding = False, java_package = None, layout_info = None, + artifact_type = "LIBRARY", deps = [], exports = [], data_binding_exec = None, @@ -183,10 +185,11 @@ def _process( enable_data_binding: boolean. Determines whether Data Binding should be enabled. java_package: String. The Java package. + layout_info: A file. The layout-info zip file. + artifact_type: String. Either LIBRARY or APPLICATION. deps: sequence of DataBindingV2Info providers. A list of deps. Optional. exports: sequence of DataBindingV2Info providers. A list of exports. Optional. - layout_info: A file. The layout-info zip file. data_binding_exec: The DataBinding executable. data_binding_annotation_processor: JavaInfo. The JavaInfo for the annotation processor. @@ -197,6 +200,9 @@ def _process( A DataBindingContextInfo provider. """ + if artifact_type not in ["LIBRARY", "APPLICATION"]: + fail("Unexpected artifact type: " + artifact_type) + # TODO(b/154513292): Clean up bad usages of context objects. if resources_ctx: defines_resources = resources_ctx.defines_resources @@ -267,6 +273,7 @@ def _process( db_info[_JAVAC_OPTS] = _get_javac_opts( ctx, java_package, + artifact_type, ( br_out.path.rpartition(br_out.short_path)[0] + ctx.label.package + diff --git a/rules/resources.bzl b/rules/resources.bzl index 2ad89c6..3b5ef5f 100644 --- a/rules/resources.bzl +++ b/rules/resources.bzl @@ -425,6 +425,7 @@ def _package( should_throw_on_conflict = True, enable_data_binding = False, enable_manifest_merging = True, + should_compile_java_srcs = True, aapt = None, android_jar = None, legacy_merger = None, @@ -478,6 +479,7 @@ def _package( parameter is enabled. Without this setting, data binding expressions produce build failures. enable_manifest_merging: boolean. If true, manifest merging will be performed. + should_compile_java_srcs: boolean. If native android_binary should perform java compilation. aapt: FilesToRunProvider. The aapt executable or FilesToRunProvider. android_jar: File. The Android jar. legacy_merger: FilesToRunProvider. The legacy manifest merger executable. @@ -728,6 +730,7 @@ def _package( r_txt = r_txt, resources_zip = resource_files_zip, databinding_info = data_binding_layout_info, + should_compile_java_srcs = should_compile_java_srcs, )) return _ResourcesPackageContextInfo(**packaged_resources_ctx) -- cgit v1.2.3 From e453a64dc83be6dcb72b59a48eb5be68bbcaaab0 Mon Sep 17 00:00:00 2001 From: Olivier St-Onge Date: Fri, 3 Dec 2021 16:25:33 -0800 Subject: Use --experimental_android_resource_cycle_shrinking for AAPT2_PACKAGE PiperOrigin-RevId: 414047751 --- rules/busybox.bzl | 5 +++++ rules/resources.bzl | 19 ++++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/rules/busybox.bzl b/rules/busybox.bzl index a92fe68..195d80a 100644 --- a/rules/busybox.bzl +++ b/rules/busybox.bzl @@ -203,6 +203,7 @@ def _package( additional_apks_to_link_against = [], nocompress_extensions = [], proto_format = False, + shrink_resource_cycles = False, version_name = None, version_code = None, android_jar = None, @@ -257,6 +258,8 @@ def _package( nocompress_extensions: A list of strings. File extension to leave uncompressed in the apk. proto_format: Boolean, whether to generate the resource table in proto format. + shrink_resource_cycles: Boolean, flag that enables more shrinking of + code and resources by instructing AAPT2 to emit conditional Proguard keep rules. version_name: A string. The version name to stamp the generated manifest with. Optional. version_code: A string. The version code to stamp the generated manifest with. Optional. android_jar: A File. The Android Jar. @@ -376,6 +379,8 @@ def _package( args.add_joined("--uncompressedExtensions", nocompress_extensions, join_with = ",") if proto_format: args.add("--resourceTableAsProto") + if shrink_resource_cycles: + args.add("--conditionalKeepRules=yes") if version_name: args.add("--versionName", version_name) if version_code: diff --git a/rules/resources.bzl b/rules/resources.bzl index 3b5ef5f..a819c34 100644 --- a/rules/resources.bzl +++ b/rules/resources.bzl @@ -599,6 +599,15 @@ def _package( host_javabase = host_javabase, ) + resource_shrinking_enabled = _is_resource_shrinking_enabled( + shrink_resources, + use_android_resource_shrinking, + ) + shrink_resource_cycles = _should_shrink_resource_cycles( + use_android_resource_cycle_shrinking, + resource_shrinking_enabled, + ) + resource_apk = ctx.actions.declare_file(ctx.label.name + "_migrated/.ap_") r_java = ctx.actions.declare_file("_migrated/" + ctx.label.name + ".srcjar") r_txt = ctx.actions.declare_file(ctx.label.name + "_migrated/_symbols/R.txt") @@ -643,6 +652,7 @@ def _package( densities = densities, nocompress_extensions = nocompress_extensions, java_package = java_package, + shrink_resource_cycles = shrink_resource_cycles, version_name = manifest_values[_VERSION_NAME] if _VERSION_NAME in manifest_values else None, version_code = manifest_values[_VERSION_CODE] if _VERSION_CODE in manifest_values else None, android_jar = android_jar, @@ -656,15 +666,6 @@ def _package( packaged_resources_ctx[_PACKAGED_RESOURCE_APK] = resource_apk packaged_resources_ctx[_PACKAGED_VALIDATION_RESULT] = resource_files_zip - resource_shrinking_enabled = _is_resource_shrinking_enabled( - shrink_resources, - use_android_resource_shrinking, - ) - shrink_resource_cycles = _should_shrink_resource_cycles( - use_android_resource_cycle_shrinking, - resource_shrinking_enabled, - ) - # Fix class jar name because some tests depend on {label_name}_resources.jar being the suffix of # the path, with _RESOURCES_DO_NOT_USE removed from the label name. _RESOURCES_SUFFIX = "_RESOURCES_DO_NOT_USE" -- cgit v1.2.3 From 944aee23014db2dd9e1b4ab97facb661add439f8 Mon Sep 17 00:00:00 2001 From: A Googler Date: Mon, 6 Dec 2021 08:50:21 -0800 Subject: Copy attrs keys to avoid "dictionary changed size during iteration". MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Python 3 `dict.keys()` returns a view of the dictionary’s keys, not a copy as Python 2. Deleting keys while iterating the view will cause ``` RuntimeError: dictionary changed size during iteration ``` PiperOrigin-RevId: 414454586 --- rules/android_binary_internal/rule.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/android_binary_internal/rule.bzl b/rules/android_binary_internal/rule.bzl index 3070688..6dc2589 100644 --- a/rules/android_binary_internal/rule.bzl +++ b/rules/android_binary_internal/rule.bzl @@ -67,7 +67,7 @@ def sanitize_attrs(attrs, allowed_attrs = ATTRS.keys()): Returns: A dictionary containing valid attributes. """ - for attr_name in attrs.keys(): + for attr_name in list(attrs.keys()): if attr_name not in allowed_attrs and attr_name not in _DEFAULT_ALLOWED_ATTRS: attrs.pop(attr_name, None) -- cgit v1.2.3 From 9b03eee90f787a685051aed7f1a839feaa40ed2d Mon Sep 17 00:00:00 2001 From: A Googler Date: Mon, 6 Dec 2021 10:16:18 -0800 Subject: Move acls.in_android_instrumentation_test_derived_test_class_rollout to its own file. Each change to any acl file transitively impacts all BUILD files which load any android rule. This change reverses the dependency, so each acl file manages its own list, with the common data structure methods moved to a new file. PiperOrigin-RevId: 414478495 --- rules/acls.bzl | 219 ++++++++++----------- ..._instrumentation_derived_test_class_rollout.bzl | 12 +- 2 files changed, 116 insertions(+), 115 deletions(-) diff --git a/rules/acls.bzl b/rules/acls.bzl index 8d9b467..3b6f67b 100644 --- a/rules/acls.bzl +++ b/rules/acls.bzl @@ -68,201 +68,195 @@ load( ) load("@rules_android//rules/acls:kt_android_library_rollout.bzl", "KT_ANDROID_LIBRARY_FALLBACK", "KT_ANDROID_LIBRARY_ROLLOUT") load("@rules_android//rules/acls:android_instrumentation_test_manifest_check_rollout.bzl", "ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK", "ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT") -load("@rules_android//rules/acls:android_instrumentation_derived_test_class_rollout.bzl", "ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_FALLBACK", "ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_ROLLOUT") def _in_aar_import_deps_checker(fqn): - return not _matches(fqn, AAR_IMPORT_DEPS_CHECKER_FALLBACK_DICT) and _matches(fqn, AAR_IMPORT_DEPS_CHECKER_ROLLOUT_DICT) + return not matches(fqn, AAR_IMPORT_DEPS_CHECKER_FALLBACK_DICT) and matches(fqn, AAR_IMPORT_DEPS_CHECKER_ROLLOUT_DICT) def _in_aar_import_explicit_exports_manifest(fqn): - return _matches(fqn, AAR_IMPORT_EXPLICIT_EXPORTS_MANIFEST_DICT) + return matches(fqn, AAR_IMPORT_EXPLICIT_EXPORTS_MANIFEST_DICT) def _in_aar_import_exports_r_java(fqn): - return _matches(fqn, AAR_IMPORT_EXPORTS_R_JAVA_DICT) + return matches(fqn, AAR_IMPORT_EXPORTS_R_JAVA_DICT) def _in_aar_propagate_resources(fqn): - return not _matches(fqn, AAR_PROPAGATE_RESOURCES_FALLBACK_DICT) and _matches(fqn, AAR_PROPAGATE_RESOURCES_ROLLOUT_DICT) + return not matches(fqn, AAR_PROPAGATE_RESOURCES_FALLBACK_DICT) and matches(fqn, AAR_PROPAGATE_RESOURCES_ROLLOUT_DICT) def _in_ait_virtual_device(fqn): - return not _matches(fqn, AIT_VIRTUAL_DEVICE_FALLBACK_DICT) and _matches(fqn, AIT_VIRTUAL_DEVICE_ROLLOUT_DICT) + return not matches(fqn, AIT_VIRTUAL_DEVICE_FALLBACK_DICT) and matches(fqn, AIT_VIRTUAL_DEVICE_ROLLOUT_DICT) def _in_android_archive_dogfood(fqn): - return _matches(fqn, ANDROID_ARCHIVE_DOGFOOD_DICT) + return matches(fqn, ANDROID_ARCHIVE_DOGFOOD_DICT) def _in_android_archive_excluded_deps_denylist(fqn): - return _matches(fqn, ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST_DICT) + return matches(fqn, ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST_DICT) def _in_android_device_plugin_rollout(fqn): - return not _matches(fqn, ANDROID_DEVICE_PLUGIN_FALLBACK_DICT) and _matches(fqn, ANDROID_DEVICE_PLUGIN_ROLLOUT_DICT) + return not matches(fqn, ANDROID_DEVICE_PLUGIN_FALLBACK_DICT) and matches(fqn, ANDROID_DEVICE_PLUGIN_ROLLOUT_DICT) def _in_android_instrumentation_binary_starlark_resources(fqn): - return not _matches(fqn, ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK_DICT) and _matches(fqn, ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT_DICT) + return not matches(fqn, ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK_DICT) and matches(fqn, ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT_DICT) def _in_android_binary_starlark_javac(fqn): - return not _matches(fqn, ANDROID_BINARY_STARLARK_JAVAC_FALLBACK_DICT) and _matches(fqn, ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT_DICT) + return not matches(fqn, ANDROID_BINARY_STARLARK_JAVAC_FALLBACK_DICT) and matches(fqn, ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT_DICT) def _in_android_feature_splits_dogfood(fqn): - return _matches(fqn, ANDROID_FEATURE_SPLITS_DOGFOOD_DICT) + return matches(fqn, ANDROID_FEATURE_SPLITS_DOGFOOD_DICT) def _in_android_lint_checks_rollout(fqn): - return not _matches(fqn, ANDROID_LINT_CHECKS_FALLBACK_DICT) and _matches(fqn, ANDROID_LINT_CHECKS_ROLLOUT_DICT) + return not matches(fqn, ANDROID_LINT_CHECKS_FALLBACK_DICT) and matches(fqn, ANDROID_LINT_CHECKS_ROLLOUT_DICT) def _in_android_lint_rollout(fqn): - return not _matches(fqn, ANDROID_LINT_FALLBACK_DICT) and _matches(fqn, ANDROID_LINT_ROLLOUT_DICT) + return not matches(fqn, ANDROID_LINT_FALLBACK_DICT) and matches(fqn, ANDROID_LINT_ROLLOUT_DICT) def _in_lint_registry_rollout(fqn): - return not _matches(fqn, LINT_REGISTRY_FALLBACK_DICT) and _matches(fqn, LINT_REGISTRY_ROLLOUT_DICT) + return not matches(fqn, LINT_REGISTRY_FALLBACK_DICT) and matches(fqn, LINT_REGISTRY_ROLLOUT_DICT) def _in_android_build_stamping_rollout(fqn): - return not _matches(fqn, ANDROID_BUILD_STAMPING_FALLBACK_DICT) and _matches(fqn, ANDROID_BUILD_STAMPING_ROLLOUT_DICT) + return not matches(fqn, ANDROID_BUILD_STAMPING_FALLBACK_DICT) and matches(fqn, ANDROID_BUILD_STAMPING_ROLLOUT_DICT) def _in_android_test_lockdown_allowlist(fqn, generator): if generator == "android_test": - return _matches(fqn, ANDROID_TEST_LOCKDOWN_TARGETS) + return matches(fqn, ANDROID_TEST_LOCKDOWN_TARGETS) return generator in ANDROID_TEST_LOCKDOWN_GENERATOR_FUNCTIONS_DICT def _in_b122039567(fqn): - return _matches(fqn, B122039567_DICT) + return matches(fqn, B122039567_DICT) def _in_b123854163(fqn): - return _matches(fqn, B123854163_DICT) + return matches(fqn, B123854163_DICT) def _in_android_library_implicit_exports(fqn): - return _matches(fqn, ANDROID_LIBRARY_IMPLICIT_EXPORTS_DICT) + return matches(fqn, ANDROID_LIBRARY_IMPLICIT_EXPORTS_DICT) def _in_android_library_implicit_exports_generator_functions(gfn): return gfn in ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS_DICT def _in_android_library_resources_without_srcs(fqn): - return _matches(fqn, ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_DICT) + return matches(fqn, ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_DICT) def _in_android_library_resources_without_srcs_generator_functions(gfn): return gfn in ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS_DICT def _in_android_library_starlark_resource_outputs_rollout(fqn): - return not _matches(fqn, ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK_DICT) and _matches(fqn, ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT_DICT) + return not matches(fqn, ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK_DICT) and matches(fqn, ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT_DICT) def _in_app_installation_snapshot(fqn): - return not _matches(fqn, APP_INSTALLATION_SNAPSHOT_FALLBACK_DICT) and _matches(fqn, APP_INSTALLATION_SNAPSHOT_DICT) + return not matches(fqn, APP_INSTALLATION_SNAPSHOT_FALLBACK_DICT) and matches(fqn, APP_INSTALLATION_SNAPSHOT_DICT) def _in_dex2oat_opts(fqn): - return _matches(fqn, CAN_USE_DEX2OAT_OPTIONS_DICT) + return matches(fqn, CAN_USE_DEX2OAT_OPTIONS_DICT) def _in_fix_export_exporting_rollout(fqn): - return not _matches(fqn, FIX_EXPORT_EXPORTING_FALLBACK_DICT) and _matches(fqn, FIX_EXPORT_EXPORTING_ROLLOUT_DICT) + return not matches(fqn, FIX_EXPORT_EXPORTING_FALLBACK_DICT) and matches(fqn, FIX_EXPORT_EXPORTING_ROLLOUT_DICT) def _in_fix_resource_transivity_rollout(fqn): - return not _matches(fqn, FIX_RESOURCE_TRANSIVITY_FALLBACK_DICT) and _matches(fqn, FIX_RESOURCE_TRANSIVITY_ROLLOUT_DICT) + return not matches(fqn, FIX_RESOURCE_TRANSIVITY_FALLBACK_DICT) and matches(fqn, FIX_RESOURCE_TRANSIVITY_ROLLOUT_DICT) def _in_host_dex2oat_rollout(fqn): - return not _matches(fqn, AIT_USE_HOST_DEX2OAT_ROLLOUT_FALLBACK_DICT) and _matches(fqn, AIT_USE_HOST_DEX2OAT_ROLLOUT_DICT) + return not matches(fqn, AIT_USE_HOST_DEX2OAT_ROLLOUT_FALLBACK_DICT) and matches(fqn, AIT_USE_HOST_DEX2OAT_ROLLOUT_DICT) def _in_install_apps_in_data(fqn): - return _matches(fqn, AIT_INSTALL_APPS_IN_DATA_DICT) + return matches(fqn, AIT_INSTALL_APPS_IN_DATA_DICT) def _in_local_test_multi_proto(fqn): - return _matches(fqn, LOCAL_TEST_MULTI_PROTO_PKG_DICT) + return matches(fqn, LOCAL_TEST_MULTI_PROTO_PKG_DICT) def _in_local_test_rollout(fqn): - return not _matches(fqn, LOCAL_TEST_FALLBACK_DICT) and _matches(fqn, LOCAL_TEST_ROLLOUT_DICT) + return not matches(fqn, LOCAL_TEST_FALLBACK_DICT) and matches(fqn, LOCAL_TEST_ROLLOUT_DICT) def _in_local_test_starlark_resources(fqn): - return not _matches(fqn, LOCAL_TEST_STARLARK_RESOURCES_FALLBACK_DICT) and _matches(fqn, LOCAL_TEST_STARLARK_RESOURCES_ROLLOUT_DICT) + return not matches(fqn, LOCAL_TEST_STARLARK_RESOURCES_FALLBACK_DICT) and matches(fqn, LOCAL_TEST_STARLARK_RESOURCES_ROLLOUT_DICT) def _in_android_test_platform_rollout(fqn): - return not _matches(fqn, ANDROID_TEST_PLATFORM_FALLBACK_DICT) and _matches(fqn, ANDROID_TEST_PLATFORM_ROLLOUT_DICT) + return not matches(fqn, ANDROID_TEST_PLATFORM_FALLBACK_DICT) and matches(fqn, ANDROID_TEST_PLATFORM_ROLLOUT_DICT) def _in_sourceless_binary_rollout(fqn): - return not _matches(fqn, SOURCELESS_BINARY_FALLBACK_DICT) and _matches(fqn, SOURCELESS_BINARY_ROLLOUT_DICT) + return not matches(fqn, SOURCELESS_BINARY_FALLBACK_DICT) and matches(fqn, SOURCELESS_BINARY_ROLLOUT_DICT) def _in_test_to_instrument_test_rollout(fqn): - return not _matches(fqn, TEST_TO_INSTRUMENT_TEST_FALLBACK_DICT) and _matches(fqn, TEST_TO_INSTRUMENT_TEST_ROLLOUT_DICT) + return not matches(fqn, TEST_TO_INSTRUMENT_TEST_FALLBACK_DICT) and matches(fqn, TEST_TO_INSTRUMENT_TEST_ROLLOUT_DICT) def _in_allow_resource_conflicts(fqn): - return _matches(fqn, ALLOW_RESOURCE_CONFLICTS_DICT) + return matches(fqn, ALLOW_RESOURCE_CONFLICTS_DICT) def _in_partial_jetification_targets(fqn): - return not _matches(fqn, PARTIAL_JETIFICATION_TARGETS_FALLBACK_DICT) and _matches(fqn, PARTIAL_JETIFICATION_TARGETS_ROLLOUT_DICT) + return not matches(fqn, PARTIAL_JETIFICATION_TARGETS_FALLBACK_DICT) and matches(fqn, PARTIAL_JETIFICATION_TARGETS_ROLLOUT_DICT) def _in_kt_android_library_rollout(fqn): - return not _matches(fqn, KT_ANDROID_LIBRARY_FALLBACK_DICT) and _matches(fqn, KT_ANDROID_LIBRARY_ROLLOUT_DICT) + return not matches(fqn, KT_ANDROID_LIBRARY_FALLBACK_DICT) and matches(fqn, KT_ANDROID_LIBRARY_ROLLOUT_DICT) def _in_android_instrumentation_test_manifest_check_rollout(fqn): - return not _matches(fqn, ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK_DICT) and _matches(fqn, ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT_DICT) + return not matches(fqn, ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK_DICT) and matches(fqn, ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT_DICT) -def _in_android_instrumentation_test_derived_test_class_rollout(fqn): - return not _matches(fqn, ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_FALLBACK_DICT) and _matches(fqn, ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_ROLLOUT_DICT) - -def _make_dict(lst): - """Do not use this method outside of this file.""" +def make_dict(lst): + """Do not use this method outside of acls directory.""" return {t: True for t in lst} -AAR_IMPORT_DEPS_CHECKER_FALLBACK_DICT = _make_dict(AAR_IMPORT_DEPS_CHECKER_FALLBACK) -AAR_IMPORT_DEPS_CHECKER_ROLLOUT_DICT = _make_dict(AAR_IMPORT_DEPS_CHECKER_ROLLOUT) -AAR_IMPORT_EXPLICIT_EXPORTS_MANIFEST_DICT = _make_dict(AAR_IMPORT_EXPLICIT_EXPORTS_MANIFEST) -AAR_IMPORT_EXPORTS_R_JAVA_DICT = _make_dict(AAR_IMPORT_EXPORTS_R_JAVA) -AAR_PROPAGATE_RESOURCES_FALLBACK_DICT = _make_dict(AAR_PROPAGATE_RESOURCES_FALLBACK) -AAR_PROPAGATE_RESOURCES_ROLLOUT_DICT = _make_dict(AAR_PROPAGATE_RESOURCES_ROLLOUT) -AIT_VIRTUAL_DEVICE_FALLBACK_DICT = _make_dict(AIT_VIRTUAL_DEVICE_FALLBACK) -AIT_VIRTUAL_DEVICE_ROLLOUT_DICT = _make_dict(AIT_VIRTUAL_DEVICE_ROLLOUT) -ANDROID_ARCHIVE_DOGFOOD_DICT = _make_dict(ANDROID_ARCHIVE_DOGFOOD) -ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST_DICT = _make_dict(ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST) -ANDROID_DEVICE_PLUGIN_ROLLOUT_DICT = _make_dict(ANDROID_DEVICE_PLUGIN_ROLLOUT) -ANDROID_DEVICE_PLUGIN_FALLBACK_DICT = _make_dict(ANDROID_DEVICE_PLUGIN_FALLBACK) -ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT_DICT = _make_dict(ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT) -ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK_DICT = _make_dict(ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK) -ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT_DICT = _make_dict(ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT) -ANDROID_BINARY_STARLARK_JAVAC_FALLBACK_DICT = _make_dict(ANDROID_BINARY_STARLARK_JAVAC_FALLBACK) -ANDROID_FEATURE_SPLITS_DOGFOOD_DICT = _make_dict(ANDROID_FEATURE_SPLITS_DOGFOOD) -ANDROID_LIBRARY_IMPLICIT_EXPORTS_DICT = _make_dict(ANDROID_LIBRARY_IMPLICIT_EXPORTS) -ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS_DICT = _make_dict(ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS) -ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_DICT = _make_dict(ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS) -ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS_DICT = _make_dict(ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS) -ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK_DICT = _make_dict(ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK) -ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT_DICT = _make_dict(ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT) -ANDROID_LINT_CHECKS_FALLBACK_DICT = _make_dict(ANDROID_LINT_CHECKS_FALLBACK) -ANDROID_LINT_CHECKS_ROLLOUT_DICT = _make_dict(ANDROID_LINT_CHECKS_ROLLOUT) -ANDROID_LINT_FALLBACK_DICT = _make_dict(ANDROID_LINT_FALLBACK) -ANDROID_LINT_ROLLOUT_DICT = _make_dict(ANDROID_LINT_ROLLOUT) -LINT_REGISTRY_FALLBACK_DICT = _make_dict(LINT_REGISTRY_FALLBACK) -LINT_REGISTRY_ROLLOUT_DICT = _make_dict(LINT_REGISTRY_ROLLOUT) -ANDROID_BUILD_STAMPING_ROLLOUT_DICT = _make_dict(ANDROID_BUILD_STAMPING_ROLLOUT) -ANDROID_BUILD_STAMPING_FALLBACK_DICT = _make_dict(ANDROID_BUILD_STAMPING_FALLBACK) -ANDROID_TEST_LOCKDOWN_GENERATOR_FUNCTIONS_DICT = _make_dict(ANDROID_TEST_LOCKDOWN_GENERATOR_FUNCTIONS) -ANDROID_TEST_LOCKDOWN_TARGETS_DICT = _make_dict(ANDROID_TEST_LOCKDOWN_TARGETS) -APP_INSTALLATION_SNAPSHOT_DICT = _make_dict(APP_INSTALLATION_SNAPSHOT) -APP_INSTALLATION_SNAPSHOT_FALLBACK_DICT = _make_dict(APP_INSTALLATION_SNAPSHOT_FALLBACK) -B122039567_DICT = _make_dict(B122039567) -B123854163_DICT = _make_dict(B123854163) -CAN_USE_DEX2OAT_OPTIONS_DICT = _make_dict(CAN_USE_DEX2OAT_OPTIONS) -FIX_RESOURCE_TRANSIVITY_FALLBACK_DICT = _make_dict(FIX_RESOURCE_TRANSITIVITY_FALLBACK) -FIX_RESOURCE_TRANSIVITY_ROLLOUT_DICT = _make_dict(FIX_RESOURCE_TRANSITIVITY_ROLLOUT) -FIX_EXPORT_EXPORTING_FALLBACK_DICT = _make_dict(FIX_EXPORT_EXPORTING_FALLBACK) -FIX_EXPORT_EXPORTING_ROLLOUT_DICT = _make_dict(FIX_EXPORT_EXPORTING_ROLLOUT) -AIT_USE_HOST_DEX2OAT_ROLLOUT_DICT = _make_dict(AIT_USE_HOST_DEX2OAT_ROLLOUT) -AIT_USE_HOST_DEX2OAT_ROLLOUT_FALLBACK_DICT = _make_dict(AIT_USE_HOST_DEX2OAT_ROLLOUT_FALLBACK) -AIT_INSTALL_APPS_IN_DATA_DICT = _make_dict(INSTALL_APPS_IN_DATA) -LOCAL_TEST_MULTI_PROTO_PKG_DICT = _make_dict(LOCAL_TEST_MULTI_PROTO_PKG) -LOCAL_TEST_FALLBACK_DICT = _make_dict(LOCAL_TEST_FALLBACK) -LOCAL_TEST_ROLLOUT_DICT = _make_dict(LOCAL_TEST_ROLLOUT) -LOCAL_TEST_STARLARK_RESOURCES_FALLBACK_DICT = _make_dict(LOCAL_TEST_STARLARK_RESOURCES_FALLBACK) -LOCAL_TEST_STARLARK_RESOURCES_ROLLOUT_DICT = _make_dict(LOCAL_TEST_STARLARK_RESOURCES_ROLLOUT) -ANDROID_TEST_PLATFORM_FALLBACK_DICT = _make_dict(ANDROID_TEST_PLATFORM_FALLBACK) -ANDROID_TEST_PLATFORM_ROLLOUT_DICT = _make_dict(ANDROID_TEST_PLATFORM_ROLLOUT) -SOURCELESS_BINARY_FALLBACK_DICT = _make_dict(SOURCELESS_BINARY_FALLBACK) -SOURCELESS_BINARY_ROLLOUT_DICT = _make_dict(SOURCELESS_BINARY_ROLLOUT) -TEST_TO_INSTRUMENT_TEST_FALLBACK_DICT = _make_dict(TEST_TO_INSTRUMENT_TEST_FALLBACK) -TEST_TO_INSTRUMENT_TEST_ROLLOUT_DICT = _make_dict(TEST_TO_INSTRUMENT_TEST_ROLLOUT) -ALLOW_RESOURCE_CONFLICTS_DICT = _make_dict(ALLOW_RESOURCE_CONFLICTS) -PARTIAL_JETIFICATION_TARGETS_ROLLOUT_DICT = _make_dict(PARTIAL_JETIFICATION_TARGETS_ROLLOUT) -PARTIAL_JETIFICATION_TARGETS_FALLBACK_DICT = _make_dict(PARTIAL_JETIFICATION_TARGETS_FALLBACK) -KT_ANDROID_LIBRARY_ROLLOUT_DICT = _make_dict(KT_ANDROID_LIBRARY_ROLLOUT) -KT_ANDROID_LIBRARY_FALLBACK_DICT = _make_dict(KT_ANDROID_LIBRARY_FALLBACK) -ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT_DICT = _make_dict(ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT) -ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK_DICT = _make_dict(ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK) -ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_ROLLOUT_DICT = _make_dict(ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_ROLLOUT) -ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_FALLBACK_DICT = _make_dict(ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_FALLBACK) - -def _matches(fqn, dct): +AAR_IMPORT_DEPS_CHECKER_FALLBACK_DICT = make_dict(AAR_IMPORT_DEPS_CHECKER_FALLBACK) +AAR_IMPORT_DEPS_CHECKER_ROLLOUT_DICT = make_dict(AAR_IMPORT_DEPS_CHECKER_ROLLOUT) +AAR_IMPORT_EXPLICIT_EXPORTS_MANIFEST_DICT = make_dict(AAR_IMPORT_EXPLICIT_EXPORTS_MANIFEST) +AAR_IMPORT_EXPORTS_R_JAVA_DICT = make_dict(AAR_IMPORT_EXPORTS_R_JAVA) +AAR_PROPAGATE_RESOURCES_FALLBACK_DICT = make_dict(AAR_PROPAGATE_RESOURCES_FALLBACK) +AAR_PROPAGATE_RESOURCES_ROLLOUT_DICT = make_dict(AAR_PROPAGATE_RESOURCES_ROLLOUT) +AIT_VIRTUAL_DEVICE_FALLBACK_DICT = make_dict(AIT_VIRTUAL_DEVICE_FALLBACK) +AIT_VIRTUAL_DEVICE_ROLLOUT_DICT = make_dict(AIT_VIRTUAL_DEVICE_ROLLOUT) +ANDROID_ARCHIVE_DOGFOOD_DICT = make_dict(ANDROID_ARCHIVE_DOGFOOD) +ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST_DICT = make_dict(ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST) +ANDROID_DEVICE_PLUGIN_ROLLOUT_DICT = make_dict(ANDROID_DEVICE_PLUGIN_ROLLOUT) +ANDROID_DEVICE_PLUGIN_FALLBACK_DICT = make_dict(ANDROID_DEVICE_PLUGIN_FALLBACK) +ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT_DICT = make_dict(ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT) +ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK_DICT = make_dict(ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK) +ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT_DICT = make_dict(ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT) +ANDROID_BINARY_STARLARK_JAVAC_FALLBACK_DICT = make_dict(ANDROID_BINARY_STARLARK_JAVAC_FALLBACK) +ANDROID_FEATURE_SPLITS_DOGFOOD_DICT = make_dict(ANDROID_FEATURE_SPLITS_DOGFOOD) +ANDROID_LIBRARY_IMPLICIT_EXPORTS_DICT = make_dict(ANDROID_LIBRARY_IMPLICIT_EXPORTS) +ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS_DICT = make_dict(ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS) +ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_DICT = make_dict(ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS) +ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS_DICT = make_dict(ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS) +ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK_DICT = make_dict(ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK) +ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT_DICT = make_dict(ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT) +ANDROID_LINT_CHECKS_FALLBACK_DICT = make_dict(ANDROID_LINT_CHECKS_FALLBACK) +ANDROID_LINT_CHECKS_ROLLOUT_DICT = make_dict(ANDROID_LINT_CHECKS_ROLLOUT) +ANDROID_LINT_FALLBACK_DICT = make_dict(ANDROID_LINT_FALLBACK) +ANDROID_LINT_ROLLOUT_DICT = make_dict(ANDROID_LINT_ROLLOUT) +LINT_REGISTRY_FALLBACK_DICT = make_dict(LINT_REGISTRY_FALLBACK) +LINT_REGISTRY_ROLLOUT_DICT = make_dict(LINT_REGISTRY_ROLLOUT) +ANDROID_BUILD_STAMPING_ROLLOUT_DICT = make_dict(ANDROID_BUILD_STAMPING_ROLLOUT) +ANDROID_BUILD_STAMPING_FALLBACK_DICT = make_dict(ANDROID_BUILD_STAMPING_FALLBACK) +ANDROID_TEST_LOCKDOWN_GENERATOR_FUNCTIONS_DICT = make_dict(ANDROID_TEST_LOCKDOWN_GENERATOR_FUNCTIONS) +ANDROID_TEST_LOCKDOWN_TARGETS_DICT = make_dict(ANDROID_TEST_LOCKDOWN_TARGETS) +APP_INSTALLATION_SNAPSHOT_DICT = make_dict(APP_INSTALLATION_SNAPSHOT) +APP_INSTALLATION_SNAPSHOT_FALLBACK_DICT = make_dict(APP_INSTALLATION_SNAPSHOT_FALLBACK) +B122039567_DICT = make_dict(B122039567) +B123854163_DICT = make_dict(B123854163) +CAN_USE_DEX2OAT_OPTIONS_DICT = make_dict(CAN_USE_DEX2OAT_OPTIONS) +FIX_RESOURCE_TRANSIVITY_FALLBACK_DICT = make_dict(FIX_RESOURCE_TRANSITIVITY_FALLBACK) +FIX_RESOURCE_TRANSIVITY_ROLLOUT_DICT = make_dict(FIX_RESOURCE_TRANSITIVITY_ROLLOUT) +FIX_EXPORT_EXPORTING_FALLBACK_DICT = make_dict(FIX_EXPORT_EXPORTING_FALLBACK) +FIX_EXPORT_EXPORTING_ROLLOUT_DICT = make_dict(FIX_EXPORT_EXPORTING_ROLLOUT) +AIT_USE_HOST_DEX2OAT_ROLLOUT_DICT = make_dict(AIT_USE_HOST_DEX2OAT_ROLLOUT) +AIT_USE_HOST_DEX2OAT_ROLLOUT_FALLBACK_DICT = make_dict(AIT_USE_HOST_DEX2OAT_ROLLOUT_FALLBACK) +AIT_INSTALL_APPS_IN_DATA_DICT = make_dict(INSTALL_APPS_IN_DATA) +LOCAL_TEST_MULTI_PROTO_PKG_DICT = make_dict(LOCAL_TEST_MULTI_PROTO_PKG) +LOCAL_TEST_FALLBACK_DICT = make_dict(LOCAL_TEST_FALLBACK) +LOCAL_TEST_ROLLOUT_DICT = make_dict(LOCAL_TEST_ROLLOUT) +LOCAL_TEST_STARLARK_RESOURCES_FALLBACK_DICT = make_dict(LOCAL_TEST_STARLARK_RESOURCES_FALLBACK) +LOCAL_TEST_STARLARK_RESOURCES_ROLLOUT_DICT = make_dict(LOCAL_TEST_STARLARK_RESOURCES_ROLLOUT) +ANDROID_TEST_PLATFORM_FALLBACK_DICT = make_dict(ANDROID_TEST_PLATFORM_FALLBACK) +ANDROID_TEST_PLATFORM_ROLLOUT_DICT = make_dict(ANDROID_TEST_PLATFORM_ROLLOUT) +SOURCELESS_BINARY_FALLBACK_DICT = make_dict(SOURCELESS_BINARY_FALLBACK) +SOURCELESS_BINARY_ROLLOUT_DICT = make_dict(SOURCELESS_BINARY_ROLLOUT) +TEST_TO_INSTRUMENT_TEST_FALLBACK_DICT = make_dict(TEST_TO_INSTRUMENT_TEST_FALLBACK) +TEST_TO_INSTRUMENT_TEST_ROLLOUT_DICT = make_dict(TEST_TO_INSTRUMENT_TEST_ROLLOUT) +ALLOW_RESOURCE_CONFLICTS_DICT = make_dict(ALLOW_RESOURCE_CONFLICTS) +PARTIAL_JETIFICATION_TARGETS_ROLLOUT_DICT = make_dict(PARTIAL_JETIFICATION_TARGETS_ROLLOUT) +PARTIAL_JETIFICATION_TARGETS_FALLBACK_DICT = make_dict(PARTIAL_JETIFICATION_TARGETS_FALLBACK) +KT_ANDROID_LIBRARY_ROLLOUT_DICT = make_dict(KT_ANDROID_LIBRARY_ROLLOUT) +KT_ANDROID_LIBRARY_FALLBACK_DICT = make_dict(KT_ANDROID_LIBRARY_FALLBACK) +ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT_DICT = make_dict(ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT) +ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK_DICT = make_dict(ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK) + +def matches(fqn, dct): # Labels with workspace names ("@workspace//pkg:target") are not supported. if fqn.startswith("@"): return False @@ -337,11 +331,10 @@ acls = struct( in_partial_jetification_targets = _in_partial_jetification_targets, in_kt_android_library_rollout = _in_kt_android_library_rollout, in_android_instrumentation_test_manifest_check_rollout = _in_android_instrumentation_test_manifest_check_rollout, - in_android_instrumentation_test_derived_test_class_rollout = _in_android_instrumentation_test_derived_test_class_rollout, ) # Visible for testing testing = struct( - matches = _matches, - make_dict = _make_dict, + matches = matches, + make_dict = make_dict, ) diff --git a/rules/acls/android_instrumentation_derived_test_class_rollout.bzl b/rules/acls/android_instrumentation_derived_test_class_rollout.bzl index 782beb8..427490c 100644 --- a/rules/acls/android_instrumentation_derived_test_class_rollout.bzl +++ b/rules/acls/android_instrumentation_derived_test_class_rollout.bzl @@ -14,10 +14,18 @@ """Rollout list for enabling test class derivation in android_instrumentation_test,""" -ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_ROLLOUT = [ +load("@rules_android//rules:acls.bzl", "make_dict", "matches") + +_ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_ROLLOUT = [ "//:__subpackages__", ] -ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_FALLBACK = [ +_ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_FALLBACK = [ "//javatests/notinacl:__subpackages__", ] + +_ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_ROLLOUT_DICT = make_dict(_ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_ROLLOUT) +_ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_FALLBACK_DICT = make_dict(_ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_FALLBACK) + +def acls_in_android_instrumentation_test_derived_test_class_rollout(fqn): + return not matches(fqn, _ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_FALLBACK_DICT) and matches(fqn, _ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_ROLLOUT_DICT) -- cgit v1.2.3 From de0895a5bbc7040d453cdd3896fb5d7743ccad12 Mon Sep 17 00:00:00 2001 From: Olivier St-Onge Date: Mon, 6 Dec 2021 12:13:27 -0800 Subject: Add docs to some attrs PiperOrigin-RevId: 414509483 --- rules/attrs.bzl | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/rules/attrs.bzl b/rules/attrs.bzl index af389ff..5638607 100644 --- a/rules/attrs.bzl +++ b/rules/attrs.bzl @@ -102,14 +102,36 @@ _COMPILATION = _add( assets = attr.label_list( allow_files = True, cfg = "target", + doc = ("The list of assets to be packaged. This is typically a glob of " + + "all files under the assets directory. You can also reference " + + "other rules (any rule that produces files) or exported files in " + + "the other packages, as long as all those files are under the " + + "assets_dir directory in the corresponding package."), + ), + assets_dir = attr.string( + doc = ("The string giving the path to the files in assets. " + + "The pair assets and assets_dir describe packaged assets and either both " + + "attributes should be provided or none of them."), + ), + custom_package = attr.string( + doc = ("Java package for which java sources will be generated. " + + "By default the package is inferred from the directory where the BUILD file " + + "containing the rule is. You can specify a different package but this is " + + "highly discouraged since it can introduce classpath conflicts with other " + + "libraries that will only be detected at runtime."), ), - assets_dir = attr.string(), - custom_package = attr.string(), manifest = attr.label( allow_single_file = [".xml"], + doc = ("The name of the Android manifest file, normally " + + "AndroidManifest.xml. Must be defined if resource_files or assets are defined."), ), resource_files = attr.label_list( allow_files = True, + doc = ("The list of resources to be packaged. This " + + "is typically a glob of all files under the res directory. Generated files " + + "(from genrules) can be referenced by Label here as well. The only " + + "restriction is that the generated outputs must be under the same \"res\" " + + "directory as any other resource files that are included."), ), data = attr.label_list( allow_files = True, @@ -278,7 +300,6 @@ ANDROID_SDK_ATTRS = dict( ANDROID_TOOLS_DEFAULTS_JAR_ATTRS = _add(_ANDROID_SDK) - attrs = struct( ANDROID_SDK = _ANDROID_SDK, COMPILATION = _COMPILATION, -- cgit v1.2.3 From 2be629d742b2e8a775b30cd4c9da286d027b62bc Mon Sep 17 00:00:00 2001 From: Zhaoqing Xu Date: Mon, 6 Dec 2021 13:14:41 -0800 Subject: Add docs to some attrs PiperOrigin-RevId: 414523459 --- rules/attrs.bzl | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/rules/attrs.bzl b/rules/attrs.bzl index 5638607..bf00ae7 100644 --- a/rules/attrs.bzl +++ b/rules/attrs.bzl @@ -135,12 +135,31 @@ _COMPILATION = _add( ), data = attr.label_list( allow_files = True, + doc = ( + "Files needed by this rule at runtime. May list file or rule targets. Generally allows any target.\n\n" + + "The default outputs and runfiles of targets in the data attribute should appear in the *.runfiles area of" + + "any executable which is output by or has a runtime dependency on this target. " + + "This may include data files or binaries used when this target's srcs are executed. " + + "See the data dependencies section (https://g3doc.corp.google.com/devtools/blaze/g3doc/build-ref.html#data) " + + "for more information about how to depend on and use data files.\n\n" + + "New rules should define a data attribute if they process inputs which might use other inputs at runtime. " + + "Rules' implementation functions must also populate the target's runfiles from the outputs and runfiles of " + + "any data attribute, as well as runfiles from any dependency attribute which provides either source code or runtime " + + "dependencies." + ), ), plugins = attr.label_list( providers = [JavaPluginInfo], cfg = "host", + doc = ("Java compiler plugins to run at compile-time. " + + "Every java_plugin specified in the plugins attribute will be run whenever this target is built. " + + "Resources generated by the plugin will be included in the result jar of the target."), + ), + javacopts = attr.string_list( + doc = ("Extra compiler options for this target. " + + "Subject to \"Make variable\" substitution and Bourne shell tokenization.\n" + + "These compiler options are passed to javac after the global compiler options."), ), - javacopts = attr.string_list(), # TODO: Expose getPlugins() in JavaConfiguration.java # com/google/devtools/build/lib/rules/java/JavaConfiguration.java # com/google/devtools/build/lib/rules/java/JavaOptions.java -- cgit v1.2.3 From a3cd8de1de76c6b42339e72c58bd7d7961b911fb Mon Sep 17 00:00:00 2001 From: Zhaoqing Xu Date: Tue, 7 Dec 2021 15:23:27 -0800 Subject: Add docs to _COMPILATION attrs PiperOrigin-RevId: 414839545 --- rules/attrs.bzl | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/rules/attrs.bzl b/rules/attrs.bzl index bf00ae7..e7917b3 100644 --- a/rules/attrs.bzl +++ b/rules/attrs.bzl @@ -137,28 +137,36 @@ _COMPILATION = _add( allow_files = True, doc = ( "Files needed by this rule at runtime. May list file or rule targets. Generally allows any target.\n\n" + - "The default outputs and runfiles of targets in the data attribute should appear in the *.runfiles area of" + + "The default outputs and runfiles of targets in the `data` attribute should appear in the `*.runfiles` area of" + "any executable which is output by or has a runtime dependency on this target. " + - "This may include data files or binaries used when this target's srcs are executed. " + - "See the data dependencies section (https://g3doc.corp.google.com/devtools/blaze/g3doc/build-ref.html#data) " + + "This may include data files or binaries used when this target's " + + "[srcs](https://docs.bazel.build/versions/main/be/common-definitions.html#typical.srcs) are executed. " + + "See the [data dependencies](https://docs.bazel.build/versions/main/build-ref.html#data) section " + "for more information about how to depend on and use data files.\n\n" + - "New rules should define a data attribute if they process inputs which might use other inputs at runtime. " + - "Rules' implementation functions must also populate the target's runfiles from the outputs and runfiles of " + - "any data attribute, as well as runfiles from any dependency attribute which provides either source code or runtime " + - "dependencies." + "New rules should define a `data` attribute if they process inputs which might use other inputs at runtime. " + + "Rules' implementation functions must also " + + "[populate the target's runfiles](https://docs.bazel.build/versions/main/skylark/rules.html#runfiles) " + + "from the outputs and runfiles of any `data` attribute, as well as runfiles from any dependency attribute " + + "which provides either source code or runtime dependencies." ), ), plugins = attr.label_list( providers = [JavaPluginInfo], cfg = "host", - doc = ("Java compiler plugins to run at compile-time. " + - "Every java_plugin specified in the plugins attribute will be run whenever this target is built. " + - "Resources generated by the plugin will be included in the result jar of the target."), + doc = ( + "Java compiler plugins to run at compile-time. " + + "Every `java_plugin` specified in the plugins attribute will be run whenever this rule is built. " + + "A library may also inherit plugins from dependencies that use [exported_plugins](https://docs.bazel.build/versions/main/be/java.html#java_library.exported_plugins). " + + "Resources generated by the plugin will be included in the resulting jar of this rule." + ), ), javacopts = attr.string_list( - doc = ("Extra compiler options for this target. " + - "Subject to \"Make variable\" substitution and Bourne shell tokenization.\n" + - "These compiler options are passed to javac after the global compiler options."), + doc = ( + "Extra compiler options for this library. " + + "Subject to \"[Make variable](https://docs.bazel.build/versions/main/be/make-variables.html)\" substitution and " + + "[Bourne shell tokenization](https://docs.bazel.build/versions/main/be/common-definitions.html#sh-tokenization).\n" + + "These compiler options are passed to javac after the global compiler options." + ), ), # TODO: Expose getPlugins() in JavaConfiguration.java # com/google/devtools/build/lib/rules/java/JavaConfiguration.java -- cgit v1.2.3 From 4ed0ff1056b76a9d4df480b92bd2dcf28e3e1184 Mon Sep 17 00:00:00 2001 From: Olivier St-Onge Date: Tue, 7 Dec 2021 15:30:25 -0800 Subject: Add doc to aar_import attrs + rule example PiperOrigin-RevId: 414841255 --- rules/aar_import/attrs.bzl | 16 ++++++++++++---- rules/aar_import/rule.bzl | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/rules/aar_import/attrs.bzl b/rules/aar_import/attrs.bzl index 5ac9c7a..884c6af 100644 --- a/rules/aar_import/attrs.bzl +++ b/rules/aar_import/attrs.bzl @@ -24,15 +24,24 @@ ATTRS = _attrs.add( aar = attr.label( allow_single_file = [".aar"], mandatory = True, + doc = "The .aar file to process.", + ), + data = attr.label_list( + allow_files = True, + doc = "Files needed by this rule at runtime. May list file or rule " + + "targets. Generally allows any target.", ), - data = attr.label_list(allow_files = True), deps = attr.label_list( allow_files = False, providers = [JavaInfo], + doc = "The list of libraries to link against.", ), exports = attr.label_list( allow_files = False, allow_rules = ["aar_import", "java_import"], + doc = "The closure of all rules reached via `exports` attributes are considered " + + "direct dependencies of any rule that directly depends on the target with " + + "`exports`. The `exports` are not direct deps of the rule they belong to.", ), has_lint_jar = attr.bool( default = False, @@ -45,9 +54,8 @@ ATTRS = _attrs.add( ), srcjar = attr.label( allow_single_file = [".srcjar"], - doc = - "A srcjar file that contains the source code for the JVM " + - "artifacts stored within the AAR.", + doc = "A srcjar file that contains the source code for the JVM " + + "artifacts stored within the AAR.", ), _flags = attr.label( default = "@rules_android//rules/flags", diff --git a/rules/aar_import/rule.bzl b/rules/aar_import/rule.bzl index 02a1d61..bb52942 100644 --- a/rules/aar_import/rule.bzl +++ b/rules/aar_import/rule.bzl @@ -17,10 +17,26 @@ load(":attrs.bzl", _ATTRS = "ATTRS") load(":impl.bzl", _impl = "impl") +RULE_DOC = """ +#### Examples + +The following example shows how to use `aar_import`. +
aar_import(
+    name = "hellobazellib",
+    aar = "lib.aar",
+    package = "bazel.hellobazellib",
+    deps = [
+        "//java/bazel/hellobazellib/activities",
+        "//java/bazel/hellobazellib/common",
+    ],
+)
+""" + aar_import = rule( attrs = _ATTRS, fragments = ["android"], implementation = _impl, + doc = RULE_DOC, provides = [ AndroidIdeInfo, AndroidLibraryResourceClassJarProvider, -- cgit v1.2.3 From b2b8747ecab5867013b91c7cfc119d88501a6c37 Mon Sep 17 00:00:00 2001 From: Olivier St-Onge Date: Tue, 7 Dec 2021 15:38:12 -0800 Subject: Add doc to android_library attrs + rule examples PiperOrigin-RevId: 414843436 --- rules/android_library/attrs.bzl | 137 +++++++++++++++++++++++++++++++++++++--- rules/android_library/rule.bzl | 80 +++++++++++++++++++++++ 2 files changed, 208 insertions(+), 9 deletions(-) diff --git a/rules/android_library/attrs.bzl b/rules/android_library/attrs.bzl index 8e38b01..db7ceb7 100644 --- a/rules/android_library/attrs.bzl +++ b/rules/android_library/attrs.bzl @@ -26,30 +26,149 @@ ATTRS = _attrs.add( [CcInfo], [JavaInfo], ], + doc = ( + "The list of other libraries to link against. Permitted library types " + + "are: `android_library`, `java_library` with `android` constraint and " + + "`cc_library` wrapping or producing `.so` native libraries for the " + + "Android target platform." + ), + ), + enable_data_binding = attr.bool( + default = False, + doc = ( + "If true, this rule processes [data binding]" + + "(https://developer.android.com/topic/libraries/data-binding) " + + "expressions in layout resources included through the [resource_files]" + + "(https://docs.bazel.build/versions/main/be/android.html#android_binary.resource_files) " + + "attribute. Without this setting, data binding expressions produce build " + + "failures. To build an Android app with data binding, you must also do the following:" + + "\n\n1. Set this attribute for all Android rules that transitively depend on " + + "this one. This is because dependers inherit the rule's data binding " + + "expressions through resource merging. So they also need to build with " + + "data binding to parse those expressions." + + "\n\n2. Add a `deps =` entry for the data binding runtime library to all targets " + + "that set this attribute. The location of this library depends on your depot setup." + ), ), - enable_data_binding = attr.bool(default = False), exported_plugins = attr.label_list( providers = [ [JavaPluginInfo], ], cfg = "host", + doc = ( + "The list of [java_plugin](https://docs.bazel.build/versions/main/be/java.html#java_plugin)s " + + "(e.g. annotation processors) to export to libraries that directly depend on this library. " + + "The specified list of `java_plugin`s will be applied to any library which directly depends on " + + "this library, just as if that library had explicitly declared these labels in " + + "[plugins](#android_library-plugins)." + ), ), exports = attr.label_list( providers = [ [CcInfo], [JavaInfo], ], + doc = ( + "The closure of all rules reached via `exports` attributes are considered " + + "direct dependencies of any rule that directly depends on the target with " + + "`exports`. The `exports` are not direct deps of the rule they belong to." + ), + ), + exports_manifest = _attrs.tristate.create( + default = _attrs.tristate.no, + doc = ( + "Whether to export manifest entries to `android_binary` targets that " + + "depend on this target. `uses-permissions` attributes are never exported." + ), + ), + idl_import_root = attr.string( + doc = ( + "Package-relative path to the root of the java package tree containing idl " + + "sources included in this library. This path will be used as the import root " + + "when processing idl sources that depend on this library." + + "\n\n" + + "When `idl_import_root` is specified, both `idl_parcelables` and `idl_srcs` must " + + "be at the path specified by the java package of the object they represent " + + "under `idl_import_root`. When `idl_import_root` is not specified, both " + + "`idl_parcelables` and `idl_srcs` must be at the path specified by their " + + "package under a Java root. " + + "See [examples](#examples)" + ), + ), + idl_parcelables = attr.label_list( + allow_files = [".aidl"], + doc = ( + "List of Android IDL definitions to supply as imports. These files will " + + "be made available as imports for any `android_library` target that depends " + + "on this library, directly or via its transitive closure, but will not be " + + "translated to Java or compiled. Only `.aidl` files that correspond directly " + + "to `.java` sources in this library should be included (e.g., custom " + + "implementations of Parcelable), otherwise `idl_srcs` should be used." + + "\n\n" + + "These files must be placed appropriately for the aidl compiler to find " + + "them. See the description of [idl_import_root](#android_library-idl_import_root) " + + "for information about what this means." + ), + ), + idl_preprocessed = attr.label_list( + allow_files = [".aidl"], + doc = ( + "List of preprocessed Android IDL definitions to supply as imports. These " + + "files will be made available as imports for any `android_library` target " + + "that depends on this library, directly or via its transitive closure, but " + + "will not be translated to Java or compiled. Only preprocessed `.aidl` " + + "files that correspond directly to `.java` sources in this library should " + + "be included (e.g., custom implementations of Parcelable), otherwise use " + + "`idl_srcs` for Android IDL definitions that need to be translated to Java " + + "interfaces and use `idl_parcelable` for non-preprocessed AIDL files." + ), + ), + idl_srcs = attr.label_list( + allow_files = [".aidl"], + doc = ( + "List of Android IDL definitions to translate to Java interfaces. After " + + "the Java interfaces are generated, they will be compiled together with " + + "the contents of `srcs`. These files will be made available as imports " + + "for any `android_library` target that depends on this library, directly " + + "or via its transitive closure." + + "\n\n" + + "These files must be placed appropriately for the aidl compiler to find " + + "them. See the description of [idl_import_root](#android_library-idl_import_root) " + + "for information about what this means." + ), + ), + neverlink = attr.bool( + default = False, + doc = ( + "Only use this library for compilation and not at runtime. The outputs " + + "of a rule marked as neverlink will not be used in `.apk` creation. " + + "Useful if the library will be provided by the runtime environment during execution." + ), + ), + proguard_specs = attr.label_list( + allow_files = True, + doc = ( + "Files to be used as Proguard specification. These will describe the set " + + "of specifications to be used by Proguard. If specified, they will be " + + "added to any `android_binary` target depending on this library. The " + + "files included here must only have idempotent rules, namely -dontnote, " + + "-dontwarn, assumenosideeffects, and rules that start with -keep. Other " + + "options can only appear in `android_binary`'s proguard_specs, to " + + "ensure non-tautological merges." + ), ), - exports_manifest = - _attrs.tristate.create(default = _attrs.tristate.no), - idl_import_root = attr.string(), - idl_parcelables = attr.label_list(allow_files = [".aidl"]), - idl_preprocessed = attr.label_list(allow_files = [".aidl"]), - idl_srcs = attr.label_list(allow_files = [".aidl"]), - neverlink = attr.bool(default = False), - proguard_specs = attr.label_list(allow_files = True), srcs = attr.label_list( allow_files = [".java", ".srcjar"], + doc = ( + "The list of `.java` or `.srcjar` files that are processed to create the " + + "target. `srcs` files of type `.java` are compiled. For *readability's " + + "sake*, it is not good to put the name of a generated `.java` source " + + "file into the `srcs`. Instead, put the depended-on rule name in the `srcs`, " + + "as described below." + + "\n\n" + + "`srcs` files of type `.srcjar` are unpacked and compiled. (This is useful " + + "if you need to generate a set of `.java` files with a genrule or build extension.)" + ), ), # TODO(b/127517031): Remove these entries once fixed. _defined_assets = attr.bool(default = False), diff --git a/rules/android_library/rule.bzl b/rules/android_library/rule.bzl index 02cc1fc..e421218 100644 --- a/rules/android_library/rule.bzl +++ b/rules/android_library/rule.bzl @@ -22,6 +22,85 @@ load( _attrs = "attrs", ) +_RULE_DOC = """ +#### Examples + +The following example shows how to use android libraries with resources. +
android_library(
+    name = "hellobazellib",
+    srcs = glob(["*.java"]),
+    resource_files = glob(["res/**/*"]),
+    manifest = "AndroidManifest.xml",
+    deps = [
+        "//java/bazel/hellobazellib/activities",
+        "//java/bazel/hellobazellib/common",
+        "//java/bazel/hellobazellib/math",
+        "//java/bazel/hellobazellib/service",
+    ],
+)
+ +The following example shows how to set `idl_import_root`. Let //java/bazel/helloandroid/BUILD contain: +
android_library(
+    name = "parcelable",
+    srcs = ["MyParcelable.java"], # bazel.helloandroid.MyParcelable
+    # MyParcelable.aidl will be used as import for other .aidl
+    # files that depend on it, but will not be compiled.
+    idl_parcelables = ["MyParcelable.aidl"] # bazel.helloandroid.MyParcelable
+    # We don't need to specify idl_import_root since the aidl file
+    # which declares bazel.helloandroid.MyParcelable
+    # is present at java/bazel/helloandroid/MyParcelable.aidl
+    # underneath a java root (java/).
+)
+android_library(
+    name = "foreign_parcelable",
+    srcs = ["src/android/helloandroid/OtherParcelable.java"], # android.helloandroid.OtherParcelable
+    idl_parcelables = [
+        "src/android/helloandroid/OtherParcelable.aidl" # android.helloandroid.OtherParcelable
+    ],
+    # We need to specify idl_import_root because the aidl file which
+    # declares android.helloandroid.OtherParcelable is not positioned
+    # at android/helloandroid/OtherParcelable.aidl under a normal java root.
+    # Setting idl_import_root to "src" in //java/bazel/helloandroid
+    # adds java/bazel/helloandroid/src to the list of roots
+    # the aidl compiler will search for imported types.
+    idl_import_root = "src",
+)
+\\# Here, OtherInterface.aidl has an "import android.helloandroid.CallbackInterface;" statement.
+android_library(
+    name = "foreign_interface",
+    idl_srcs = [
+        "src/android/helloandroid/OtherInterface.aidl" # android.helloandroid.OtherInterface
+        "src/android/helloandroid/CallbackInterface.aidl" # android.helloandroid.CallbackInterface
+    ],
+    # As above, idl_srcs which are not correctly positioned under a java root
+    # must have idl_import_root set. Otherwise, OtherInterface (or any other
+    # interface in a library which depends on this one) will not be able
+    # to find CallbackInterface when it is imported.
+    idl_import_root = "src",
+)
+\\# MyParcelable.aidl is imported by MyInterface.aidl, so the generated
+\\# MyInterface.java requires MyParcelable.class at compile time.
+\\# Depending on :parcelable ensures that aidl compilation of MyInterface.aidl
+\\# specifies the correct import roots and can access MyParcelable.aidl, and
+\\# makes MyParcelable.class available to Java compilation of MyInterface.java
+\\# as usual.
+android_library(
+    name = "idl",
+    idl_srcs = ["MyInterface.aidl"],
+    deps = [":parcelable"],
+)
+\\# Here, ServiceParcelable uses and thus depends on ParcelableService,
+\\# when it's compiled, but ParcelableService also uses ServiceParcelable,
+\\# which creates a circular dependency.
+\\# As a result, these files must be compiled together, in the same android_library.
+android_library(
+    name = "circular_dependencies",
+    srcs = ["ServiceParcelable.java"],
+    idl_srcs = ["ParcelableService.aidl"],
+    idl_parcelables = ["ServiceParcelable.aidl"],
+)
+""" + def _outputs(name, _package_name, _defined_local_resources): outputs = dict( lib_jar = "lib%{name}.jar", @@ -70,6 +149,7 @@ def make_rule( "java", ], implementation = implementation, + doc = _RULE_DOC, provides = [ AndroidCcLinkParamsInfo, AndroidIdeInfo, -- cgit v1.2.3 From ed101c2110a8f0c52731f55186351cc258d5feec Mon Sep 17 00:00:00 2001 From: Tim Peut Date: Wed, 5 Jan 2022 17:11:04 -0800 Subject: Remove old, unused sign_apk method. PiperOrigin-RevId: 419944514 --- rules/common.bzl | 41 +---------------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/rules/common.bzl b/rules/common.bzl index 6e84559..4ff8a23 100644 --- a/rules/common.bzl +++ b/rules/common.bzl @@ -15,7 +15,7 @@ """Bazel common library for the Android rules.""" load(":java.bzl", _java = "java") -load(":utils.bzl", "get_android_sdk", "get_android_toolchain", _log = "log") +load(":utils.bzl", "get_android_toolchain", _log = "log") # TODO(ostonge): Remove once kotlin/jvm_library.internal.bzl # is updated and released to use the java.resolve_package function @@ -45,44 +45,6 @@ def _get_host_javabase(ctx): _log.error("Missing _host_javabase attr") return ctx.attr._host_javabase -def _sign_apk(ctx, unsigned_apk, signed_apk, keystore = None, signing_keys = [], signing_lineage = None): - """Signs an apk. Usage of keystore is deprecated. Prefer using signing_keys.""" - inputs = [unsigned_apk] - signer_args = ctx.actions.args() - signer_args.add("sign") - - if signing_keys: - inputs.extend(signing_keys) - for i, key in enumerate(signing_keys): - if i > 0: - signer_args.add("--next-signer") - signer_args.add("--ks") - signer_args.add(key.path) - signer_args.add("--ks-pass") - signer_args.add("pass:android") - if signing_lineage: - inputs.append(signing_lineage) - signer_args.add("--lineage", signing_lineage.path) - elif keystore: - inputs.append(keystore) - signer_args.add("--ks", keystore.path) - signer_args.add("--ks-pass", "pass:android") - - signer_args.add("--v1-signing-enabled", ctx.fragments.android.apk_signing_method_v1) - signer_args.add("--v1-signer-name", "CERT") - signer_args.add("--v2-signing-enabled", ctx.fragments.android.apk_signing_method_v2) - signer_args.add("--out", signed_apk.path) - signer_args.add(unsigned_apk.path) - ctx.actions.run( - executable = get_android_sdk(ctx).apk_signer, - inputs = inputs, - outputs = [signed_apk], - arguments = [signer_args], - mnemonic = "ApkSignerTool", - progress_message = "Signing APK for %s" % unsigned_apk.path, - ) - return signed_apk - def _filter_zip(ctx, in_zip, out_zip, filters = []): """Creates a copy of a zip file with files that match filters.""" args = ctx.actions.args() @@ -107,5 +69,4 @@ common = struct( get_java_toolchain = _get_java_toolchain, filter_zip = _filter_zip, java_package = _java_package, - sign_apk = _sign_apk, ) -- cgit v1.2.3 From 934502082e25c8c3f6873035b87a30cb8fb5193a Mon Sep 17 00:00:00 2001 From: Tim Peut Date: Tue, 11 Jan 2022 14:01:19 -0800 Subject: Propagate visiblity to android_application PiperOrigin-RevId: 421119357 Change-Id: I438258e59702ca092e2bfe36a1e0b076549fd638 --- rules/android_application/android_application_rule.bzl | 1 + 1 file changed, 1 insertion(+) diff --git a/rules/android_application/android_application_rule.bzl b/rules/android_application/android_application_rule.bzl index ab26456..396e97b 100644 --- a/rules/android_application/android_application_rule.bzl +++ b/rules/android_application/android_application_rule.bzl @@ -382,4 +382,5 @@ def android_application_macro(_android_binary, **attrs): transitive_configs = attrs.get("transitive_configs", []), feature_modules = feature_modules, application_id = attrs["manifest_values"]["applicationId"], + visibility = attrs.get("visibility", None), ) -- cgit v1.2.3 From e559df834d012bd3b5cb839f5d653971fe7b5a87 Mon Sep 17 00:00:00 2001 From: Alex Humesky Date: Tue, 18 Jan 2022 14:00:45 -0800 Subject: Add stub presubmit script for Kokoro. PiperOrigin-RevId: 422641960 Change-Id: Ie98faa9166554a665c4d3f08dadc42c9a2e71712 --- kokoro/presubmit/kokoro_presubmit.sh | 17 +++++++++++++++++ kokoro/presubmit/presubmit.cfg | 16 ++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 kokoro/presubmit/kokoro_presubmit.sh create mode 100644 kokoro/presubmit/presubmit.cfg diff --git a/kokoro/presubmit/kokoro_presubmit.sh b/kokoro/presubmit/kokoro_presubmit.sh new file mode 100644 index 0000000..ce4008d --- /dev/null +++ b/kokoro/presubmit/kokoro_presubmit.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# TODO(ahumesky): Fill in test +exit 0 diff --git a/kokoro/presubmit/presubmit.cfg b/kokoro/presubmit/presubmit.cfg new file mode 100644 index 0000000..7536277 --- /dev/null +++ b/kokoro/presubmit/presubmit.cfg @@ -0,0 +1,16 @@ + +# The version of bazel to use to test the Starlark Android Rules. +# Update this as newer versions of bazel are released. +build_params { + key: "bazel_version" + value: "4.0.0" +} + +env_vars { + key: "bazel_version" + value: "$[bazel_version]" +} + +gfile_resources: "/x20/teams/bazel/releases/bazel-$[bazel_version]-linux-x86_64" + +build_file: "rules_android/kokoro/presubmit/kokoro_presubmit.sh" \ No newline at end of file -- cgit v1.2.3 From 3ff638215eb85b6d5b04aed9593c33f38534f607 Mon Sep 17 00:00:00 2001 From: Tim Peut Date: Thu, 20 Jan 2022 17:37:20 -0800 Subject: Introduce ACL and check for using a prebuilt test apk in a_i_t. PiperOrigin-RevId: 423197978 Change-Id: I641eb7f8c0e954d41cd7c2bc4d16bf633912f1e5 --- rules/acls.bzl | 6 ++++++ ...android_instrumentation_test_prebuilt_test_apk.bzl | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 rules/acls/android_instrumentation_test_prebuilt_test_apk.bzl diff --git a/rules/acls.bzl b/rules/acls.bzl index 3b6f67b..c6c10cb 100644 --- a/rules/acls.bzl +++ b/rules/acls.bzl @@ -68,6 +68,7 @@ load( ) load("@rules_android//rules/acls:kt_android_library_rollout.bzl", "KT_ANDROID_LIBRARY_FALLBACK", "KT_ANDROID_LIBRARY_ROLLOUT") load("@rules_android//rules/acls:android_instrumentation_test_manifest_check_rollout.bzl", "ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK", "ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT") +load("@rules_android//rules/acls:android_instrumentation_test_prebuilt_test_apk.bzl", "ANDROID_INSTRUMENTATION_TEST_PREBUILT_TEST_APK") def _in_aar_import_deps_checker(fqn): return not matches(fqn, AAR_IMPORT_DEPS_CHECKER_FALLBACK_DICT) and matches(fqn, AAR_IMPORT_DEPS_CHECKER_ROLLOUT_DICT) @@ -188,6 +189,9 @@ def _in_kt_android_library_rollout(fqn): def _in_android_instrumentation_test_manifest_check_rollout(fqn): return not matches(fqn, ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK_DICT) and matches(fqn, ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT_DICT) +def _in_android_instrumentation_test_prebuilt_test_apk(fqn): + return matches(fqn, ANDROID_INSTRUMENTATION_TEST_PREBUILT_TEST_APK_DICT) + def make_dict(lst): """Do not use this method outside of acls directory.""" return {t: True for t in lst} @@ -255,6 +259,7 @@ KT_ANDROID_LIBRARY_ROLLOUT_DICT = make_dict(KT_ANDROID_LIBRARY_ROLLOUT) KT_ANDROID_LIBRARY_FALLBACK_DICT = make_dict(KT_ANDROID_LIBRARY_FALLBACK) ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT_DICT = make_dict(ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT) ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK_DICT = make_dict(ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK) +ANDROID_INSTRUMENTATION_TEST_PREBUILT_TEST_APK_DICT = make_dict(ANDROID_INSTRUMENTATION_TEST_PREBUILT_TEST_APK) def matches(fqn, dct): # Labels with workspace names ("@workspace//pkg:target") are not supported. @@ -331,6 +336,7 @@ acls = struct( in_partial_jetification_targets = _in_partial_jetification_targets, in_kt_android_library_rollout = _in_kt_android_library_rollout, in_android_instrumentation_test_manifest_check_rollout = _in_android_instrumentation_test_manifest_check_rollout, + in_android_instrumentation_test_prebuilt_test_apk = _in_android_instrumentation_test_prebuilt_test_apk, ) # Visible for testing diff --git a/rules/acls/android_instrumentation_test_prebuilt_test_apk.bzl b/rules/acls/android_instrumentation_test_prebuilt_test_apk.bzl new file mode 100644 index 0000000..64aaa17 --- /dev/null +++ b/rules/acls/android_instrumentation_test_prebuilt_test_apk.bzl @@ -0,0 +1,19 @@ +# Copyright 2022 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list of a_i_t targets allowed to use a prebuilt test apk.""" + +# keep sorted +ANDROID_INSTRUMENTATION_TEST_PREBUILT_TEST_APK = [ +] -- cgit v1.2.3 From 0bdd0f77ed0b40a8d016f9dab3c5486b6a110838 Mon Sep 17 00:00:00 2001 From: Olivier St-Onge Date: Wed, 9 Feb 2022 09:42:11 -0800 Subject: Internal PiperOrigin-RevId: 427495075 Change-Id: Ie0ed78b242484fbe5b4098b38b52342add3b4274 --- rules/common.bzl | 7 ------- 1 file changed, 7 deletions(-) diff --git a/rules/common.bzl b/rules/common.bzl index 4ff8a23..3d7ceff 100644 --- a/rules/common.bzl +++ b/rules/common.bzl @@ -14,14 +14,8 @@ """Bazel common library for the Android rules.""" -load(":java.bzl", _java = "java") load(":utils.bzl", "get_android_toolchain", _log = "log") -# TODO(ostonge): Remove once kotlin/jvm_library.internal.bzl -# is updated and released to use the java.resolve_package function -def _java_package(label, custom_package): - return _java.resolve_package_from_label(label, custom_package) - # Validates that the packages listed under "deps" all have the given constraint. If a package # does not have this attribute, an error is generated. def _validate_constraints(targets, constraint): @@ -68,5 +62,4 @@ common = struct( get_host_javabase = _get_host_javabase, get_java_toolchain = _get_java_toolchain, filter_zip = _filter_zip, - java_package = _java_package, ) -- cgit v1.2.3 From 8ec24cf93433df4e9334c49bc9fd32eb933581fb Mon Sep 17 00:00:00 2001 From: Tim Peut Date: Tue, 15 Feb 2022 17:31:47 -0800 Subject: Support key rotation in bundle deploy script PiperOrigin-RevId: 428915956 Change-Id: Ic30f367e6758cc7cf17654ed9a969cf739f7d269 --- .../android_application_rule.bzl | 28 ++++++++++++++-- rules/android_application/attrs.bzl | 4 +++ .../android_application/bundle_deploy.sh_template | 38 ++++++++++++++++------ 3 files changed, 57 insertions(+), 13 deletions(-) diff --git a/rules/android_application/android_application_rule.bzl b/rules/android_application/android_application_rule.bzl index 396e97b..c5b4d68 100644 --- a/rules/android_application/android_application_rule.bzl +++ b/rules/android_application/android_application_rule.bzl @@ -217,6 +217,15 @@ def _create_feature_manifest( return manifest +def _create_signer_properties(ctx, oldest_key): + properties = ctx.actions.declare_file("%s/keystore.properties" % ctx.label.name) + ctx.actions.expand_template( + template = ctx.file._bundle_keystore_properties, + output = properties, + substitutions = {"%oldest_key%": oldest_key.short_path}, + ) + return properties + def _impl(ctx): # Convert base apk to .proto_ap_ base_apk = ctx.attr.base_module[ApkInfo].unsigned_apk @@ -284,11 +293,25 @@ def _impl(ctx): ) # Create `blaze run` script + base_apk_info = ctx.attr.base_module[ApkInfo] + deploy_script_files = [base_apk_info.signing_keys[-1]] subs = { "%bundletool_path%": get_android_toolchain(ctx).bundletool.files_to_run.executable.short_path, "%aab%": ctx.outputs.unsigned_aab.short_path, - "%key%": ctx.attr.base_module[ApkInfo].signing_keys[0].short_path, + "%newest_key%": base_apk_info.signing_keys[-1].short_path, } + if base_apk_info.signing_lineage: + signer_properties = _create_signer_properties(ctx, base_apk_info.signing_keys[0]) + subs["%oldest_signer_properties%"] = signer_properties.short_path + subs["%lineage%"] = base_apk_info.signing_lineage.short_path + subs["%min_rotation_api%"] = base_apk_info.signing_min_v3_rotation_api_version + deploy_script_files.extend( + [signer_properties, base_apk_info.signing_lineage, base_apk_info.signing_keys[0]], + ) + else: + subs["%oldest_signer_properties%"] = "" + subs["%lineage%"] = "" + subs["%min_rotation_api%"] = "" ctx.actions.expand_template( template = ctx.file._bundle_deploy, output = ctx.outputs.deploy_script, @@ -304,9 +327,8 @@ def _impl(ctx): executable = ctx.outputs.deploy_script, runfiles = ctx.runfiles([ ctx.outputs.unsigned_aab, - ctx.attr.base_module[ApkInfo].signing_keys[0], get_android_toolchain(ctx).bundletool.files_to_run.executable, - ]), + ] + deploy_script_files), ), ] diff --git a/rules/android_application/attrs.bzl b/rules/android_application/attrs.bzl index 33e43fb..330e20b 100644 --- a/rules/android_application/attrs.bzl +++ b/rules/android_application/attrs.bzl @@ -46,6 +46,10 @@ ANDROID_APPLICATION_ATTRS = _attrs.add( allow_single_file = True, default = ":bundle_deploy.sh_template", ), + _bundle_keystore_properties = attr.label( + allow_single_file = True, + default = ":bundle_keystore_properties.tmpl", + ), _feature_manifest_script = attr.label( allow_single_file = True, cfg = "host", diff --git a/rules/android_application/bundle_deploy.sh_template b/rules/android_application/bundle_deploy.sh_template index 37f6d4d..362a8ea 100644 --- a/rules/android_application/bundle_deploy.sh_template +++ b/rules/android_application/bundle_deploy.sh_template @@ -2,7 +2,10 @@ bundletool="%bundletool_path%" aab="%aab%" -key="%key%" +oldest_signer_properties="%oldest_signer_properties%" +newest_key="%newest_key%" +lineage="%lineage%" +min_rotation_api="%min_rotation_api%" tmp="$(mktemp /tmp/XXXXbundle.apks)" function cleanup { @@ -10,15 +13,30 @@ function cleanup { } trap cleanup EXIT -java -jar "$bundletool" build-apks \ - --bundle="$aab" \ - --output="$tmp" \ - --overwrite \ - --local-testing \ - --ks="$key" \ - --ks-pass=pass:android \ - --ks-key-alias=androiddebugkey \ - --key-pass=pass:android || exit +args=( + --bundle="$aab" + --output="$tmp" + --overwrite + --local-testing + --ks="$newest_key" + --ks-pass=pass:android + --ks-key-alias=androiddebugkey + --key-pass=pass:android +) + +if [[ ! -z "$lineage" ]]; then + args+=(--lineage="$lineage") +fi + +if [[ ! -z "$oldest_signer_properties" ]]; then + args+=(--oldest-signer="$oldest_signer_properties") +fi + +if [[ ! -z "$min_rotation_api" ]]; then + args+=(--min-v3-rotation-api-version="$min_rotation_api") +fi + +java -jar "$bundletool" build-apks "${args[@]}" || exit java -jar "$bundletool" install-apks \ --adb="$(which adb)" \ -- cgit v1.2.3 From acd08991654190bd677c6ef73d734617cff81287 Mon Sep 17 00:00:00 2001 From: Tim Peut Date: Wed, 16 Feb 2022 22:11:56 -0800 Subject: Support key rotation in bundle_to_apks. Also switch android_application to pass --rotation-min-sdk-version PiperOrigin-RevId: 429221075 Change-Id: Icd47313c4af6b487adbade67582d48c96b5becf2 --- rules/android_application/android_application_rule.bzl | 11 +---------- rules/android_application/attrs.bzl | 2 +- rules/android_application/bundle_deploy.sh_template | 2 +- rules/bundletool.bzl | 17 +++++++++++++++++ rules/common.bzl | 10 ++++++++++ 5 files changed, 30 insertions(+), 12 deletions(-) diff --git a/rules/android_application/android_application_rule.bzl b/rules/android_application/android_application_rule.bzl index c5b4d68..760faf0 100644 --- a/rules/android_application/android_application_rule.bzl +++ b/rules/android_application/android_application_rule.bzl @@ -217,15 +217,6 @@ def _create_feature_manifest( return manifest -def _create_signer_properties(ctx, oldest_key): - properties = ctx.actions.declare_file("%s/keystore.properties" % ctx.label.name) - ctx.actions.expand_template( - template = ctx.file._bundle_keystore_properties, - output = properties, - substitutions = {"%oldest_key%": oldest_key.short_path}, - ) - return properties - def _impl(ctx): # Convert base apk to .proto_ap_ base_apk = ctx.attr.base_module[ApkInfo].unsigned_apk @@ -301,7 +292,7 @@ def _impl(ctx): "%newest_key%": base_apk_info.signing_keys[-1].short_path, } if base_apk_info.signing_lineage: - signer_properties = _create_signer_properties(ctx, base_apk_info.signing_keys[0]) + signer_properties = _common.create_signer_properties(ctx, base_apk_info.signing_keys[0]) subs["%oldest_signer_properties%"] = signer_properties.short_path subs["%lineage%"] = base_apk_info.signing_lineage.short_path subs["%min_rotation_api%"] = base_apk_info.signing_min_v3_rotation_api_version diff --git a/rules/android_application/attrs.bzl b/rules/android_application/attrs.bzl index 330e20b..de16964 100644 --- a/rules/android_application/attrs.bzl +++ b/rules/android_application/attrs.bzl @@ -48,7 +48,7 @@ ANDROID_APPLICATION_ATTRS = _attrs.add( ), _bundle_keystore_properties = attr.label( allow_single_file = True, - default = ":bundle_keystore_properties.tmpl", + default = "@rules_android//rules:bundle_keystore_properties.tmpl", ), _feature_manifest_script = attr.label( allow_single_file = True, diff --git a/rules/android_application/bundle_deploy.sh_template b/rules/android_application/bundle_deploy.sh_template index 362a8ea..bcc1f8f 100644 --- a/rules/android_application/bundle_deploy.sh_template +++ b/rules/android_application/bundle_deploy.sh_template @@ -33,7 +33,7 @@ if [[ ! -z "$oldest_signer_properties" ]]; then fi if [[ ! -z "$min_rotation_api" ]]; then - args+=(--min-v3-rotation-api-version="$min_rotation_api") + args+=(--rotation-min-sdk-version="$min_rotation_api") fi java -jar "$bundletool" build-apks "${args[@]}" || exit diff --git a/rules/bundletool.bzl b/rules/bundletool.bzl index 161882a..abb305e 100644 --- a/rules/bundletool.bzl +++ b/rules/bundletool.bzl @@ -14,6 +14,7 @@ """Bazel Bundletool Commands.""" +load(":common.bzl", _common = "common") load(":java.bzl", _java = "java") _density_mapping = { @@ -199,6 +200,9 @@ def _bundle_to_apks( universal = False, device_spec = None, keystore = None, + oldest_signer = None, + lineage = None, + rotation_min_sdk = None, modules = None, aapt2 = None, bundletool = None, @@ -219,6 +223,19 @@ def _bundle_to_apks( args.add("--ks-key-alias", "AndroidDebugKey") inputs.append(keystore) + if lineage: + if not oldest_signer: + fail("Key rotation requires oldest_signer in %s" % ctx.label) + oldest_signer_properties = _common.create_signer_properties(ctx, oldest_signer) + args.add("--oldest-signer", oldest_signer_properties.path) + args.add("--lineage", lineage.short_path) + inputs.append(oldest_signer_properties) + inputs.append(oldest_signer) + inputs.append(lineage) + + if rotation_min_sdk: + args.add("--rotation-min-sdk-version", rotation_min_sdk) + if device_spec: args.add("--device-spec", device_spec) inputs.append(device_spec) diff --git a/rules/common.bzl b/rules/common.bzl index 3d7ceff..c507bb4 100644 --- a/rules/common.bzl +++ b/rules/common.bzl @@ -57,8 +57,18 @@ def _filter_zip(ctx, in_zip, out_zip, filters = []): progress_message = "Filtering %s" % in_zip.short_path, ) +def _create_signer_properties(ctx, oldest_key): + properties = ctx.actions.declare_file("%s/keystore.properties" % ctx.label.name) + ctx.actions.expand_template( + template = ctx.file._bundle_keystore_properties, + output = properties, + substitutions = {"%oldest_key%": oldest_key.short_path}, + ) + return properties + common = struct( check_rule = _check_rule, + create_signer_properties = _create_signer_properties, get_host_javabase = _get_host_javabase, get_java_toolchain = _get_java_toolchain, filter_zip = _filter_zip, -- cgit v1.2.3 From 18072e52037caf6e7487db2341b460ecbb642b32 Mon Sep 17 00:00:00 2001 From: Alex Humesky Date: Tue, 22 Feb 2022 18:27:47 -0800 Subject: Add compiler_annotation_processor so that the enclosing workspace doesn't need to define it. PiperOrigin-RevId: 430340390 Change-Id: Ied847fa72d8e149be996e6ea217d2a571feac495 --- toolchains/android/toolchain.bzl | 2 +- tools/android/BUILD | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/toolchains/android/toolchain.bzl b/toolchains/android/toolchain.bzl index 42cdf9d..9f929ba 100644 --- a/toolchains/android/toolchain.bzl +++ b/toolchains/android/toolchain.bzl @@ -104,7 +104,7 @@ _ATTRS = dict( ), data_binding_annotation_processor = attr.label( cfg = "exec", - default = "@//tools/android:compiler_annotation_processor", # TODO: processor rules should be moved into rules_android + default = "//tools/android:compiler_annotation_processor", ), data_binding_annotation_template = attr.label( default = "//rules:data_binding_annotation_template.txt", diff --git a/tools/android/BUILD b/tools/android/BUILD index cb8148a..7a85b59 100644 --- a/tools/android/BUILD +++ b/tools/android/BUILD @@ -30,3 +30,13 @@ java_binary( visibility = ["//visibility:public"], runtime_deps = ["@rules_android_maven//:com_android_tools_build_bundletool"], ) + +java_plugin( + name = "compiler_annotation_processor", + generates_api = True, + processor_class = "android.databinding.annotationprocessor.ProcessDataBinding", + visibility = ["//visibility:public"], + deps = [ + "@bazel_tools//src/tools/android/java/com/google/devtools/build/android:all_android_tools", + ], +) -- cgit v1.2.3 From eca9b80bf04e500bf16cbe9019109b2d7efae02b Mon Sep 17 00:00:00 2001 From: A Googler Date: Wed, 23 Feb 2022 15:45:30 -0800 Subject: Internal change PiperOrigin-RevId: 430556376 Change-Id: I340d8c414b35cba975266f2aa848ecd7759c2019 --- rules/aar_import/attrs.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/aar_import/attrs.bzl b/rules/aar_import/attrs.bzl index 884c6af..60b329b 100644 --- a/rules/aar_import/attrs.bzl +++ b/rules/aar_import/attrs.bzl @@ -38,7 +38,7 @@ ATTRS = _attrs.add( ), exports = attr.label_list( allow_files = False, - allow_rules = ["aar_import", "java_import"], + allow_rules = ["aar_import", "java_import", "kt_jvm_import"], doc = "The closure of all rules reached via `exports` attributes are considered " + "direct dependencies of any rule that directly depends on the target with " + "`exports`. The `exports` are not direct deps of the rule they belong to.", -- cgit v1.2.3 From 1543ea8ff3f69e044ade9dc78f859620ecfd48e4 Mon Sep 17 00:00:00 2001 From: Alex Humesky Date: Fri, 25 Feb 2022 15:28:21 -0800 Subject: Add a basic presubmit test for the Android Starlark rules. PiperOrigin-RevId: 431042952 Change-Id: I1e70ac4624f017d852d922c690927f049fd1e634 --- examples/basicapp/WORKSPACE | 39 +++++++++++++++++++ .../basicapp/java/com/basicapp/AndroidManifest.xml | 22 +++++++++++ examples/basicapp/java/com/basicapp/BUILD | 14 +++++++ .../basicapp/java/com/basicapp/BasicActivity.java | 40 ++++++++++++++++++++ .../com/basicapp/res/drawable-hdpi/ic_launcher.png | Bin 0 -> 1678 bytes .../com/basicapp/res/drawable-mdpi/ic_launcher.png | Bin 0 -> 1283 bytes .../basicapp/res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 1817 bytes .../basicapp/res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 2137 bytes .../com/basicapp/res/layout/basic_activity.xml | 12 ++++++ .../basicapp/java/com/basicapp/res/menu/menu.xml | 8 ++++ .../java/com/basicapp/res/values/dimens.xml | 5 +++ .../java/com/basicapp/res/values/strings.xml | 8 ++++ kokoro/presubmit/kokoro_presubmit.sh | 42 +++++++++++++++++++-- kokoro/presubmit/presubmit.cfg | 2 +- 14 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 examples/basicapp/WORKSPACE create mode 100644 examples/basicapp/java/com/basicapp/AndroidManifest.xml create mode 100644 examples/basicapp/java/com/basicapp/BUILD create mode 100644 examples/basicapp/java/com/basicapp/BasicActivity.java create mode 100644 examples/basicapp/java/com/basicapp/res/drawable-hdpi/ic_launcher.png create mode 100644 examples/basicapp/java/com/basicapp/res/drawable-mdpi/ic_launcher.png create mode 100644 examples/basicapp/java/com/basicapp/res/drawable-xhdpi/ic_launcher.png create mode 100644 examples/basicapp/java/com/basicapp/res/drawable-xxhdpi/ic_launcher.png create mode 100644 examples/basicapp/java/com/basicapp/res/layout/basic_activity.xml create mode 100644 examples/basicapp/java/com/basicapp/res/menu/menu.xml create mode 100644 examples/basicapp/java/com/basicapp/res/values/dimens.xml create mode 100644 examples/basicapp/java/com/basicapp/res/values/strings.xml diff --git a/examples/basicapp/WORKSPACE b/examples/basicapp/WORKSPACE new file mode 100644 index 0000000..12ed5b3 --- /dev/null +++ b/examples/basicapp/WORKSPACE @@ -0,0 +1,39 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +android_sdk_repository( + name = "androidsdk", +) + +android_ndk_repository( + name = "androidndk", +) + +http_archive( + name = "rules_jvm_external", + strip_prefix = "rules_jvm_external-fa73b1a8e4846cee88240d0019b8f80d39feb1c3", + sha256 = "7e13e48b50f9505e8a99cc5a16c557cbe826e9b68d733050cd1e318d69f94bb5", + url = "https://github.com/bazelbuild/rules_jvm_external/archive/fa73b1a8e4846cee88240d0019b8f80d39feb1c3.zip", +) + +http_archive( + name = "bazel_skylib", + urls = [ + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz", + ], + sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c", +) +load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") +bazel_skylib_workspace() + +local_repository( + name = "rules_android", + path = "../..", # rules_android's WORKSPACE relative to this inner workspace +) +load("@rules_android//:defs.bzl", "rules_android_workspace") +rules_android_workspace() +register_toolchains( + "@rules_android//toolchains/android:android_default_toolchain", + "@rules_android//toolchains/android_sdk:android_sdk_tools", +) + diff --git a/examples/basicapp/java/com/basicapp/AndroidManifest.xml b/examples/basicapp/java/com/basicapp/AndroidManifest.xml new file mode 100644 index 0000000..d9b0640 --- /dev/null +++ b/examples/basicapp/java/com/basicapp/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + diff --git a/examples/basicapp/java/com/basicapp/BUILD b/examples/basicapp/java/com/basicapp/BUILD new file mode 100644 index 0000000..adcb05b --- /dev/null +++ b/examples/basicapp/java/com/basicapp/BUILD @@ -0,0 +1,14 @@ +load("@rules_android//rules:rules.bzl", "android_binary", "android_library") + +android_binary( + name = "basic_app", + manifest = "AndroidManifest.xml", + deps = [":basic_lib"], +) + +android_library( + name = "basic_lib", + srcs = ["BasicActivity.java"], + manifest = "AndroidManifest.xml", + resource_files = glob(["res/**"]), +) diff --git a/examples/basicapp/java/com/basicapp/BasicActivity.java b/examples/basicapp/java/com/basicapp/BasicActivity.java new file mode 100644 index 0000000..14042be --- /dev/null +++ b/examples/basicapp/java/com/basicapp/BasicActivity.java @@ -0,0 +1,40 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.basicapp; + +import android.app.Activity; +import android.os.Bundle; +import android.view.Menu; +import android.widget.TextView; + + +/** + * The main activity of the Basic Sample App. + */ +public class BasicActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.basic_activity); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu, menu); + return true; + } +} diff --git a/examples/basicapp/java/com/basicapp/res/drawable-hdpi/ic_launcher.png b/examples/basicapp/java/com/basicapp/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..6ab2add Binary files /dev/null and b/examples/basicapp/java/com/basicapp/res/drawable-hdpi/ic_launcher.png differ diff --git a/examples/basicapp/java/com/basicapp/res/drawable-mdpi/ic_launcher.png b/examples/basicapp/java/com/basicapp/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..c0a73c3 Binary files /dev/null and b/examples/basicapp/java/com/basicapp/res/drawable-mdpi/ic_launcher.png differ diff --git a/examples/basicapp/java/com/basicapp/res/drawable-xhdpi/ic_launcher.png b/examples/basicapp/java/com/basicapp/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..014b0f1 Binary files /dev/null and b/examples/basicapp/java/com/basicapp/res/drawable-xhdpi/ic_launcher.png differ diff --git a/examples/basicapp/java/com/basicapp/res/drawable-xxhdpi/ic_launcher.png b/examples/basicapp/java/com/basicapp/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..20703a1 Binary files /dev/null and b/examples/basicapp/java/com/basicapp/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/examples/basicapp/java/com/basicapp/res/layout/basic_activity.xml b/examples/basicapp/java/com/basicapp/res/layout/basic_activity.xml new file mode 100644 index 0000000..61fb73e --- /dev/null +++ b/examples/basicapp/java/com/basicapp/res/layout/basic_activity.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/examples/basicapp/java/com/basicapp/res/menu/menu.xml b/examples/basicapp/java/com/basicapp/res/menu/menu.xml new file mode 100644 index 0000000..a56bed6 --- /dev/null +++ b/examples/basicapp/java/com/basicapp/res/menu/menu.xml @@ -0,0 +1,8 @@ + + + diff --git a/examples/basicapp/java/com/basicapp/res/values/dimens.xml b/examples/basicapp/java/com/basicapp/res/values/dimens.xml new file mode 100644 index 0000000..47c8224 --- /dev/null +++ b/examples/basicapp/java/com/basicapp/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + 16dp + 16dp + diff --git a/examples/basicapp/java/com/basicapp/res/values/strings.xml b/examples/basicapp/java/com/basicapp/res/values/strings.xml new file mode 100644 index 0000000..565c987 --- /dev/null +++ b/examples/basicapp/java/com/basicapp/res/values/strings.xml @@ -0,0 +1,8 @@ + + + + basicapp + Hello world! + Settings + + diff --git a/kokoro/presubmit/kokoro_presubmit.sh b/kokoro/presubmit/kokoro_presubmit.sh index ce4008d..78b5c8e 100644 --- a/kokoro/presubmit/kokoro_presubmit.sh +++ b/kokoro/presubmit/kokoro_presubmit.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 2021 The Bazel Authors. All rights reserved. +# Copyright 2022 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,5 +13,41 @@ # See the License for the specific language governing permissions and # limitations under the License. -# TODO(ahumesky): Fill in test -exit 0 +set -e +set -x + +bazel="${KOKORO_GFILE_DIR}/bazel-${bazel_version}-linux-x86_64" +chmod +x "$bazel" + +# Kokoro is no longer updating toolchains in their images, so install newer +# android build tools, because the latest one installed (26.0.2) has some bug +# in APPT2 which causes the magic number to be incorrect for some files it +# outputs. +# +# Use "yes" to accept sdk licenses. +cd "$ANDROID_HOME" +yes | tools/bin/sdkmanager --install "build-tools;30.0.3" &>/dev/null +yes | tools/bin/sdkmanager --licenses &>/dev/null + +# ANDROID_HOME is already in the environment. +export ANDROID_NDK_HOME="/opt/android-ndk-r16b" + +# Go to basic app workspace in the source tree +cd "${KOKORO_ARTIFACTS_DIR}/git/rules_android/examples/basicapp" + +# Create a tmpfs in the sandbox at "/tmp/hsperfdata_$USERNAME" to avoid the +# problems described in https://github.com/bazelbuild/bazel/issues/3236 +# Basically, the JVM creates a file at /tmp/hsperfdata_$USERNAME/$PID, but +# processes all get a PID of 2 in the sandbox, so concurrent Java build actions +# could crash because they're trying to modify the same file. So, tell the +# sandbox to mount a tmpfs at /tmp/hsperfdata_$(whoami) so that each JVM gets +# its own version of that directory. +hsperfdata_dir="/tmp/hsperfdata_$(whoami)_rules_android" +mkdir "$hsperfdata_dir" + +"$bazel" build \ + --sandbox_tmpfs_path="$hsperfdata_dir" \ + --verbose_failures \ + --experimental_google_legacy_api \ + --experimental_enable_android_migration_apis \ + //java/com/basicapp:basic_app diff --git a/kokoro/presubmit/presubmit.cfg b/kokoro/presubmit/presubmit.cfg index 7536277..7ef6a89 100644 --- a/kokoro/presubmit/presubmit.cfg +++ b/kokoro/presubmit/presubmit.cfg @@ -3,7 +3,7 @@ # Update this as newer versions of bazel are released. build_params { key: "bazel_version" - value: "4.0.0" + value: "5.0.0" } env_vars { -- cgit v1.2.3 From 58f7e2c73e5db580962b1e30c4c02abd16fc72d5 Mon Sep 17 00:00:00 2001 From: Alex Humesky Date: Tue, 8 Mar 2022 16:41:36 -0800 Subject: Remove unused _UNEXPECTED_LINT_JAR_ERROR. PiperOrigin-RevId: 433342534 Change-Id: I46f2e6e138b39f29b4c33b83b50120760951828f --- rules/aar_import/impl.bzl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/rules/aar_import/impl.bzl b/rules/aar_import/impl.bzl index 280060e..218a8ce 100644 --- a/rules/aar_import/impl.bzl +++ b/rules/aar_import/impl.bzl @@ -41,10 +41,6 @@ load( RULE_PREFIX = "_aar" ANDROID_MANIFEST = "AndroidManifest.xml" LINT_JAR = "lint.jar" -_UNEXPECTED_LINT_JAR_ERROR = ( - "In target %s, has_lint_jar attribute is required when the aar contains " + - "a lint.jar file." -) # Resources context dict fields. _PROVIDERS = "providers" -- cgit v1.2.3 From b6506d001786ee18d4daf33fac729738c648dc0f Mon Sep 17 00:00:00 2001 From: Zhaoqing Xu Date: Tue, 5 Apr 2022 12:43:23 -0700 Subject: Internal PiperOrigin-RevId: 439650271 Change-Id: If25c473f794f082c4399ab204a577604cb7d2727 --- rules/acls.bzl | 7 +++++++ rules/acls/android_rules_with_kt_rollout.bzl | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 rules/acls/android_rules_with_kt_rollout.bzl diff --git a/rules/acls.bzl b/rules/acls.bzl index c6c10cb..c40d0af 100644 --- a/rules/acls.bzl +++ b/rules/acls.bzl @@ -69,6 +69,7 @@ load( load("@rules_android//rules/acls:kt_android_library_rollout.bzl", "KT_ANDROID_LIBRARY_FALLBACK", "KT_ANDROID_LIBRARY_ROLLOUT") load("@rules_android//rules/acls:android_instrumentation_test_manifest_check_rollout.bzl", "ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK", "ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT") load("@rules_android//rules/acls:android_instrumentation_test_prebuilt_test_apk.bzl", "ANDROID_INSTRUMENTATION_TEST_PREBUILT_TEST_APK") +load("@rules_android//rules/acls:android_rules_with_kt_rollout.bzl", "ANDROID_RULES_WITH_KT_ROLLOUT") def _in_aar_import_deps_checker(fqn): return not matches(fqn, AAR_IMPORT_DEPS_CHECKER_FALLBACK_DICT) and matches(fqn, AAR_IMPORT_DEPS_CHECKER_ROLLOUT_DICT) @@ -192,6 +193,9 @@ def _in_android_instrumentation_test_manifest_check_rollout(fqn): def _in_android_instrumentation_test_prebuilt_test_apk(fqn): return matches(fqn, ANDROID_INSTRUMENTATION_TEST_PREBUILT_TEST_APK_DICT) +def _in_android_rules_with_kt_rollout(fqn): + return matches(fqn, ANDROID_RULES_WITH_KT_ROLLOUT) + def make_dict(lst): """Do not use this method outside of acls directory.""" return {t: True for t in lst} @@ -223,6 +227,8 @@ ANDROID_LINT_CHECKS_FALLBACK_DICT = make_dict(ANDROID_LINT_CHECKS_FALLBACK) ANDROID_LINT_CHECKS_ROLLOUT_DICT = make_dict(ANDROID_LINT_CHECKS_ROLLOUT) ANDROID_LINT_FALLBACK_DICT = make_dict(ANDROID_LINT_FALLBACK) ANDROID_LINT_ROLLOUT_DICT = make_dict(ANDROID_LINT_ROLLOUT) +ANDROID_RULES_WITH_KT_ROLLOUT_DICT = make_dict(ANDROID_RULES_WITH_KT_ROLLOUT) + LINT_REGISTRY_FALLBACK_DICT = make_dict(LINT_REGISTRY_FALLBACK) LINT_REGISTRY_ROLLOUT_DICT = make_dict(LINT_REGISTRY_ROLLOUT) ANDROID_BUILD_STAMPING_ROLLOUT_DICT = make_dict(ANDROID_BUILD_STAMPING_ROLLOUT) @@ -337,6 +343,7 @@ acls = struct( in_kt_android_library_rollout = _in_kt_android_library_rollout, in_android_instrumentation_test_manifest_check_rollout = _in_android_instrumentation_test_manifest_check_rollout, in_android_instrumentation_test_prebuilt_test_apk = _in_android_instrumentation_test_prebuilt_test_apk, + in_android_rules_with_kt_rollout = _in_android_rules_with_kt_rollout, ) # Visible for testing diff --git a/rules/acls/android_rules_with_kt_rollout.bzl b/rules/acls/android_rules_with_kt_rollout.bzl new file mode 100644 index 0000000..01564d4 --- /dev/null +++ b/rules/acls/android_rules_with_kt_rollout.bzl @@ -0,0 +1,19 @@ +# Copyright 2022 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow and fallback lists for Kotlin Compilation in the Android Rules.""" + +# keep sorted +ANDROID_RULES_WITH_KT_ROLLOUT = [ +] -- cgit v1.2.3 From 63bec25a6c9c0c4f1ae33672b46596cd9f395a48 Mon Sep 17 00:00:00 2001 From: Zhaoqing Xu Date: Wed, 13 Apr 2022 18:07:28 -0700 Subject: Internal Change PiperOrigin-RevId: 441628703 Change-Id: I3b7fdca288f4c398fff313668aec1155c172ca97 --- rules/android_library/impl.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/android_library/impl.bzl b/rules/android_library/impl.bzl index 286d7b5..69feb58 100644 --- a/rules/android_library/impl.bzl +++ b/rules/android_library/impl.bzl @@ -216,7 +216,7 @@ def _process_data_binding(ctx, java_package, resources_ctx, **unused_sub_ctxs): exports = utils.collect_providers(DataBindingV2Info, ctx.attr.exports), data_binding_exec = get_android_toolchain(ctx).data_binding_exec.files_to_run, data_binding_annotation_processor = - get_android_toolchain(ctx).data_binding_annotation_processor[JavaPluginInfo], + get_android_toolchain(ctx).data_binding_annotation_processor, data_binding_annotation_template = utils.only(get_android_toolchain(ctx).data_binding_annotation_template.files.to_list()), ), -- cgit v1.2.3 From db6bb1644473b2b44819f79d9834e77e2d91fece Mon Sep 17 00:00:00 2001 From: Zhaoqing Xu Date: Tue, 26 Apr 2022 17:14:49 -0700 Subject: Internal Change PiperOrigin-RevId: 444709310 Change-Id: Ifdc3ba4a10905315e6f0135c5ab9208ff6654f0b --- rules/aar_import/impl.bzl | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/rules/aar_import/impl.bzl b/rules/aar_import/impl.bzl index 218a8ce..f1a4fb8 100644 --- a/rules/aar_import/impl.bzl +++ b/rules/aar_import/impl.bzl @@ -53,22 +53,32 @@ def _create_aar_tree_artifact(ctx, name): return ctx.actions.declare_directory("%s/unzipped/%s/%s" % (RULE_PREFIX, name, ctx.label.name)) # Create an action to extract a file (specified by the parameter filename) from an AAR file. +# Will optionally create an empty output if the requested file does not exist.. def _extract_single_file( ctx, out_file, aar, filename, - unzip_tool): - args = ctx.actions.args() - args.add(aar) - args.add(filename) - args.add("-d", out_file.dirname) - - ctx.actions.run( - executable = unzip_tool, - arguments = [args], + unzip_tool, + create_empty_file = False): + ctx.actions.run_shell( + tools = [unzip_tool], inputs = [aar], outputs = [out_file], + command = + """ + if ! {create_empty_file} || {unzip_tool} -l {aar} {file} 2>/dev/null; then + {unzip_tool} {aar} {file} -d {dirname}; + else + touch {dirname}/{file}; + fi + """.format( + unzip_tool = unzip_tool.executable.path, + aar = aar.path, + file = out_file.basename, + dirname = out_file.dirname, + create_empty_file = str(create_empty_file).lower(), + ), mnemonic = "AarFileExtractor", progress_message = "Extracting %s from %s" % (filename, aar.basename), ) -- cgit v1.2.3 From 839808f6c2344c425a8ea51aa65851fcbf827a95 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Wed, 27 Apr 2022 13:59:14 -0700 Subject: Make the default minSdkVersion 21, to match defaulting to native multidex below PiperOrigin-RevId: 444959127 Change-Id: I31c2621de0ffb91d14bec12f6606a7c26a9deb66 --- rules/android_application/android_feature_module_rule.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/android_application/android_feature_module_rule.bzl b/rules/android_application/android_feature_module_rule.bzl index 3041656..b33cbcd 100644 --- a/rules/android_application/android_feature_module_rule.bzl +++ b/rules/android_application/android_feature_module_rule.bzl @@ -140,7 +140,7 @@ EOF ) # Create AndroidManifest.xml - min_sdk_version = getattr(attrs, "min_sdk_version", "14") or "14" + min_sdk_version = getattr(attrs, "min_sdk_version", "21") or "21" package = _java.resolve_package_from_label(Label(fqn), getattr(attrs, "custom_package", None)) native.genrule( name = targets.manifest_lib.name, -- cgit v1.2.3 From bbd3cd3db4df9298597dfefb1dc4f8ae580a1fb7 Mon Sep 17 00:00:00 2001 From: Zhaoqing Xu Date: Fri, 29 Apr 2022 09:43:52 -0700 Subject: Internal Change PiperOrigin-RevId: 445441198 Change-Id: I66b4d57bd401e27c97df3bfc9b475416ad8d01b2 --- rules/android_library/impl.bzl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rules/android_library/impl.bzl b/rules/android_library/impl.bzl index 69feb58..3c34a7a 100644 --- a/rules/android_library/impl.bzl +++ b/rules/android_library/impl.bzl @@ -252,10 +252,7 @@ def _process_jvm(ctx, exceptions_ctx, resources_ctx, idl_ctx, db_ctx, **unused_s deps = utils.collect_providers(JavaInfo, ctx.attr.deps, idl_ctx.idl_deps), exports = utils.collect_providers(JavaInfo, ctx.attr.exports), - plugins = ( - utils.collect_providers(JavaPluginInfo, ctx.attr.plugins) + - db_ctx.java_plugins - ), + plugins = utils.collect_providers(JavaPluginInfo, ctx.attr.plugins, db_ctx.java_plugins), exported_plugins = utils.collect_providers( JavaPluginInfo, ctx.attr.exported_plugins, -- cgit v1.2.3 From 1b31f1bd2bb1a206f98956555954729f74809502 Mon Sep 17 00:00:00 2001 From: Zhaoqing Xu Date: Mon, 2 May 2022 11:49:16 -0700 Subject: Internal Change PiperOrigin-RevId: 445990840 Change-Id: I349cd180d79406551ae9693f86dae70c054f5ee6 --- rules/aar_import/impl.bzl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rules/aar_import/impl.bzl b/rules/aar_import/impl.bzl index f1a4fb8..4d9cfe6 100644 --- a/rules/aar_import/impl.bzl +++ b/rules/aar_import/impl.bzl @@ -67,8 +67,8 @@ def _extract_single_file( outputs = [out_file], command = """ - if ! {create_empty_file} || {unzip_tool} -l {aar} {file} 2>/dev/null; then - {unzip_tool} {aar} {file} -d {dirname}; + if ! {create_empty_file} || {unzip_tool} -l {aar} | grep -q {file}; then + {unzip_tool} -q {aar} {file} -d {dirname}; else touch {dirname}/{file}; fi -- cgit v1.2.3 From 5078eac3f2e30236a32ba5a3ddaac729b80d227e Mon Sep 17 00:00:00 2001 From: A Googler Date: Tue, 10 May 2022 07:17:03 -0700 Subject: using constraint values in @platforms PiperOrigin-RevId: 447725613 Change-Id: Iad768f915aefecc1f34058069677827d207a741c --- toolchains/android_sdk/BUILD | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toolchains/android_sdk/BUILD b/toolchains/android_sdk/BUILD index d675cbe..105b773 100644 --- a/toolchains/android_sdk/BUILD +++ b/toolchains/android_sdk/BUILD @@ -17,8 +17,8 @@ toolchain_type( toolchain( name = "android_sdk_tools", exec_compatible_with = [ - "@bazel_tools//platforms:x86_64", - "@bazel_tools//platforms:linux", + "@platforms//cpu:x86_64", + "@platforms//os:linux", ], # TODO(b/175833893): This causes the toolchain to not be selected, so # disable for now. -- cgit v1.2.3 From 1d86b38550ab2ffd1d9d935adc24cd8406fc801e Mon Sep 17 00:00:00 2001 From: Samuel Freilich Date: Tue, 10 May 2022 15:38:40 -0700 Subject: Add coverage-relevant source_attributes for android_library PiperOrigin-RevId: 447844874 Change-Id: Ib5660b9a23909ce78f2dc60979d843446328eb32 --- rules/android_library/impl.bzl | 1 + 1 file changed, 1 insertion(+) diff --git a/rules/android_library/impl.bzl b/rules/android_library/impl.bzl index 3c34a7a..30c8dc7 100644 --- a/rules/android_library/impl.bzl +++ b/rules/android_library/impl.bzl @@ -401,6 +401,7 @@ def _process_coverage(ctx, **unused_ctx): providers = [ coverage_common.instrumented_files_info( ctx, + source_attributes = ["srcs"], dependency_attributes = ["assets", "deps", "exports"], ), ], -- cgit v1.2.3 From 05f7dcb78b28f2017033ea7a5ea8632e4baea7c3 Mon Sep 17 00:00:00 2001 From: Tim Peut Date: Thu, 12 May 2022 20:24:35 -0700 Subject: Enable android_archive to override the exposed package denylist. Will enable android_archive to be used to build "official" archives for e.g. androidx.test, which is currently blocked by the exposed package denylist. PiperOrigin-RevId: 448400592 Change-Id: Ib0f3e83bc24d9980f7ded80cd8c6f0d55633dfb6 --- rules/acls.bzl | 5 +++++ .../android_archive_exposed_package_allowlist.bzl | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 rules/acls/android_archive_exposed_package_allowlist.bzl diff --git a/rules/acls.bzl b/rules/acls.bzl index c40d0af..ac10d65 100644 --- a/rules/acls.bzl +++ b/rules/acls.bzl @@ -36,6 +36,7 @@ load("@rules_android//rules/acls:ait_virtual_device.bzl", "AIT_VIRTUAL_DEVICE_FA load("@rules_android//rules/acls:allow_resource_conflicts.bzl", "ALLOW_RESOURCE_CONFLICTS") load("@rules_android//rules/acls:android_archive_dogfood.bzl", "ANDROID_ARCHIVE_DOGFOOD") load("@rules_android//rules/acls:android_archive_excluded_deps_denylist.bzl", "ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST") +load("@rules_android//rules/acls:android_archive_exposed_package_allowlist.bzl", "ANDROID_ARCHIVE_EXPOSED_PACKAGE_ALLOWLIST") load("@rules_android//rules/acls:android_test_lockdown.bzl", "ANDROID_TEST_LOCKDOWN_GENERATOR_FUNCTIONS", "ANDROID_TEST_LOCKDOWN_TARGETS") load("@rules_android//rules/acls:android_device_plugin_rollout.bzl", "ANDROID_DEVICE_PLUGIN_FALLBACK", "ANDROID_DEVICE_PLUGIN_ROLLOUT") load("@rules_android//rules/acls:android_instrumentation_binary_starlark_resources.bzl", "ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK", "ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT") @@ -196,6 +197,9 @@ def _in_android_instrumentation_test_prebuilt_test_apk(fqn): def _in_android_rules_with_kt_rollout(fqn): return matches(fqn, ANDROID_RULES_WITH_KT_ROLLOUT) +def _get_android_archive_exposed_package_allowlist(fqn): + return ANDROID_ARCHIVE_EXPOSED_PACKAGE_ALLOWLIST.get(fqn, []) + def make_dict(lst): """Do not use this method outside of acls directory.""" return {t: True for t in lst} @@ -303,6 +307,7 @@ def matches(fqn, dct): return False acls = struct( + get_android_archive_exposed_package_allowlist = _get_android_archive_exposed_package_allowlist, in_aar_import_deps_checker = _in_aar_import_deps_checker, in_aar_import_explicit_exports_manifest = _in_aar_import_explicit_exports_manifest, in_aar_import_exports_r_java = _in_aar_import_exports_r_java, diff --git a/rules/acls/android_archive_exposed_package_allowlist.bzl b/rules/acls/android_archive_exposed_package_allowlist.bzl new file mode 100644 index 0000000..5825c06 --- /dev/null +++ b/rules/acls/android_archive_exposed_package_allowlist.bzl @@ -0,0 +1,22 @@ +# Copyright 2022 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allowlist for android_archive targets to expose packages that would otherwise be restricted.""" + +# Map of {"target": ["list", "of", "packages"]} which will be excluded from +# exposed package checks. +# keep sorted +ANDROID_ARCHIVE_EXPOSED_PACKAGE_ALLOWLIST = { + "@rules_android//test/rules/android_archive/java/com/testdata:archive_denied_package_allowlisted": ["androidx.test"], +} -- cgit v1.2.3 From a0fa60b32b9d1fe6e2e57d1210480a73c27dd702 Mon Sep 17 00:00:00 2001 From: Zhaoqing Xu Date: Tue, 17 May 2022 10:42:35 -0700 Subject: Internal Change PiperOrigin-RevId: 449259103 Change-Id: Iab92cbc61ab78d40c6f39478d08698f7c79ecbbd --- rules/aar_import/impl.bzl | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/rules/aar_import/impl.bzl b/rules/aar_import/impl.bzl index 4d9cfe6..db322a8 100644 --- a/rules/aar_import/impl.bzl +++ b/rules/aar_import/impl.bzl @@ -46,15 +46,15 @@ LINT_JAR = "lint.jar" _PROVIDERS = "providers" _VALIDATION_RESULTS = "validation_results" -def _create_aar_artifact(ctx, name): - return ctx.actions.declare_file("%s/%s/%s" % (RULE_PREFIX, ctx.label.name, name)) - def _create_aar_tree_artifact(ctx, name): return ctx.actions.declare_directory("%s/unzipped/%s/%s" % (RULE_PREFIX, name, ctx.label.name)) +def create_aar_artifact(ctx, name): + return ctx.actions.declare_file("%s/%s/%s" % (RULE_PREFIX, ctx.label.name, name)) + # Create an action to extract a file (specified by the parameter filename) from an AAR file. # Will optionally create an empty output if the requested file does not exist.. -def _extract_single_file( +def extract_single_file( ctx, out_file, aar, @@ -226,7 +226,7 @@ def _extract_and_merge_jars( extracts and merges all Jars. """ jars_tree_artifact = _create_aar_tree_artifact(ctx, "jars") - jars_params_file = _create_aar_artifact(ctx, "jar_merging_params") + jars_params_file = create_aar_artifact(ctx, "jar_merging_params") _extract_jars( ctx, jars_tree_artifact, @@ -320,7 +320,7 @@ def _process_jars( ]) merged_java_info = java_common.merge(java_infos + r_java_info) - jdeps_artifact = _create_aar_artifact(ctx, "jdeps.proto") + jdeps_artifact = create_aar_artifact(ctx, "jdeps.proto") _create_import_deps_check( ctx, [out_jar], @@ -393,8 +393,8 @@ def _process_lint_rules( providers = [] if ctx.attr.has_lint_jar: - lint_jar = _create_aar_artifact(ctx, LINT_JAR) - _extract_single_file( + lint_jar = create_aar_artifact(ctx, LINT_JAR) + extract_single_file( ctx, lint_jar, aar, @@ -449,8 +449,8 @@ def impl(ctx): package = _java.resolve_package_from_label(ctx.label, ctx.attr.package) # Extract the AndroidManifest.xml from the AAR. - android_manifest = _create_aar_artifact(ctx, ANDROID_MANIFEST) - _extract_single_file( + android_manifest = create_aar_artifact(ctx, ANDROID_MANIFEST) + extract_single_file( ctx, android_manifest, aar, @@ -470,7 +470,7 @@ def impl(ctx): ) providers.extend(resources_ctx.providers) - merged_jar = _create_aar_artifact(ctx, "classes_and_libs_merged.jar") + merged_jar = create_aar_artifact(ctx, "classes_and_libs_merged.jar") jvm_ctx = _process_jars( ctx, out_jar = merged_jar, @@ -499,7 +499,7 @@ def impl(ctx): providers.extend(jvm_ctx.providers) validation_outputs.extend(jvm_ctx.validation_results) - native_libs = _create_aar_artifact(ctx, "native_libs.zip") + native_libs = create_aar_artifact(ctx, "native_libs.zip") _extract_native_libs( ctx, native_libs, @@ -523,7 +523,7 @@ def impl(ctx): ) # Will be empty if there's no proguard.txt file in the aar - proguard_spec = _create_aar_artifact(ctx, "proguard.txt") + proguard_spec = create_aar_artifact(ctx, "proguard.txt") providers.append(_collect_proguard( ctx, proguard_spec, -- cgit v1.2.3 From af2ea4724f77eefb3bb457c78424ab9306fc6ab0 Mon Sep 17 00:00:00 2001 From: Zhaoqing Xu Date: Wed, 18 May 2022 13:38:06 -0700 Subject: Internal Change PiperOrigin-RevId: 449564857 Change-Id: I434489917b36b6d44937b22f729600e2ed4d29e1 --- rules/acls.bzl | 6 ++++++ rules/acls/baseline_profiles_rollout.bzl | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 rules/acls/baseline_profiles_rollout.bzl diff --git a/rules/acls.bzl b/rules/acls.bzl index ac10d65..0acfc41 100644 --- a/rules/acls.bzl +++ b/rules/acls.bzl @@ -71,6 +71,7 @@ load("@rules_android//rules/acls:kt_android_library_rollout.bzl", "KT_ANDROID_LI load("@rules_android//rules/acls:android_instrumentation_test_manifest_check_rollout.bzl", "ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK", "ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT") load("@rules_android//rules/acls:android_instrumentation_test_prebuilt_test_apk.bzl", "ANDROID_INSTRUMENTATION_TEST_PREBUILT_TEST_APK") load("@rules_android//rules/acls:android_rules_with_kt_rollout.bzl", "ANDROID_RULES_WITH_KT_ROLLOUT") +load("@rules_android//rules/acls:baseline_profiles_rollout.bzl", "BASELINE_PROFILES_ROLLOUT") def _in_aar_import_deps_checker(fqn): return not matches(fqn, AAR_IMPORT_DEPS_CHECKER_FALLBACK_DICT) and matches(fqn, AAR_IMPORT_DEPS_CHECKER_ROLLOUT_DICT) @@ -200,6 +201,9 @@ def _in_android_rules_with_kt_rollout(fqn): def _get_android_archive_exposed_package_allowlist(fqn): return ANDROID_ARCHIVE_EXPOSED_PACKAGE_ALLOWLIST.get(fqn, []) +def _in_baseline_profiles_rollout(fqn): + return matches(fqn, BASELINE_PROFILES_ROLLOUT) + def make_dict(lst): """Do not use this method outside of acls directory.""" return {t: True for t in lst} @@ -270,6 +274,7 @@ KT_ANDROID_LIBRARY_FALLBACK_DICT = make_dict(KT_ANDROID_LIBRARY_FALLBACK) ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT_DICT = make_dict(ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT) ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK_DICT = make_dict(ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK) ANDROID_INSTRUMENTATION_TEST_PREBUILT_TEST_APK_DICT = make_dict(ANDROID_INSTRUMENTATION_TEST_PREBUILT_TEST_APK) +BASELINE_PROFILES_ROLLOUT_DICT = make_dict(BASELINE_PROFILES_ROLLOUT) def matches(fqn, dct): # Labels with workspace names ("@workspace//pkg:target") are not supported. @@ -349,6 +354,7 @@ acls = struct( in_android_instrumentation_test_manifest_check_rollout = _in_android_instrumentation_test_manifest_check_rollout, in_android_instrumentation_test_prebuilt_test_apk = _in_android_instrumentation_test_prebuilt_test_apk, in_android_rules_with_kt_rollout = _in_android_rules_with_kt_rollout, + in_baseline_profiles_rollout = _in_baseline_profiles_rollout, ) # Visible for testing diff --git a/rules/acls/baseline_profiles_rollout.bzl b/rules/acls/baseline_profiles_rollout.bzl new file mode 100644 index 0000000..e4677bc --- /dev/null +++ b/rules/acls/baseline_profiles_rollout.bzl @@ -0,0 +1,19 @@ +# Copyright 2022 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allowlist for targets allowed to set baseline_profiles.""" + +# keep sorted +BASELINE_PROFILES_ROLLOUT = [ +] -- cgit v1.2.3 From 5b12258b0c0c97d600b22b47debf4059aee7fa26 Mon Sep 17 00:00:00 2001 From: Kevin Bierhoff Date: Tue, 24 May 2022 15:45:57 -0700 Subject: make `AndroidLintRulesInfo` contain a depset and adjust `exports` propagation in `aar_import` PiperOrigin-RevId: 450786794 Change-Id: Iae66361371e8654db110486e76e543f695f62161 --- rules/aar_import/impl.bzl | 25 +++++++++++++++---------- rules/providers.bzl | 2 +- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/rules/aar_import/impl.bzl b/rules/aar_import/impl.bzl index db322a8..a5b9941 100644 --- a/rules/aar_import/impl.bzl +++ b/rules/aar_import/impl.bzl @@ -390,7 +390,10 @@ def _process_lint_rules( ctx, aar, unzip_tool): - providers = [] + transitive_lint_jars = [info.lint_jars for info in _utils.collect_providers( + AndroidLintRulesInfo, + ctx.attr.exports, + )] if ctx.attr.has_lint_jar: lint_jar = create_aar_artifact(ctx, LINT_JAR) @@ -401,15 +404,17 @@ def _process_lint_rules( LINT_JAR, unzip_tool, ) - providers.append(AndroidLintRulesInfo( - lint_jar = lint_jar, - )) - - providers.extend(_utils.collect_providers( - AndroidLintRulesInfo, - ctx.attr.exports, - )) - return providers + return [ + AndroidLintRulesInfo( + lint_jars = depset(direct = [lint_jar], transitive = transitive_lint_jars), + ), + ] + elif transitive_lint_jars: + return [ + AndroidLintRulesInfo(lint_jars = depset(transitive = transitive_lint_jars)), + ] + else: + return [] def _collect_proguard( ctx, diff --git a/rules/providers.bzl b/rules/providers.bzl index e7db209..4460af1 100644 --- a/rules/providers.bzl +++ b/rules/providers.bzl @@ -99,7 +99,7 @@ StarlarkAndroidResourcesInfo = provider( AndroidLintRulesInfo = provider( doc = "Provides extra lint rules to use with AndroidLint.", fields = dict( - lint_jar = "A file, a lint jar found in an aar.", + lint_jars = "A depset of lint rule jars found in AARs and exported by a target.", ), ) -- cgit v1.2.3 From 5402f72557a620607d5d2699a6207118397ffc7e Mon Sep 17 00:00:00 2001 From: Kevin Bierhoff Date: Tue, 31 May 2022 18:57:20 -0700 Subject: propagate AndroidLintRuleJars provider through exports PiperOrigin-RevId: 452190373 Change-Id: Iec874e2079c55b6db3b53da1842aa61c021fb711 --- rules/android_library/impl.bzl | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/rules/android_library/impl.bzl b/rules/android_library/impl.bzl index 30c8dc7..f46e227 100644 --- a/rules/android_library/impl.bzl +++ b/rules/android_library/impl.bzl @@ -27,6 +27,7 @@ load( "processing_pipeline", ) load("@rules_android//rules:proguard.bzl", _proguard = "proguard") +load("@rules_android//rules:providers.bzl", "AndroidLintRulesInfo") load("@rules_android//rules:resources.bzl", _resources = "resources") load("@rules_android//rules:utils.bzl", "get_android_sdk", "get_android_toolchain", "log", "utils") load("@rules_android//rules/flags:flags.bzl", _flags = "flags") @@ -269,11 +270,25 @@ def _process_jvm(ctx, exceptions_ctx, resources_ctx, idl_ctx, db_ctx, **unused_s java_toolchain = _common.get_java_toolchain(ctx), ) + providers = [java_info] + + # Propagate Lint rule Jars from any exported AARs (b/229993446) + android_lint_rules = [info.lint_jars for info in utils.collect_providers( + AndroidLintRulesInfo, + ctx.attr.exports, + )] + if android_lint_rules: + providers.append( + AndroidLintRulesInfo( + lint_jars = depset(transitive = android_lint_rules), + ), + ) + return ProviderInfo( name = "jvm_ctx", value = struct( java_info = java_info, - providers = [java_info], + providers = providers, ), ) -- cgit v1.2.3 From fc1f9799edb87ea408f2c67700b058dd86605352 Mon Sep 17 00:00:00 2001 From: A Googler Date: Fri, 10 Jun 2022 10:34:55 -0700 Subject: Wire ObjectMethodRewriter in toolchain. PiperOrigin-RevId: 454194224 Change-Id: I361c9d8ce46b11c3cea144b36068b61a5ba23dae --- toolchains/android/toolchain.bzl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/toolchains/android/toolchain.bzl b/toolchains/android/toolchain.bzl index 9f929ba..d1e17dd 100644 --- a/toolchains/android/toolchain.bzl +++ b/toolchains/android/toolchain.bzl @@ -148,6 +148,12 @@ _ATTRS = dict( default = "@androidsdk//:fail", # TODO: "//src/tools/jdeps", needs Go executable = True, ), + object_method_rewriter = attr.label( + allow_files = True, + cfg = "exec", + default = "@androidsdk//:fail", + executable = True, + ), proguard_allowlister = attr.label( cfg = "exec", default = "@bazel_tools//tools/jdk:proguard_whitelister", -- cgit v1.2.3 From c1afe0ec0fe5c861db12fbc45230802b143b0f6e Mon Sep 17 00:00:00 2001 From: Joseph Park Date: Tue, 12 Jul 2022 12:14:22 -0700 Subject: Adding acl for enforce_min_sdk_floor PiperOrigin-RevId: 460522734 Change-Id: I95ad5682675c270a48dbc970b86100a5b36478ef --- rules/acls.bzl | 7 +++++++ rules/acls/enforce_min_sdk_floor_rollout.bzl | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 rules/acls/enforce_min_sdk_floor_rollout.bzl diff --git a/rules/acls.bzl b/rules/acls.bzl index 0acfc41..4633393 100644 --- a/rules/acls.bzl +++ b/rules/acls.bzl @@ -72,6 +72,7 @@ load("@rules_android//rules/acls:android_instrumentation_test_manifest_check_rol load("@rules_android//rules/acls:android_instrumentation_test_prebuilt_test_apk.bzl", "ANDROID_INSTRUMENTATION_TEST_PREBUILT_TEST_APK") load("@rules_android//rules/acls:android_rules_with_kt_rollout.bzl", "ANDROID_RULES_WITH_KT_ROLLOUT") load("@rules_android//rules/acls:baseline_profiles_rollout.bzl", "BASELINE_PROFILES_ROLLOUT") +load("@rules_android//rules/acls:enforce_min_sdk_floor_rollout.bzl", "ENFORCE_MIN_SDK_FLOOR_FALLBACK", "ENFORCE_MIN_SDK_FLOOR_ROLLOUT") def _in_aar_import_deps_checker(fqn): return not matches(fqn, AAR_IMPORT_DEPS_CHECKER_FALLBACK_DICT) and matches(fqn, AAR_IMPORT_DEPS_CHECKER_ROLLOUT_DICT) @@ -204,6 +205,9 @@ def _get_android_archive_exposed_package_allowlist(fqn): def _in_baseline_profiles_rollout(fqn): return matches(fqn, BASELINE_PROFILES_ROLLOUT) +def _in_enforce_min_sdk_floor_rollout(fqn): + return not matches(fqn, ENFORCE_MIN_SDK_FLOOR_FALLBACK_DICT) and matches(fqn, ENFORCE_MIN_SDK_FLOOR_ROLLOUT_DICT) + def make_dict(lst): """Do not use this method outside of acls directory.""" return {t: True for t in lst} @@ -275,6 +279,8 @@ ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT_DICT = make_dict(ANDROID_INS ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK_DICT = make_dict(ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK) ANDROID_INSTRUMENTATION_TEST_PREBUILT_TEST_APK_DICT = make_dict(ANDROID_INSTRUMENTATION_TEST_PREBUILT_TEST_APK) BASELINE_PROFILES_ROLLOUT_DICT = make_dict(BASELINE_PROFILES_ROLLOUT) +ENFORCE_MIN_SDK_FLOOR_ROLLOUT_DICT = make_dict(ENFORCE_MIN_SDK_FLOOR_ROLLOUT) +ENFORCE_MIN_SDK_FLOOR_FALLBACK_DICT = make_dict(ENFORCE_MIN_SDK_FLOOR_FALLBACK) def matches(fqn, dct): # Labels with workspace names ("@workspace//pkg:target") are not supported. @@ -355,6 +361,7 @@ acls = struct( in_android_instrumentation_test_prebuilt_test_apk = _in_android_instrumentation_test_prebuilt_test_apk, in_android_rules_with_kt_rollout = _in_android_rules_with_kt_rollout, in_baseline_profiles_rollout = _in_baseline_profiles_rollout, + in_enforce_min_sdk_floor_rollout = _in_enforce_min_sdk_floor_rollout, ) # Visible for testing diff --git a/rules/acls/enforce_min_sdk_floor_rollout.bzl b/rules/acls/enforce_min_sdk_floor_rollout.bzl new file mode 100644 index 0000000..29c18ab --- /dev/null +++ b/rules/acls/enforce_min_sdk_floor_rollout.bzl @@ -0,0 +1,22 @@ +# Copyright 2022 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Rollout list for enabling enforce min SDK floor.""" + +ENFORCE_MIN_SDK_FLOOR_ROLLOUT = [ +] + +ENFORCE_MIN_SDK_FLOOR_FALLBACK = [ + "//:__subpackages__", +] -- cgit v1.2.3 From ed135a70664c9dc7d897b43604f2923c30292374 Mon Sep 17 00:00:00 2001 From: Joseph Park Date: Mon, 18 Jul 2022 20:10:35 -0700 Subject: Move enforce_min_sdk_floor tool. PiperOrigin-RevId: 461780782 Change-Id: I58402c4fbf4e0ec3bc2da6f796fe05b063b39870 --- src/tools/enforce_min_sdk_floor/BUILD | 21 +++ .../enforce_min_sdk_floor/enforce_min_sdk_floor.py | 194 +++++++++++++++++++++ .../enforce_min_sdk_floor_test.py | 111 ++++++++++++ toolchains/android/toolchain.bzl | 5 + 4 files changed, 331 insertions(+) create mode 100644 src/tools/enforce_min_sdk_floor/BUILD create mode 100644 src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py create mode 100644 src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor_test.py diff --git a/src/tools/enforce_min_sdk_floor/BUILD b/src/tools/enforce_min_sdk_floor/BUILD new file mode 100644 index 0000000..221fe8b --- /dev/null +++ b/src/tools/enforce_min_sdk_floor/BUILD @@ -0,0 +1,21 @@ +# Description: +# Package for tool to enforce min SDK floor on AndroidManifests +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +py_binary( + name = "enforce_min_sdk_floor", + srcs = ["enforce_min_sdk_floor.py"], + deps = [ + "@bazel_tools//third_party/py/abseil", + ], +) + +py_test( + name = "enforce_min_sdk_floor_test", + srcs = ["enforce_min_sdk_floor_test.py"], + deps = [ + ":enforce_min_sdk_floor", + ], +) diff --git a/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py b/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py new file mode 100644 index 0000000..77be5f9 --- /dev/null +++ b/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py @@ -0,0 +1,194 @@ +# pylint: disable=g-direct-third-party-import +# Copyright 2022 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""AndroidManifest tool to enforce a floor on the minSdkVersion attribute. + +Ensures that the minSdkVersion attribute is >= than the specified floor, +and if the attribute is either not specified or less than the floor, +sets it to the floor. +""" + +import os +import sys + +import xml.etree.ElementTree as ET +import google3 +from absl import app +from absl import flags + +BUMP = "bump" +VALIDATE = "validate" + +USES_SDK = "uses-sdk" +MIN_SDK_ATTRIB = "{http://schemas.android.com/apk/res/android}minSdkVersion" + +_ACTION = flags.DEFINE_enum( + "action", + None, + [BUMP, VALIDATE], + f"Action to perform, either {BUMP} or {VALIDATE}") +_MANIFEST = flags.DEFINE_string( + "manifest", + None, + "AndroidManifest.xml of the instrumentation APK") +_MIN_SDK_FLOOR = flags.DEFINE_integer( + "min_sdk_floor", + 0, + "Min SDK floor", + lower_bound=0) +_OUTPUT = flags.DEFINE_string( + "output", + None, + f"Output AndroidManifest.xml to generate, only needed for {BUMP}") +_LOG = flags.DEFINE_string("log", None, "Path to write the log to") + + +class MinSdkError(Exception): + """Raised when there is a problem with the min SDK attribute in AndroidManifest.xml.""" + + +def _BumpMinSdk(xml_content, min_sdk_floor): + """Checks the min SDK in xml_content and replaces with min_sdk_floor if needed. + + Args: + xml_content: str, the contents of the AndroidManifest.xml file + min_sdk_floor: int, the min SDK floor + + Returns: + A tuple with the following elements: + - str: The xml contents of the manifest with the min SDK floor enforced. + This string will be equal to the input if the min SDK is already not less + than the floor. + - str: log message of action taken + """ + if min_sdk_floor == 0: + return xml_content, "No min SDK floor specified. Manifest unchanged." + + # Parse namespaces first to keep the prefix. + ns_parser = ET.XMLPullParser(events=["start-ns"]) + ns_parser.feed(xml_content) + ns_parser.close() + for _, ns_tuple in ns_parser.read_events(): + ET.register_namespace(ns_tuple[0], ns_tuple[1]) + + root = ET.fromstring(xml_content) + uses_sdk = root.find(USES_SDK) + if uses_sdk is None: + ET.SubElement(root, USES_SDK, {MIN_SDK_ATTRIB: str(min_sdk_floor)}) + return ( + ET.tostring(root, encoding="utf-8"), + "No uses-sdk element found while floor is specified " + + f"({min_sdk_floor}). Min SDK added.") + + min_sdk = uses_sdk.get(MIN_SDK_ATTRIB) + if min_sdk is None: + uses_sdk.set(MIN_SDK_ATTRIB, str(min_sdk_floor)) + return ( + ET.tostring(root, encoding="utf-8"), + "No minSdkVersion attribute found while floor is specified" + + f"({min_sdk_floor}). Min SDK added.") + + try: + min_sdk_int = int(min_sdk) + except ValueError: + return ( + xml_content, + f"Placeholder used for the minSdkVersion attribute ({min_sdk}). " + + "Manifest unchanged.") + + if min_sdk_int < min_sdk_floor: + uses_sdk.set(MIN_SDK_ATTRIB, str(min_sdk_floor)) + return ( + ET.tostring(root, encoding="utf-8"), + f"minSdkVersion attribute specified in the manifest ({min_sdk}) " + + f"is less than the floor ({min_sdk_floor}). Min SDK replaced.") + return ( + xml_content, + f"minSdkVersion attribute specified in the manifest ({min_sdk}) " + + f"is not less than the floor ({min_sdk_floor}). Manifest unchanged.") + + +def _ValidateMinSdk(xml_content, min_sdk_floor): + """Checks the min SDK in xml_content and raises MinSdkError if it is either not specified or less than the floor. + + Args: + xml_content: str, the contents of the AndroidManifest.xml file + min_sdk_floor: int, the min SDK floor + Returns: + str: log message + Raises: + MinSdkError: The min SDK is less than the specified floor. + """ + if min_sdk_floor == 0: + return "No min SDK floor specified." + + root = ET.fromstring(xml_content) + + uses_sdk = root.find(USES_SDK) + if uses_sdk is None: + raise MinSdkError( + "No uses-sdk element found in manifest " + + f"while floor is specified ({min_sdk_floor}).") + + min_sdk = uses_sdk.get(MIN_SDK_ATTRIB) + if min_sdk is None: + raise MinSdkError( + "No minSdkVersion attribute found in manifest " + + f"while floor is specified ({min_sdk_floor}).") + + try: + min_sdk_int = int(min_sdk) + except ValueError: + return f"Placeholder minSdkVersion = {min_sdk}\n min SDK floor = {min_sdk_floor}" + + if min_sdk_int < min_sdk_floor: + raise MinSdkError( + f"minSdkVersion attribute specified in the manifest ({min_sdk}) " + + f"is less than the floor ({min_sdk_floor}).") + return f"minSdkVersion = {min_sdk}\n min SDK floor = {min_sdk_floor}" + + +def main(unused_argv): + manifest_path = _MANIFEST.value + with open(manifest_path, "rb") as f: + manifest = f.read() + + if _ACTION.value == BUMP: + output_path = _OUTPUT.value + dirname = os.path.dirname(output_path) + if not os.path.exists(dirname): + os.makedirs(dirname) + + out_contents, log_message = _BumpMinSdk(manifest, _MIN_SDK_FLOOR.value) + with open(output_path, "wb") as f: + f.write(out_contents) + + elif _ACTION.value == VALIDATE: + try: + log_message = _ValidateMinSdk(manifest, _MIN_SDK_FLOOR.value) + except MinSdkError as e: + sys.exit(str(e)) + else: + sys.exit(f"Action must be either {BUMP} or {VALIDATE}") + + if _LOG.value is not None: + log_path = _LOG.value + dirname = os.path.dirname(log_path) + if not os.path.exists(dirname): + os.makedirs(dirname) + with open(log_path, "w") as f: + f.write(log_message) + +if __name__ == "__main__": + app.run(main) diff --git a/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor_test.py b/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor_test.py new file mode 100644 index 0000000..e49d2ac --- /dev/null +++ b/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor_test.py @@ -0,0 +1,111 @@ +# Copyright 2017 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Unit tests for enforce_min_sdk_floor.py.""" + +import unittest +import xml.etree.ElementTree as ET + +from google3.third_party.bazel_rules.rules_android.src.tools.enforce_min_sdk_floor.enforce_min_sdk_floor import _BumpMinSdk +from google3.third_party.bazel_rules.rules_android.src.tools.enforce_min_sdk_floor.enforce_min_sdk_floor import _ValidateMinSdk + +from google3.third_party.bazel_rules.rules_android.src.tools.enforce_min_sdk_floor.enforce_min_sdk_floor import MIN_SDK_ATTRIB +from google3.third_party.bazel_rules.rules_android.src.tools.enforce_min_sdk_floor.enforce_min_sdk_floor import MinSdkError +from google3.third_party.bazel_rules.rules_android.src.tools.enforce_min_sdk_floor.enforce_min_sdk_floor import USES_SDK + +MANIFEST_NO_USES_SDK = """ + + +""".encode("utf-8") + +MANIFEST_NO_MIN_SDK = """ + + + +""".encode("utf-8") + +MANIFEST_MIN_SDK_PLACEHOLDER = """ + + + +""".encode("utf-8") + +MANIFEST_MIN_SDK = """ + + + +""".encode("utf-8") + + +class EnforceMinSdkFloorTest(unittest.TestCase): + + def test_bump_no_min_sdk_floor(self): + out, _ = _BumpMinSdk(MANIFEST_NO_USES_SDK, 0) + self.assertEqual(out, MANIFEST_NO_USES_SDK) + + def test_bump_no_uses_sdk(self): + out, _ = _BumpMinSdk(MANIFEST_NO_USES_SDK, 11) + min_sdk = ET.fromstring(out).find(USES_SDK).get(MIN_SDK_ATTRIB) + self.assertEqual(min_sdk, "11") + + def test_bump_no_min_sdk_attrib(self): + out, _ = _BumpMinSdk(MANIFEST_NO_MIN_SDK, 7) + min_sdk = ET.fromstring(out).find(USES_SDK).get(MIN_SDK_ATTRIB) + self.assertEqual(min_sdk, "7") + + def test_bump_min_sdk_attrib_placeholder(self): + out, _ = _BumpMinSdk(MANIFEST_MIN_SDK_PLACEHOLDER, 13) + self.assertEqual(out, MANIFEST_MIN_SDK_PLACEHOLDER) + + def test_bump_higher_min_sdk(self): + out, _ = _BumpMinSdk(MANIFEST_MIN_SDK, 10) + self.assertEqual(out, MANIFEST_MIN_SDK) + + def test_bump_lower_min_sdk(self): + out, _ = _BumpMinSdk(MANIFEST_MIN_SDK, 14) + min_sdk = ET.fromstring(out).find(USES_SDK).get(MIN_SDK_ATTRIB) + self.assertEqual(min_sdk, "14") + + def test_validate_no_min_sdk_floor(self): + _ = _ValidateMinSdk(MANIFEST_NO_USES_SDK, 0) + + def test_validate_no_uses_sdk(self): + self.assertRaises(MinSdkError, + _ValidateMinSdk, + xml_content=MANIFEST_NO_USES_SDK, + min_sdk_floor=5) + + def test_validate_no_min_sdk_attrib(self): + self.assertRaises(MinSdkError, + _ValidateMinSdk, + xml_content=MANIFEST_NO_MIN_SDK, + min_sdk_floor=19) + + def test_validate_min_sdk_attrib_placeholder(self): + _ = _ValidateMinSdk(MANIFEST_MIN_SDK_PLACEHOLDER, 21) + + def test_validate_higher_min_sdk(self): + _ = _ValidateMinSdk(MANIFEST_MIN_SDK, 8) + + def test_validate_lower_min_sdk(self): + self.assertRaises(MinSdkError, + _ValidateMinSdk, + xml_content=MANIFEST_MIN_SDK, + min_sdk_floor=18) + +if __name__ == "__main__": + unittest.main() diff --git a/toolchains/android/toolchain.bzl b/toolchains/android/toolchain.bzl index d1e17dd..a692305 100644 --- a/toolchains/android/toolchain.bzl +++ b/toolchains/android/toolchain.bzl @@ -121,6 +121,11 @@ _ATTRS = dict( default = "@bazel_tools//tools/android:desugar_java8_extra_bootclasspath", executable = True, ), + enforce_min_sdk_floor_tool = attr.label( + cfg = "exec", + default = "//src/tools/enforce_min_sdk_floor", + executable = True, + ), idlclass = attr.label( allow_files = True, cfg = "exec", -- cgit v1.2.3 From 60e34110ca6bf61e8305dd62c0efcfc22faeaf8d Mon Sep 17 00:00:00 2001 From: Joseph Park Date: Tue, 19 Jul 2022 15:24:50 -0700 Subject: Create action for enforcing AndroidManifest min SDK floor. PiperOrigin-RevId: 461989552 Change-Id: Ifd8478a116edf9ecc8c352b287c451ae438a1b2c --- rules/resources.bzl | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/rules/resources.bzl b/rules/resources.bzl index a819c34..3e161e7 100644 --- a/rules/resources.bzl +++ b/rules/resources.bzl @@ -69,6 +69,9 @@ _INCORRECT_RESOURCE_LAYOUT_ERROR = ( _VERSION_NAME = "versionName" _VERSION_CODE = "versionCode" +# Min SDK floor +_MIN_SDK_FLOOR = 14 + # Resources context attributes. _ASSETS_PROVIDER = "assets_provider" _DATA_BINDING_LAYOUT_INFO = "data_binding_layout_info" @@ -127,6 +130,23 @@ _ResourcesPackageContextInfo = provider( }, ) +# Manifest context attributes +_MIN_SDK_BUMPED_MANIFEST = "min_sdk_bumped_manifest" + +_ManifestContextInfo = provider( + "Manifest context object", + fields = { + _MIN_SDK_BUMPED_MANIFEST: "The manifest with the min SDK bumped to the floor.", + }, +) + +_ManifestValidationContextInfo = provider( + "Manifest validation context object", + fields = { + _VALIDATION_OUTPUTS: "List of outputs given to OutputGroupInfo _validation group.", + }, +) + def _generate_dummy_manifest( ctx, out_manifest = None, @@ -973,6 +993,99 @@ def _validate_resources(resource_files = None): if res_type not in _RESOURCE_FOLDER_TYPES: fail(_INCORRECT_RESOURCE_LAYOUT_ERROR % resource_file) +def _bump_min_sdk( + ctx, + enforce_min_sdk_floor_tool, + manifest, + floor = _MIN_SDK_FLOOR): + """Bumps the min SDK attribute of AndroidManifest to the floor. + + Args: + ctx: The rules context. + enforce_min_sdk_floor_tool: FilesToRunProvider. The enforce_min_sdk_tool executable or + FilesToRunprovider + manifest: File. The AndroidManifest.xml file. + floor: int. The min SDK floor. + + Returns: + A dict containing _MinSdkFloorEnforcedContextInfo provider fields. + """ + manifest_ctx = {} + if not manifest: + manifest_ctx[_MIN_SDK_BUMPED_MANIFEST] = manifest + return _ManifestContextInfo(**manifest_ctx) + + args = ctx.actions.args() + args.add("-action", "bump") + args.add("-manifest", manifest) + args.add("-min_sdk_floor", floor) + + out_dir = "_migrated/_min_sdk_bumped/" + ctx.label.name + "/" + log = ctx.actions.declare_file( + out_dir + "log.txt", + ) + args.add("-log", log.path) + + out_manifest = ctx.actions.declare_file( + out_dir + "AndroidManifest.xml", + ) + args.add("-output", out_manifest.path) + ctx.actions.run( + executable = enforce_min_sdk_floor_tool, + inputs = [manifest], + outputs = [out_manifest, log], + arguments = [args], + mnemonic = "BumpMinSdkFloor", + progress_message = "Bumping up AndroidManifest min SDK %s" % str(ctx.label), + ) + manifest_ctx[_MIN_SDK_BUMPED_MANIFEST] = out_manifest + + return _ManifestContextInfo(**manifest_ctx) + +def _validate_min_sdk( + ctx, + enforce_min_sdk_floor_tool, + manifest, + floor = _MIN_SDK_FLOOR): + """Validates that the min SDK attribute of AndroidManifest is at least at the floor. + + Args: + ctx: The rules context. + enforce_min_sdk_floor_tool: FilesToRunProvider. The enforce_min_sdk_tool executable or + FilesToRunprovider + manifest: File. The AndroidManifest.xml file. + floor: int. The min SDK floor. + + Returns: + A dict containing _MinSdkFloorEnforcedContextInfo provider fields. + """ + manifest_validation_ctx = {_VALIDATION_OUTPUTS: []} + if not manifest: + return _ManifestValidationContextInfo(**manifest_validation_ctx) + + args = ctx.actions.args() + args.add("-action", "validate") + args.add("-manifest", manifest) + args.add("-min_sdk_floor", floor) + + out_dir = "_migrated/_min_sdk_validated/" + ctx.label.name + "/" + log = ctx.actions.declare_file( + out_dir + "log.txt", + ) + args.add("-log", log.path) + + ctx.actions.run( + executable = enforce_min_sdk_floor_tool, + inputs = [manifest], + outputs = [log], + arguments = [args], + mnemonic = "ValidateMinSdkFloor", + progress_message = "Validating AndroidManifest min SDK %s" % str(ctx.label), + ) + manifest_validation_ctx[_VALIDATION_OUTPUTS].append(log) + + return _ManifestValidationContextInfo(**manifest_validation_ctx) + def _process_starlark( ctx, java_package = None, @@ -1672,6 +1785,12 @@ resources = struct( # Exposed for android_local_test and android_library generate_dummy_manifest = _generate_dummy_manifest, + + # Exposed for android_library, aar_import, and android_binary + bump_min_sdk = _bump_min_sdk, + + # Exposed for android_binary + validate_min_sdk = _validate_min_sdk, ) testing = struct( -- cgit v1.2.3 From b59ad96ea94f7c72da2993b06a7e404031943684 Mon Sep 17 00:00:00 2001 From: Joseph Park Date: Tue, 19 Jul 2022 15:57:16 -0700 Subject: Update toolchains to allow file for enforce_min_sdk_floor_tool PiperOrigin-RevId: 461996415 Change-Id: I1de7cda71aba66ae78d011eadde2206a46bf595b --- toolchains/android/toolchain.bzl | 1 + 1 file changed, 1 insertion(+) diff --git a/toolchains/android/toolchain.bzl b/toolchains/android/toolchain.bzl index a692305..1a45c5d 100644 --- a/toolchains/android/toolchain.bzl +++ b/toolchains/android/toolchain.bzl @@ -122,6 +122,7 @@ _ATTRS = dict( executable = True, ), enforce_min_sdk_floor_tool = attr.label( + allow_files = True, cfg = "exec", default = "//src/tools/enforce_min_sdk_floor", executable = True, -- cgit v1.2.3 From ed88be6619f381bf4fd8831cfc6feea066d5f5e1 Mon Sep 17 00:00:00 2001 From: Joseph Park Date: Wed, 20 Jul 2022 10:09:41 -0700 Subject: For actions to validate min SDK floor, do nothing if floor <= 0. PiperOrigin-RevId: 462169599 Change-Id: I07b1317788251caa29dd9c99f166fd920504a6c3 --- rules/resources.bzl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rules/resources.bzl b/rules/resources.bzl index 3e161e7..4bbec37 100644 --- a/rules/resources.bzl +++ b/rules/resources.bzl @@ -1005,13 +1005,13 @@ def _bump_min_sdk( enforce_min_sdk_floor_tool: FilesToRunProvider. The enforce_min_sdk_tool executable or FilesToRunprovider manifest: File. The AndroidManifest.xml file. - floor: int. The min SDK floor. + floor: int. The min SDK floor. Manifest is unchanged if floor <= 0. Returns: - A dict containing _MinSdkFloorEnforcedContextInfo provider fields. + A dict containing _ManifestContextInfo provider fields. """ manifest_ctx = {} - if not manifest: + if not manifest or floor <= 0: manifest_ctx[_MIN_SDK_BUMPED_MANIFEST] = manifest return _ManifestContextInfo(**manifest_ctx) @@ -1054,13 +1054,13 @@ def _validate_min_sdk( enforce_min_sdk_floor_tool: FilesToRunProvider. The enforce_min_sdk_tool executable or FilesToRunprovider manifest: File. The AndroidManifest.xml file. - floor: int. The min SDK floor. + floor: int. The min SDK floor. No validation is done if floor <= 0. Returns: - A dict containing _MinSdkFloorEnforcedContextInfo provider fields. + A dict containing _ManifestValidationContextInfo provider fields. """ manifest_validation_ctx = {_VALIDATION_OUTPUTS: []} - if not manifest: + if not manifest or floor <= 0: return _ManifestValidationContextInfo(**manifest_validation_ctx) args = ctx.actions.args() -- cgit v1.2.3 From 0f8408439eb8826573dc9a9a681bdf17e4919121 Mon Sep 17 00:00:00 2001 From: Joseph Park Date: Wed, 20 Jul 2022 15:34:16 -0700 Subject: Add xml declaration for generated manifests. PiperOrigin-RevId: 462245243 Change-Id: I034fb454280a01907080eabe0df913bc2ca90b88 --- src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py b/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py index 77be5f9..5810170 100644 --- a/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py +++ b/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py @@ -87,7 +87,7 @@ def _BumpMinSdk(xml_content, min_sdk_floor): if uses_sdk is None: ET.SubElement(root, USES_SDK, {MIN_SDK_ATTRIB: str(min_sdk_floor)}) return ( - ET.tostring(root, encoding="utf-8"), + ET.tostring(root, encoding="utf-8", xml_declaration=True), "No uses-sdk element found while floor is specified " + f"({min_sdk_floor}). Min SDK added.") @@ -95,7 +95,7 @@ def _BumpMinSdk(xml_content, min_sdk_floor): if min_sdk is None: uses_sdk.set(MIN_SDK_ATTRIB, str(min_sdk_floor)) return ( - ET.tostring(root, encoding="utf-8"), + ET.tostring(root, encoding="utf-8", xml_declaration=True), "No minSdkVersion attribute found while floor is specified" + f"({min_sdk_floor}). Min SDK added.") @@ -110,7 +110,7 @@ def _BumpMinSdk(xml_content, min_sdk_floor): if min_sdk_int < min_sdk_floor: uses_sdk.set(MIN_SDK_ATTRIB, str(min_sdk_floor)) return ( - ET.tostring(root, encoding="utf-8"), + ET.tostring(root, encoding="utf-8", xml_declaration=True), f"minSdkVersion attribute specified in the manifest ({min_sdk}) " + f"is less than the floor ({min_sdk_floor}). Min SDK replaced.") return ( -- cgit v1.2.3 From 49a8050b6c281e356afeb1755393b907af63f075 Mon Sep 17 00:00:00 2001 From: Joseph Park Date: Wed, 20 Jul 2022 16:21:36 -0700 Subject: Expose the depot min SDK floor from resources.bzl PiperOrigin-RevId: 462254570 Change-Id: I7cf7a70caff9c75b45e007371d023fae16dca515 --- rules/resources.bzl | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/rules/resources.bzl b/rules/resources.bzl index 4bbec37..f4e9d90 100644 --- a/rules/resources.bzl +++ b/rules/resources.bzl @@ -31,6 +31,9 @@ load( _log = "log", ) +# Depot-wide min SDK floor +_DEPOT_MIN_SDK_FLOOR = 14 + _RESOURCE_FOLDER_TYPES = [ "anim", "animator", @@ -69,9 +72,6 @@ _INCORRECT_RESOURCE_LAYOUT_ERROR = ( _VERSION_NAME = "versionName" _VERSION_CODE = "versionCode" -# Min SDK floor -_MIN_SDK_FLOOR = 14 - # Resources context attributes. _ASSETS_PROVIDER = "assets_provider" _DATA_BINDING_LAYOUT_INFO = "data_binding_layout_info" @@ -995,17 +995,17 @@ def _validate_resources(resource_files = None): def _bump_min_sdk( ctx, - enforce_min_sdk_floor_tool, manifest, - floor = _MIN_SDK_FLOOR): + floor, + enforce_min_sdk_floor_tool): """Bumps the min SDK attribute of AndroidManifest to the floor. Args: ctx: The rules context. - enforce_min_sdk_floor_tool: FilesToRunProvider. The enforce_min_sdk_tool executable or - FilesToRunprovider manifest: File. The AndroidManifest.xml file. floor: int. The min SDK floor. Manifest is unchanged if floor <= 0. + enforce_min_sdk_floor_tool: FilesToRunProvider. The enforce_min_sdk_tool executable or + FilesToRunprovider Returns: A dict containing _ManifestContextInfo provider fields. @@ -1044,17 +1044,17 @@ def _bump_min_sdk( def _validate_min_sdk( ctx, - enforce_min_sdk_floor_tool, manifest, - floor = _MIN_SDK_FLOOR): + floor, + enforce_min_sdk_floor_tool): """Validates that the min SDK attribute of AndroidManifest is at least at the floor. Args: ctx: The rules context. - enforce_min_sdk_floor_tool: FilesToRunProvider. The enforce_min_sdk_tool executable or - FilesToRunprovider manifest: File. The AndroidManifest.xml file. floor: int. The min SDK floor. No validation is done if floor <= 0. + enforce_min_sdk_floor_tool: FilesToRunProvider. The enforce_min_sdk_tool executable or + FilesToRunprovider Returns: A dict containing _ManifestValidationContextInfo provider fields. @@ -1791,6 +1791,9 @@ resources = struct( # Exposed for android_binary validate_min_sdk = _validate_min_sdk, + + # Exposed for android_library, aar_import, and android_binary + DEPOT_MIN_SDK_FLOOR = _DEPOT_MIN_SDK_FLOOR, ) testing = struct( -- cgit v1.2.3 From 584505d9c76d875551c15c5782ae02227789a147 Mon Sep 17 00:00:00 2001 From: Joseph Park Date: Wed, 20 Jul 2022 17:47:05 -0700 Subject: Turn on enforce_min_sdk_floor action in aar_import. PiperOrigin-RevId: 462268960 Change-Id: I48ec182c8281a4e1b976423622e67ece2493673f --- rules/aar_import/impl.bzl | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/rules/aar_import/impl.bzl b/rules/aar_import/impl.bzl index a5b9941..e61c248 100644 --- a/rules/aar_import/impl.bzl +++ b/rules/aar_import/impl.bzl @@ -463,11 +463,19 @@ def impl(ctx): unzip_tool, ) + # Bump min SDK to floor + manifest_ctx = _resources.bump_min_sdk( + ctx, + manifest = android_manifest, + floor = _resources.DEPOT_MIN_SDK_FLOOR if _acls.in_enforce_min_sdk_floor_rollout(str(ctx.label)) else 0, + enforce_min_sdk_floor_tool = _get_android_toolchain(ctx).enforce_min_sdk_floor_tool.files_to_run, + ) + resources_ctx = _process_resources( ctx, aar = aar, package = package, - manifest = android_manifest, + manifest = manifest_ctx.min_sdk_bumped_manifest, deps = ctx.attr.deps, aar_resources_extractor_tool = _get_android_toolchain(ctx).aar_resources_extractor.files_to_run, @@ -547,7 +555,7 @@ def impl(ctx): ctx, aar = aar, package = package, - manifest = android_manifest, + manifest = manifest_ctx.min_sdk_bumped_manifest, checks = _get_android_toolchain(ctx).aar_import_checks.files_to_run, )) -- cgit v1.2.3 From fb3afff32c5eaef6cd31845a403e9ee7ceb9c232 Mon Sep 17 00:00:00 2001 From: Joseph Park Date: Wed, 20 Jul 2022 18:01:08 -0700 Subject: Turn on enforce_min_sdk_floor action in android_library. PiperOrigin-RevId: 462270942 Change-Id: I28e95f48e89219be1c97db1ff53a3cb5f23b005d --- rules/android_library/impl.bzl | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/rules/android_library/impl.bzl b/rules/android_library/impl.bzl index f46e227..10ba29b 100644 --- a/rules/android_library/impl.bzl +++ b/rules/android_library/impl.bzl @@ -122,7 +122,20 @@ def _exceptions_processor(ctx, **unused_ctxs): value = _validate_rule_context(ctx), ) -def _process_resources(ctx, java_package, **unused_ctxs): +def _process_manifest(ctx, **unused_ctxs): + manifest_ctx = _resources.bump_min_sdk( + ctx, + manifest = ctx.file.manifest, + floor = _resources.DEPOT_MIN_SDK_FLOOR if acls.in_enforce_min_sdk_floor_rollout(str(ctx.label)) else 0, + enforce_min_sdk_floor_tool = get_android_toolchain(ctx).enforce_min_sdk_floor_tool.files_to_run, + ) + + return ProviderInfo( + name = "manifest_ctx", + value = manifest_ctx, + ) + +def _process_resources(ctx, java_package, manifest_ctx, **unused_ctxs): # exports_manifest can be overridden by a bazel flag. if ctx.attr.exports_manifest == _attrs.tristate.auto: exports_manifest = ctx.fragments.android.get_exports_manifest_default @@ -132,7 +145,7 @@ def _process_resources(ctx, java_package, **unused_ctxs): # Process Android Resources resources_ctx = _resources.process( ctx, - manifest = ctx.file.manifest, + manifest = manifest_ctx.min_sdk_bumped_manifest, resource_files = ctx.attr.resource_files, defined_assets = ctx.attr._defined_assets, assets = ctx.attr.assets, @@ -381,11 +394,11 @@ def _process_native(ctx, idl_ctx, **unused_ctx): ), ) -def _process_intellij(ctx, java_package, resources_ctx, idl_ctx, jvm_ctx, **unused_sub_ctxs): +def _process_intellij(ctx, java_package, manifest_ctx, resources_ctx, idl_ctx, jvm_ctx, **unused_sub_ctxs): android_ide_info = _intellij.make_android_ide_info( ctx, java_package = java_package, - manifest = ctx.file.manifest, + manifest = manifest_ctx.min_sdk_bumped_manifest, defines_resources = resources_ctx.defines_resources, merged_manifest = resources_ctx.merged_manifest, resources_apk = resources_ctx.resources_apk, @@ -428,6 +441,7 @@ def _process_coverage(ctx, **unused_ctx): # insertion. PROCESSORS = dict( ExceptionsProcessor = _exceptions_processor, + ManifestProcessor = _process_manifest, ResourceProcessor = _process_resources, IdlProcessor = _process_idl, DataBindingProcessor = _process_data_binding, -- cgit v1.2.3 From bf67ad7b055d38918631a98d16cd7e11ba01b64e Mon Sep 17 00:00:00 2001 From: Joseph Park Date: Wed, 20 Jul 2022 18:07:35 -0700 Subject: Turn on enforce_min_sdk_floor action for android_binary. PiperOrigin-RevId: 462272106 Change-Id: Ic850cb19b418b07b308ea7184b0e79f4e10e2cf0 --- rules/android_binary_internal/impl.bzl | 43 ++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/rules/android_binary_internal/impl.bzl b/rules/android_binary_internal/impl.bzl index 6838a81..133b50d 100644 --- a/rules/android_binary_internal/impl.bzl +++ b/rules/android_binary_internal/impl.bzl @@ -24,13 +24,26 @@ load( load("@rules_android//rules:resources.bzl", _resources = "resources") load("@rules_android//rules:utils.bzl", "compilation_mode", "get_android_toolchain", "utils") -def _process_resources(ctx, java_package, **unused_ctxs): +def _process_manifest(ctx, **unused_ctxs): + manifest_ctx = _resources.bump_min_sdk( + ctx, + manifest = ctx.file.manifest, + floor = _resources.DEPOT_MIN_SDK_FLOOR if (_is_test_binary(ctx) and acls.in_enforce_min_sdk_floor_rollout(str(ctx.label))) else 0, + enforce_min_sdk_floor_tool = get_android_toolchain(ctx).enforce_min_sdk_floor_tool.files_to_run, + ) + + return ProviderInfo( + name = "manifest_ctx", + value = manifest_ctx, + ) + +def _process_resources(ctx, manifest_ctx, java_package, **unused_ctxs): packaged_resources_ctx = _resources.package( ctx, assets = ctx.files.assets, assets_dir = ctx.attr.assets_dir, resource_files = ctx.files.resource_files, - manifest = ctx.file.manifest, + manifest = manifest_ctx.min_sdk_bumped_manifest, manifest_values = utils.expand_make_vars(ctx, ctx.attr.manifest_values), resource_configs = ctx.attr.resource_configuration_filters, densities = ctx.attr.densities, @@ -59,6 +72,19 @@ def _process_resources(ctx, java_package, **unused_ctxs): value = packaged_resources_ctx, ) +def _validate_manifest(ctx, packaged_resources_ctx, **unused_ctxs): + manifest_validation_ctx = _resources.validate_min_sdk( + ctx, + manifest = packaged_resources_ctx.processed_manifest, + floor = _resources.DEPOT_MIN_SDK_FLOOR if acls.in_enforce_min_sdk_floor_rollout(str(ctx.label)) else 0, + enforce_min_sdk_floor_tool = get_android_toolchain(ctx).enforce_min_sdk_floor_tool.files_to_run, + ) + + return ProviderInfo( + name = "manifest_validation_ctx", + value = manifest_validation_ctx, + ) + def use_legacy_manifest_merger(ctx): """Whether legacy manifest merging is enabled. @@ -86,11 +112,24 @@ def finalize(ctx, providers, validation_outputs, **unused_ctxs): ) return providers +def _is_test_binary(ctx): + """Whether this android_binary target is a test binary. + + Args: + ctx: The context. + + Returns: + Boolean indicating whether the target is a test target. + """ + return ctx.attr.testonly or ctx.attr.instruments or str(ctx.label).find("/javatests/") >= 0 + # Order dependent, as providers will not be available to downstream processors # that may depend on the provider. Iteration order for a dictionary is based on # insertion. PROCESSORS = dict( + ManifestProcessor = _process_manifest, ResourceProcessor = _process_resources, + ValidateManifestProcessor = _validate_manifest, ) _PROCESSING_PIPELINE = processing_pipeline.make_processing_pipeline( -- cgit v1.2.3 From b1de143b5ac7db8aa833197940e87c6514239360 Mon Sep 17 00:00:00 2001 From: Joseph Park Date: Fri, 22 Jul 2022 09:59:23 -0700 Subject: Fix bug where calling register_namespace on reserved prefix causes ValueError PiperOrigin-RevId: 462645481 Change-Id: Id49d68787b28f8d8ebfcc344c6f8e6df14a5e4ea --- src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py b/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py index 5810170..1a265f4 100644 --- a/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py +++ b/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py @@ -80,7 +80,10 @@ def _BumpMinSdk(xml_content, min_sdk_floor): ns_parser.feed(xml_content) ns_parser.close() for _, ns_tuple in ns_parser.read_events(): - ET.register_namespace(ns_tuple[0], ns_tuple[1]) + try: + ET.register_namespace(ns_tuple[0], ns_tuple[1]) + except ValueError: + pass root = ET.fromstring(xml_content) uses_sdk = root.find(USES_SDK) -- cgit v1.2.3 From a7ebf16312b7ff5ac97bfdce4e48e5165ea03c85 Mon Sep 17 00:00:00 2001 From: Zhaoqing Xu Date: Thu, 28 Jul 2022 14:49:31 -0700 Subject: Internal Change PiperOrigin-RevId: 463931020 Change-Id: Ib3529cbe0472616954e95cdde13dc213bc67402e --- rules/acls.bzl | 5 +++-- rules/acls/android_rules_with_kt_rollout.bzl | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/rules/acls.bzl b/rules/acls.bzl index 4633393..f640bb1 100644 --- a/rules/acls.bzl +++ b/rules/acls.bzl @@ -70,7 +70,7 @@ load( load("@rules_android//rules/acls:kt_android_library_rollout.bzl", "KT_ANDROID_LIBRARY_FALLBACK", "KT_ANDROID_LIBRARY_ROLLOUT") load("@rules_android//rules/acls:android_instrumentation_test_manifest_check_rollout.bzl", "ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK", "ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT") load("@rules_android//rules/acls:android_instrumentation_test_prebuilt_test_apk.bzl", "ANDROID_INSTRUMENTATION_TEST_PREBUILT_TEST_APK") -load("@rules_android//rules/acls:android_rules_with_kt_rollout.bzl", "ANDROID_RULES_WITH_KT_ROLLOUT") +load("@rules_android//rules/acls:android_rules_with_kt_rollout.bzl", "ANDROID_RULES_WITH_KT_FALLBACK", "ANDROID_RULES_WITH_KT_ROLLOUT") load("@rules_android//rules/acls:baseline_profiles_rollout.bzl", "BASELINE_PROFILES_ROLLOUT") load("@rules_android//rules/acls:enforce_min_sdk_floor_rollout.bzl", "ENFORCE_MIN_SDK_FLOOR_FALLBACK", "ENFORCE_MIN_SDK_FLOOR_ROLLOUT") @@ -197,7 +197,7 @@ def _in_android_instrumentation_test_prebuilt_test_apk(fqn): return matches(fqn, ANDROID_INSTRUMENTATION_TEST_PREBUILT_TEST_APK_DICT) def _in_android_rules_with_kt_rollout(fqn): - return matches(fqn, ANDROID_RULES_WITH_KT_ROLLOUT) + return not matches(fqn, ANDROID_RULES_WITH_KT_FALLBACK_DICT) and matches(fqn, ANDROID_RULES_WITH_KT_ROLLOUT_DICT) def _get_android_archive_exposed_package_allowlist(fqn): return ANDROID_ARCHIVE_EXPOSED_PACKAGE_ALLOWLIST.get(fqn, []) @@ -240,6 +240,7 @@ ANDROID_LINT_CHECKS_ROLLOUT_DICT = make_dict(ANDROID_LINT_CHECKS_ROLLOUT) ANDROID_LINT_FALLBACK_DICT = make_dict(ANDROID_LINT_FALLBACK) ANDROID_LINT_ROLLOUT_DICT = make_dict(ANDROID_LINT_ROLLOUT) ANDROID_RULES_WITH_KT_ROLLOUT_DICT = make_dict(ANDROID_RULES_WITH_KT_ROLLOUT) +ANDROID_RULES_WITH_KT_FALLBACK_DICT = make_dict(ANDROID_RULES_WITH_KT_FALLBACK) LINT_REGISTRY_FALLBACK_DICT = make_dict(LINT_REGISTRY_FALLBACK) LINT_REGISTRY_ROLLOUT_DICT = make_dict(LINT_REGISTRY_ROLLOUT) diff --git a/rules/acls/android_rules_with_kt_rollout.bzl b/rules/acls/android_rules_with_kt_rollout.bzl index 01564d4..7240b92 100644 --- a/rules/acls/android_rules_with_kt_rollout.bzl +++ b/rules/acls/android_rules_with_kt_rollout.bzl @@ -17,3 +17,6 @@ # keep sorted ANDROID_RULES_WITH_KT_ROLLOUT = [ ] + +ANDROID_RULES_WITH_KT_FALLBACK = [ +] -- cgit v1.2.3 From b7a13271a130de1e7f3817110b12f97845c7b73f Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Tue, 16 Aug 2022 11:19:19 -0700 Subject: internal change PiperOrigin-RevId: 467976461 Change-Id: Idf07231a9696254160d850d9149aaeb1f8001af5 --- kokoro/presubmit/download_bazel.sh | 74 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 kokoro/presubmit/download_bazel.sh diff --git a/kokoro/presubmit/download_bazel.sh b/kokoro/presubmit/download_bazel.sh new file mode 100644 index 0000000..2c96b70 --- /dev/null +++ b/kokoro/presubmit/download_bazel.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# Copyright 2022 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +function DownloadBazel() { + # Utility function to download a specified version of bazel to a given + # installation directory. + # Positional arguments: + # ver: The version to install. Supports "latest" (major and minor releases), + # "latest-with-prereleases" (all versions from "latest" + prereleases), + # major/minor releases such as 5.2.0, and also prereleases such as + # 6.0.0-pre.20220720.3. Release candidates with "rc" in the name are NOT + # supported. + # platform: The platform to install. Currently only "linux" has been + # validated. + # arch: Architecture to install. Currently only "x86_64" has been validated. + # dest: Where to install Bazel. Must be a user-writeable directory, + # otherwise the root user must call this function through sudo. + # Returns: + # Echoes the installation directory at the end of installation. + ( + set -euxo pipefail + # Significantly cribbed from + # devtools/kokoro/vanadium/linux_scripts/usr/local/bin/use_bazel.sh + # Temporary workaround solution until use_bazel.sh can download prereleases. + + # Positional arguments + local ver="$1" + local platform="$2" + local arch="$3" + local dest="$4" + + # Function-local helper variables + local gcs_uri="" + local revision_identifier="" + if [[ "$ver" == "latest" || "$ver" == "latest-with-prereleases" ]]; then + # Query binary blob bucket to find the latest prerelease + if [[ "$ver" == "latest" ]]; then + # Filter out prereleases + ver=$(gsutil ls -l gs://bazel/**/*-installer-"${platform}"-"${arch}".sh | grep "gs://" | grep -v rc | grep -v pre | tail -n1 | awk '{print $NF}') + else + ver=$(gsutil ls -l gs://bazel/**/*-installer-"${platform}"-"${arch}".sh | grep "gs://" | grep -v rc | tail -n1 | awk '{print $NF}') + fi + ver=$(echo "$ver" | sed -n "s/.*bazel\-\(.*\)\-installer.*/\1/p") + fi + if [[ "$ver" =~ pre ]]; then + revision_identifier=$(echo "$ver" | awk -F"-" '{print $1}') + gcs_uri="gs://bazel/${revision_identifier}/rolling/${ver}/bazel-${ver}-installer-${platform}-${arch}.sh" + else + gcs_uri="gs://bazel/${ver}/release/bazel-${ver}-installer-${platform}-${arch}.sh" + fi + + # Download the installer from GCS + gsutil -q cp "$gcs_uri" "$dest"/bazel_installer.sh + mkdir -p "$dest"/install + # Run the installer + bash "$dest"/bazel_installer.sh --prefix="$dest"/install > /dev/null + ls -d "$dest"/install + ) +} + + -- cgit v1.2.3 From 8d34132ebf20914c7232bb34b179397d6dbe4420 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Wed, 17 Aug 2022 13:25:33 -0700 Subject: rules_android WORKSPACEs declare bazel_skylib in maybe() PiperOrigin-RevId: 468278553 Change-Id: I0d0e1c027fd58bb9ec7c779b5ac879b3dcef3af3 --- WORKSPACE | 11 +++++++++++ examples/basicapp/WORKSPACE | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/WORKSPACE b/WORKSPACE index 9c8339a..06e7ab1 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,3 +1,14 @@ workspace(name = "build_bazel_rules_android") +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") +maybe( + http_archive, + name = "bazel_skylib", + urls = [ + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz", + ], + sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c", +) register_toolchains("//android/toolchains/emulator:all") diff --git a/examples/basicapp/WORKSPACE b/examples/basicapp/WORKSPACE index 12ed5b3..1748662 100644 --- a/examples/basicapp/WORKSPACE +++ b/examples/basicapp/WORKSPACE @@ -1,4 +1,5 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") android_sdk_repository( name = "androidsdk", @@ -15,7 +16,8 @@ http_archive( url = "https://github.com/bazelbuild/rules_jvm_external/archive/fa73b1a8e4846cee88240d0019b8f80d39feb1c3.zip", ) -http_archive( +maybe( + http_archive, name = "bazel_skylib", urls = [ "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz", -- cgit v1.2.3 From b1d72e30b77bd87c7b266d4311e6ee6d5dfcbf3f Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Fri, 26 Aug 2022 07:52:16 -0700 Subject: internal change PiperOrigin-RevId: 470245065 Change-Id: I573008f46a958e3f0c9d1f44eb383ee4965eda7a --- kokoro/presubmit/kokoro_presubmit.sh | 16 +++++++++++++++- kokoro/presubmit/presubmit.cfg | 3 ++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/kokoro/presubmit/kokoro_presubmit.sh b/kokoro/presubmit/kokoro_presubmit.sh index 78b5c8e..762e7b0 100644 --- a/kokoro/presubmit/kokoro_presubmit.sh +++ b/kokoro/presubmit/kokoro_presubmit.sh @@ -16,8 +16,22 @@ set -e set -x -bazel="${KOKORO_GFILE_DIR}/bazel-${bazel_version}-linux-x86_64" +source "${KOKORO_GFILE_DIR}/download_bazel.sh" +echo "== installing bazel =========================================" +bazel_install_dir=$(mktemp -d) +BAZEL_VERSION="latest-with-prereleases" +DownloadBazel "$BAZEL_VERSION" linux x86_64 "$bazel_install_dir" +bazel="$bazel_install_dir/install/bin/bazel" chmod +x "$bazel" +bazel_detected_version=$("$bazel" version | grep "Build label" | awk -F": " '{print $2}') +echo "=============================================================" + +function Cleanup() { + # Clean up all temporary directories: bazel install, sandbox, and + # android_tools. + rm -rf "$bazel_install_dir" +} +trap Cleanup EXIT # Kokoro is no longer updating toolchains in their images, so install newer # android build tools, because the latest one installed (26.0.2) has some bug diff --git a/kokoro/presubmit/presubmit.cfg b/kokoro/presubmit/presubmit.cfg index 7ef6a89..8bf3662 100644 --- a/kokoro/presubmit/presubmit.cfg +++ b/kokoro/presubmit/presubmit.cfg @@ -12,5 +12,6 @@ env_vars { } gfile_resources: "/x20/teams/bazel/releases/bazel-$[bazel_version]-linux-x86_64" +gfile_resources: "/google_src/files/head/depot/google3/third_party/bazel_rules/rules_android/kokoro/presubmit/download_bazel.sh" -build_file: "rules_android/kokoro/presubmit/kokoro_presubmit.sh" \ No newline at end of file +build_file: "rules_android/kokoro/presubmit/kokoro_presubmit.sh" -- cgit v1.2.3 From 4cd33400b0cd487cef603b5e89ba94a80ae51e21 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Tue, 6 Sep 2022 07:19:06 -0700 Subject: Remap all references to @rules_android// to '//'. PiperOrigin-RevId: 472451085 Change-Id: I2642cd87afa75131ea807ced4b3a33290aa2de31 --- rules/aar_import/BUILD | 4 +- rules/aar_import/attrs.bzl | 4 +- rules/aar_import/impl.bzl | 14 ++-- rules/aar_import/rule.bzl | 2 +- rules/acls.bzl | 84 +++++++++++----------- .../android_archive_excluded_deps_denylist.bzl | 2 +- .../android_archive_exposed_package_allowlist.bzl | 2 +- ..._instrumentation_derived_test_class_rollout.bzl | 2 +- rules/android_application/BUILD | 4 +- rules/android_application/android_application.bzl | 2 +- .../android_application_rule.bzl | 16 ++--- .../android_application/android_feature_module.bzl | 4 +- .../android_feature_module_rule.bzl | 10 +-- rules/android_application/attrs.bzl | 4 +- rules/android_binary.bzl | 2 +- rules/android_binary_internal/BUILD | 2 +- rules/android_binary_internal/attrs.bzl | 2 +- rules/android_binary_internal/impl.bzl | 10 +-- rules/android_binary_internal/rule.bzl | 4 +- rules/android_library/BUILD | 4 +- rules/android_library/attrs.bzl | 4 +- rules/android_library/impl.bzl | 26 +++---- rules/android_library/rule.bzl | 8 +-- rules/android_tools_defaults_jar.bzl | 2 +- rules/attrs.bzl | 4 +- rules/flags/BUILD | 4 +- rules/flags/flag_defs.bzl | 2 +- rules/flags/flags.bzl | 2 +- rules/utils.bzl | 4 +- 29 files changed, 117 insertions(+), 117 deletions(-) diff --git a/rules/aar_import/BUILD b/rules/aar_import/BUILD index 50d41fe..b57f593 100644 --- a/rules/aar_import/BUILD +++ b/rules/aar_import/BUILD @@ -15,7 +15,7 @@ bzl_library( name = "bzl", srcs = glob(["*.bzl"]), deps = [ - "@rules_android//rules:common_bzl", - "@rules_android//rules/flags:bzl", + "//rules:common_bzl", + "//rules/flags:bzl", ], ) diff --git a/rules/aar_import/attrs.bzl b/rules/aar_import/attrs.bzl index 60b329b..a76cdea 100644 --- a/rules/aar_import/attrs.bzl +++ b/rules/aar_import/attrs.bzl @@ -15,7 +15,7 @@ """Attributes.""" load( - "@rules_android//rules:attrs.bzl", + "//rules:attrs.bzl", _attrs = "attrs", ) @@ -58,7 +58,7 @@ ATTRS = _attrs.add( "artifacts stored within the AAR.", ), _flags = attr.label( - default = "@rules_android//rules/flags", + default = "//rules/flags", ), _java_toolchain = attr.label( default = Label("//tools/jdk:toolchain_android_only"), diff --git a/rules/aar_import/impl.bzl b/rules/aar_import/impl.bzl index e61c248..894baba 100644 --- a/rules/aar_import/impl.bzl +++ b/rules/aar_import/impl.bzl @@ -15,25 +15,25 @@ """Implementation.""" load( - "@rules_android//rules:acls.bzl", + "//rules:acls.bzl", _acls = "acls", ) load( - "@rules_android//rules:common.bzl", + "//rules:common.bzl", _common = "common", ) -load("@rules_android//rules:intellij.bzl", "intellij") +load("//rules:intellij.bzl", "intellij") load( - "@rules_android//rules:java.bzl", + "//rules:java.bzl", _java = "java", ) -load("@rules_android//rules:providers.bzl", "AndroidLintRulesInfo") +load("//rules:providers.bzl", "AndroidLintRulesInfo") load( - "@rules_android//rules:resources.bzl", + "//rules:resources.bzl", _resources = "resources", ) load( - "@rules_android//rules:utils.bzl", + "//rules:utils.bzl", _get_android_toolchain = "get_android_toolchain", _utils = "utils", ) diff --git a/rules/aar_import/rule.bzl b/rules/aar_import/rule.bzl index bb52942..3274c5d 100644 --- a/rules/aar_import/rule.bzl +++ b/rules/aar_import/rule.bzl @@ -43,5 +43,5 @@ aar_import = rule( AndroidNativeLibsInfo, JavaInfo, ], - toolchains = ["@rules_android//toolchains/android:toolchain_type"], + toolchains = ["//toolchains/android:toolchain_type"], ) diff --git a/rules/acls.bzl b/rules/acls.bzl index f640bb1..a1bdb1e 100644 --- a/rules/acls.bzl +++ b/rules/acls.bzl @@ -27,52 +27,52 @@ To update a list: 1. Directly add/remove/edit targets in the appropriate .bzl file """ -load("@rules_android//rules/acls:aar_import_deps_checker.bzl", "AAR_IMPORT_DEPS_CHECKER_FALLBACK", "AAR_IMPORT_DEPS_CHECKER_ROLLOUT") -load("@rules_android//rules/acls:aar_import_explicit_exports_manifest.bzl", "AAR_IMPORT_EXPLICIT_EXPORTS_MANIFEST") -load("@rules_android//rules/acls:aar_import_exports_r_java.bzl", "AAR_IMPORT_EXPORTS_R_JAVA") -load("@rules_android//rules/acls:aar_propagate_resources.bzl", "AAR_PROPAGATE_RESOURCES_FALLBACK", "AAR_PROPAGATE_RESOURCES_ROLLOUT") -load("@rules_android//rules/acls:ait_install_snapshots.bzl", "APP_INSTALLATION_SNAPSHOT", "APP_INSTALLATION_SNAPSHOT_FALLBACK") -load("@rules_android//rules/acls:ait_virtual_device.bzl", "AIT_VIRTUAL_DEVICE_FALLBACK", "AIT_VIRTUAL_DEVICE_ROLLOUT") -load("@rules_android//rules/acls:allow_resource_conflicts.bzl", "ALLOW_RESOURCE_CONFLICTS") -load("@rules_android//rules/acls:android_archive_dogfood.bzl", "ANDROID_ARCHIVE_DOGFOOD") -load("@rules_android//rules/acls:android_archive_excluded_deps_denylist.bzl", "ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST") -load("@rules_android//rules/acls:android_archive_exposed_package_allowlist.bzl", "ANDROID_ARCHIVE_EXPOSED_PACKAGE_ALLOWLIST") -load("@rules_android//rules/acls:android_test_lockdown.bzl", "ANDROID_TEST_LOCKDOWN_GENERATOR_FUNCTIONS", "ANDROID_TEST_LOCKDOWN_TARGETS") -load("@rules_android//rules/acls:android_device_plugin_rollout.bzl", "ANDROID_DEVICE_PLUGIN_FALLBACK", "ANDROID_DEVICE_PLUGIN_ROLLOUT") -load("@rules_android//rules/acls:android_instrumentation_binary_starlark_resources.bzl", "ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK", "ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT") -load("@rules_android//rules/acls:android_binary_starlark_javac.bzl", "ANDROID_BINARY_STARLARK_JAVAC_FALLBACK", "ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT") -load("@rules_android//rules/acls:android_feature_splits_dogfood.bzl", "ANDROID_FEATURE_SPLITS_DOGFOOD") -load("@rules_android//rules/acls:android_library_implicit_exports.bzl", "ANDROID_LIBRARY_IMPLICIT_EXPORTS", "ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS") -load("@rules_android//rules/acls:android_library_resources_without_srcs.bzl", "ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS", "ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS") -load("@rules_android//rules/acls:android_library_starlark_resource_outputs.bzl", "ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK", "ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT") -load("@rules_android//rules/acls:android_lint_checks_rollout.bzl", "ANDROID_LINT_CHECKS_FALLBACK", "ANDROID_LINT_CHECKS_ROLLOUT") -load("@rules_android//rules/acls:android_lint_rollout.bzl", "ANDROID_LINT_FALLBACK", "ANDROID_LINT_ROLLOUT") -load("@rules_android//rules/acls:lint_registry_rollout.bzl", "LINT_REGISTRY_FALLBACK", "LINT_REGISTRY_ROLLOUT") -load("@rules_android//rules/acls:android_build_stamping_rollout.bzl", "ANDROID_BUILD_STAMPING_FALLBACK", "ANDROID_BUILD_STAMPING_ROLLOUT") -load("@rules_android//rules/acls:b122039567.bzl", "B122039567") -load("@rules_android//rules/acls:b123854163.bzl", "B123854163") -load("@rules_android//rules/acls:dex2oat_opts.bzl", "CAN_USE_DEX2OAT_OPTIONS") -load("@rules_android//rules/acls:fix_export_exporting_rollout.bzl", "FIX_EXPORT_EXPORTING_FALLBACK", "FIX_EXPORT_EXPORTING_ROLLOUT") -load("@rules_android//rules/acls:fix_resource_transitivity_rollout.bzl", "FIX_RESOURCE_TRANSITIVITY_FALLBACK", "FIX_RESOURCE_TRANSITIVITY_ROLLOUT") -load("@rules_android//rules/acls:host_dex2oat_rollout.bzl", "AIT_USE_HOST_DEX2OAT_ROLLOUT", "AIT_USE_HOST_DEX2OAT_ROLLOUT_FALLBACK") -load("@rules_android//rules/acls:install_apps_in_data.bzl", "INSTALL_APPS_IN_DATA") -load("@rules_android//rules/acls:local_test_multi_proto.bzl", "LOCAL_TEST_MULTI_PROTO_PKG") -load("@rules_android//rules/acls:local_test_rollout.bzl", "LOCAL_TEST_FALLBACK", "LOCAL_TEST_ROLLOUT") -load("@rules_android//rules/acls:local_test_starlark_resources.bzl", "LOCAL_TEST_STARLARK_RESOURCES_FALLBACK", "LOCAL_TEST_STARLARK_RESOURCES_ROLLOUT") -load("@rules_android//rules/acls:android_test_platform_rollout.bzl", "ANDROID_TEST_PLATFORM_FALLBACK", "ANDROID_TEST_PLATFORM_ROLLOUT") -load("@rules_android//rules/acls:sourceless_binary_rollout.bzl", "SOURCELESS_BINARY_FALLBACK", "SOURCELESS_BINARY_ROLLOUT") -load("@rules_android//rules/acls:test_to_instrument_test_rollout.bzl", "TEST_TO_INSTRUMENT_TEST_FALLBACK", "TEST_TO_INSTRUMENT_TEST_ROLLOUT") +load("//rules/acls:aar_import_deps_checker.bzl", "AAR_IMPORT_DEPS_CHECKER_FALLBACK", "AAR_IMPORT_DEPS_CHECKER_ROLLOUT") +load("//rules/acls:aar_import_explicit_exports_manifest.bzl", "AAR_IMPORT_EXPLICIT_EXPORTS_MANIFEST") +load("//rules/acls:aar_import_exports_r_java.bzl", "AAR_IMPORT_EXPORTS_R_JAVA") +load("//rules/acls:aar_propagate_resources.bzl", "AAR_PROPAGATE_RESOURCES_FALLBACK", "AAR_PROPAGATE_RESOURCES_ROLLOUT") +load("//rules/acls:ait_install_snapshots.bzl", "APP_INSTALLATION_SNAPSHOT", "APP_INSTALLATION_SNAPSHOT_FALLBACK") +load("//rules/acls:ait_virtual_device.bzl", "AIT_VIRTUAL_DEVICE_FALLBACK", "AIT_VIRTUAL_DEVICE_ROLLOUT") +load("//rules/acls:allow_resource_conflicts.bzl", "ALLOW_RESOURCE_CONFLICTS") +load("//rules/acls:android_archive_dogfood.bzl", "ANDROID_ARCHIVE_DOGFOOD") +load("//rules/acls:android_archive_excluded_deps_denylist.bzl", "ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST") +load("//rules/acls:android_archive_exposed_package_allowlist.bzl", "ANDROID_ARCHIVE_EXPOSED_PACKAGE_ALLOWLIST") +load("//rules/acls:android_test_lockdown.bzl", "ANDROID_TEST_LOCKDOWN_GENERATOR_FUNCTIONS", "ANDROID_TEST_LOCKDOWN_TARGETS") +load("//rules/acls:android_device_plugin_rollout.bzl", "ANDROID_DEVICE_PLUGIN_FALLBACK", "ANDROID_DEVICE_PLUGIN_ROLLOUT") +load("//rules/acls:android_instrumentation_binary_starlark_resources.bzl", "ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK", "ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT") +load("//rules/acls:android_binary_starlark_javac.bzl", "ANDROID_BINARY_STARLARK_JAVAC_FALLBACK", "ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT") +load("//rules/acls:android_feature_splits_dogfood.bzl", "ANDROID_FEATURE_SPLITS_DOGFOOD") +load("//rules/acls:android_library_implicit_exports.bzl", "ANDROID_LIBRARY_IMPLICIT_EXPORTS", "ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS") +load("//rules/acls:android_library_resources_without_srcs.bzl", "ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS", "ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS") +load("//rules/acls:android_library_starlark_resource_outputs.bzl", "ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK", "ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT") +load("//rules/acls:android_lint_checks_rollout.bzl", "ANDROID_LINT_CHECKS_FALLBACK", "ANDROID_LINT_CHECKS_ROLLOUT") +load("//rules/acls:android_lint_rollout.bzl", "ANDROID_LINT_FALLBACK", "ANDROID_LINT_ROLLOUT") +load("//rules/acls:lint_registry_rollout.bzl", "LINT_REGISTRY_FALLBACK", "LINT_REGISTRY_ROLLOUT") +load("//rules/acls:android_build_stamping_rollout.bzl", "ANDROID_BUILD_STAMPING_FALLBACK", "ANDROID_BUILD_STAMPING_ROLLOUT") +load("//rules/acls:b122039567.bzl", "B122039567") +load("//rules/acls:b123854163.bzl", "B123854163") +load("//rules/acls:dex2oat_opts.bzl", "CAN_USE_DEX2OAT_OPTIONS") +load("//rules/acls:fix_export_exporting_rollout.bzl", "FIX_EXPORT_EXPORTING_FALLBACK", "FIX_EXPORT_EXPORTING_ROLLOUT") +load("//rules/acls:fix_resource_transitivity_rollout.bzl", "FIX_RESOURCE_TRANSITIVITY_FALLBACK", "FIX_RESOURCE_TRANSITIVITY_ROLLOUT") +load("//rules/acls:host_dex2oat_rollout.bzl", "AIT_USE_HOST_DEX2OAT_ROLLOUT", "AIT_USE_HOST_DEX2OAT_ROLLOUT_FALLBACK") +load("//rules/acls:install_apps_in_data.bzl", "INSTALL_APPS_IN_DATA") +load("//rules/acls:local_test_multi_proto.bzl", "LOCAL_TEST_MULTI_PROTO_PKG") +load("//rules/acls:local_test_rollout.bzl", "LOCAL_TEST_FALLBACK", "LOCAL_TEST_ROLLOUT") +load("//rules/acls:local_test_starlark_resources.bzl", "LOCAL_TEST_STARLARK_RESOURCES_FALLBACK", "LOCAL_TEST_STARLARK_RESOURCES_ROLLOUT") +load("//rules/acls:android_test_platform_rollout.bzl", "ANDROID_TEST_PLATFORM_FALLBACK", "ANDROID_TEST_PLATFORM_ROLLOUT") +load("//rules/acls:sourceless_binary_rollout.bzl", "SOURCELESS_BINARY_FALLBACK", "SOURCELESS_BINARY_ROLLOUT") +load("//rules/acls:test_to_instrument_test_rollout.bzl", "TEST_TO_INSTRUMENT_TEST_FALLBACK", "TEST_TO_INSTRUMENT_TEST_ROLLOUT") load( - "@rules_android//rules/acls:partial_jetification_targets.bzl", + "//rules/acls:partial_jetification_targets.bzl", "PARTIAL_JETIFICATION_TARGETS_FALLBACK", "PARTIAL_JETIFICATION_TARGETS_ROLLOUT", ) -load("@rules_android//rules/acls:kt_android_library_rollout.bzl", "KT_ANDROID_LIBRARY_FALLBACK", "KT_ANDROID_LIBRARY_ROLLOUT") -load("@rules_android//rules/acls:android_instrumentation_test_manifest_check_rollout.bzl", "ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK", "ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT") -load("@rules_android//rules/acls:android_instrumentation_test_prebuilt_test_apk.bzl", "ANDROID_INSTRUMENTATION_TEST_PREBUILT_TEST_APK") -load("@rules_android//rules/acls:android_rules_with_kt_rollout.bzl", "ANDROID_RULES_WITH_KT_FALLBACK", "ANDROID_RULES_WITH_KT_ROLLOUT") -load("@rules_android//rules/acls:baseline_profiles_rollout.bzl", "BASELINE_PROFILES_ROLLOUT") -load("@rules_android//rules/acls:enforce_min_sdk_floor_rollout.bzl", "ENFORCE_MIN_SDK_FLOOR_FALLBACK", "ENFORCE_MIN_SDK_FLOOR_ROLLOUT") +load("//rules/acls:kt_android_library_rollout.bzl", "KT_ANDROID_LIBRARY_FALLBACK", "KT_ANDROID_LIBRARY_ROLLOUT") +load("//rules/acls:android_instrumentation_test_manifest_check_rollout.bzl", "ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_FALLBACK", "ANDROID_INSTRUMENTATION_TEST_MANIFEST_CHECK_ROLLOUT") +load("//rules/acls:android_instrumentation_test_prebuilt_test_apk.bzl", "ANDROID_INSTRUMENTATION_TEST_PREBUILT_TEST_APK") +load("//rules/acls:android_rules_with_kt_rollout.bzl", "ANDROID_RULES_WITH_KT_FALLBACK", "ANDROID_RULES_WITH_KT_ROLLOUT") +load("//rules/acls:baseline_profiles_rollout.bzl", "BASELINE_PROFILES_ROLLOUT") +load("//rules/acls:enforce_min_sdk_floor_rollout.bzl", "ENFORCE_MIN_SDK_FLOOR_FALLBACK", "ENFORCE_MIN_SDK_FLOOR_ROLLOUT") def _in_aar_import_deps_checker(fqn): return not matches(fqn, AAR_IMPORT_DEPS_CHECKER_FALLBACK_DICT) and matches(fqn, AAR_IMPORT_DEPS_CHECKER_ROLLOUT_DICT) diff --git a/rules/acls/android_archive_excluded_deps_denylist.bzl b/rules/acls/android_archive_excluded_deps_denylist.bzl index 2a46e68..ba9fe07 100644 --- a/rules/acls/android_archive_excluded_deps_denylist.bzl +++ b/rules/acls/android_archive_excluded_deps_denylist.bzl @@ -17,5 +17,5 @@ # keep sorted ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST = [ # Failure test support. - "@rules_android//test/rules/android_archive/java/com/testdata/denied:__pkg__", + "//test/rules/android_archive/java/com/testdata/denied:__pkg__", ] diff --git a/rules/acls/android_archive_exposed_package_allowlist.bzl b/rules/acls/android_archive_exposed_package_allowlist.bzl index 5825c06..0803134 100644 --- a/rules/acls/android_archive_exposed_package_allowlist.bzl +++ b/rules/acls/android_archive_exposed_package_allowlist.bzl @@ -18,5 +18,5 @@ # exposed package checks. # keep sorted ANDROID_ARCHIVE_EXPOSED_PACKAGE_ALLOWLIST = { - "@rules_android//test/rules/android_archive/java/com/testdata:archive_denied_package_allowlisted": ["androidx.test"], + "//test/rules/android_archive/java/com/testdata:archive_denied_package_allowlisted": ["androidx.test"], } diff --git a/rules/acls/android_instrumentation_derived_test_class_rollout.bzl b/rules/acls/android_instrumentation_derived_test_class_rollout.bzl index 427490c..d00129d 100644 --- a/rules/acls/android_instrumentation_derived_test_class_rollout.bzl +++ b/rules/acls/android_instrumentation_derived_test_class_rollout.bzl @@ -14,7 +14,7 @@ """Rollout list for enabling test class derivation in android_instrumentation_test,""" -load("@rules_android//rules:acls.bzl", "make_dict", "matches") +load("//rules:acls.bzl", "make_dict", "matches") _ANDROID_INSTRUMENTATION_TEST_DERIVED_TEST_CLASS_ROLLOUT = [ "//:__subpackages__", diff --git a/rules/android_application/BUILD b/rules/android_application/BUILD index 31cf119..45f31fe 100644 --- a/rules/android_application/BUILD +++ b/rules/android_application/BUILD @@ -21,7 +21,7 @@ bzl_library( name = "bzl", srcs = glob(["*.bzl"]), deps = [ - "@rules_android//rules:common_bzl", - "@rules_android//rules/flags:bzl", + "//rules:common_bzl", + "//rules/flags:bzl", ], ) diff --git a/rules/android_application/android_application.bzl b/rules/android_application/android_application.bzl index f7e1710..4c666f1 100644 --- a/rules/android_application/android_application.bzl +++ b/rules/android_application/android_application.bzl @@ -18,7 +18,7 @@ This file exists to inject the correct version of android_binary. """ load(":android_application_rule.bzl", _android_application_macro = "android_application_macro") -load("@rules_android//rules:android_binary.bzl", _android_binary = "android_binary") +load("//rules:android_binary.bzl", _android_binary = "android_binary") def android_application(**attrs): """Rule to build an Android Application (app bundle). diff --git a/rules/android_application/android_application_rule.bzl b/rules/android_application/android_application_rule.bzl index 760faf0..05b3b22 100644 --- a/rules/android_application/android_application_rule.bzl +++ b/rules/android_application/android_application_rule.bzl @@ -17,33 +17,33 @@ load(":android_feature_module_rule.bzl", "get_feature_module_paths") load(":attrs.bzl", "ANDROID_APPLICATION_ATTRS") load( - "@rules_android//rules:aapt.bzl", + "//rules:aapt.bzl", _aapt = "aapt", ) load( - "@rules_android//rules:bundletool.bzl", + "//rules:bundletool.bzl", _bundletool = "bundletool", ) load( - "@rules_android//rules:busybox.bzl", + "//rules:busybox.bzl", _busybox = "busybox", ) load( - "@rules_android//rules:common.bzl", + "//rules:common.bzl", _common = "common", ) load( - "@rules_android//rules:java.bzl", + "//rules:java.bzl", _java = "java", ) load( - "@rules_android//rules:providers.bzl", + "//rules:providers.bzl", "AndroidBundleInfo", "AndroidFeatureModuleInfo", "StarlarkAndroidResourcesInfo", ) load( - "@rules_android//rules:utils.bzl", + "//rules:utils.bzl", "get_android_toolchain", _log = "log", ) @@ -335,7 +335,7 @@ android_application = rule( "deploy_script": "%{name}.sh", "unsigned_aab": "%{name}_unsigned.aab", }, - toolchains = ["@rules_android//toolchains/android:toolchain_type"], + toolchains = ["//toolchains/android:toolchain_type"], _skylark_testable = True, ) diff --git a/rules/android_application/android_feature_module.bzl b/rules/android_application/android_feature_module.bzl index 840d961..6b8732a 100644 --- a/rules/android_application/android_feature_module.bzl +++ b/rules/android_application/android_feature_module.bzl @@ -22,11 +22,11 @@ load( _android_feature_module_macro = "android_feature_module_macro", ) load( - "@rules_android//rules:android_binary.bzl", + "//rules:android_binary.bzl", _android_binary = "android_binary", ) load( - "@rules_android//rules/android_library:rule.bzl", + "//rules/android_library:rule.bzl", _android_library_macro = "android_library_macro", ) diff --git a/rules/android_application/android_feature_module_rule.bzl b/rules/android_application/android_feature_module_rule.bzl index b33cbcd..ec051d5 100644 --- a/rules/android_application/android_feature_module_rule.bzl +++ b/rules/android_application/android_feature_module_rule.bzl @@ -15,14 +15,14 @@ """android_feature_module rule.""" load(":attrs.bzl", "ANDROID_FEATURE_MODULE_ATTRS") -load("@rules_android//rules:java.bzl", _java = "java") +load("//rules:java.bzl", _java = "java") load( - "@rules_android//rules:providers.bzl", + "//rules:providers.bzl", "AndroidFeatureModuleInfo", ) -load("@rules_android//rules:acls.bzl", "acls") +load("//rules:acls.bzl", "acls") load( - "@rules_android//rules:utils.bzl", + "//rules:utils.bzl", "get_android_toolchain", ) @@ -77,7 +77,7 @@ android_feature_module = rule( ], implementation = _impl, provides = [AndroidFeatureModuleInfo], - toolchains = ["@rules_android//toolchains/android:toolchain_type"], + toolchains = ["//toolchains/android:toolchain_type"], _skylark_testable = True, ) diff --git a/rules/android_application/attrs.bzl b/rules/android_application/attrs.bzl index de16964..77f24e3 100644 --- a/rules/android_application/attrs.bzl +++ b/rules/android_application/attrs.bzl @@ -15,7 +15,7 @@ """Attributes for android_application.""" load( - "@rules_android//rules:attrs.bzl", + "//rules:attrs.bzl", _attrs = "attrs", ) @@ -48,7 +48,7 @@ ANDROID_APPLICATION_ATTRS = _attrs.add( ), _bundle_keystore_properties = attr.label( allow_single_file = True, - default = "@rules_android//rules:bundle_keystore_properties.tmpl", + default = "//rules:bundle_keystore_properties.tmpl", ), _feature_manifest_script = attr.label( allow_single_file = True, diff --git a/rules/android_binary.bzl b/rules/android_binary.bzl index 5d33512..3772c5e 100644 --- a/rules/android_binary.bzl +++ b/rules/android_binary.bzl @@ -15,7 +15,7 @@ """Bazel rule for building an APK.""" load(":migration_tag_DONOTUSE.bzl", "add_migration_tag") -load("@rules_android//rules/android_binary_internal:rule.bzl", "android_binary_internal_macro") +load("//rules/android_binary_internal:rule.bzl", "android_binary_internal_macro") def android_binary(**attrs): """Bazel android_binary rule. diff --git a/rules/android_binary_internal/BUILD b/rules/android_binary_internal/BUILD index 2440016..aa1e0ef 100644 --- a/rules/android_binary_internal/BUILD +++ b/rules/android_binary_internal/BUILD @@ -13,6 +13,6 @@ bzl_library( name = "bzl", srcs = glob(["*.bzl"]), deps = [ - "@rules_android//rules:common_bzl", + "//rules:common_bzl", ], ) diff --git a/rules/android_binary_internal/attrs.bzl b/rules/android_binary_internal/attrs.bzl index 5a7bca7..b9658d4 100644 --- a/rules/android_binary_internal/attrs.bzl +++ b/rules/android_binary_internal/attrs.bzl @@ -15,7 +15,7 @@ """Attributes.""" load( - "@rules_android//rules:attrs.bzl", + "//rules:attrs.bzl", _attrs = "attrs", ) diff --git a/rules/android_binary_internal/impl.bzl b/rules/android_binary_internal/impl.bzl index 133b50d..847844a 100644 --- a/rules/android_binary_internal/impl.bzl +++ b/rules/android_binary_internal/impl.bzl @@ -14,15 +14,15 @@ """Implementation.""" -load("@rules_android//rules:acls.bzl", "acls") -load("@rules_android//rules:java.bzl", "java") +load("//rules:acls.bzl", "acls") +load("//rules:java.bzl", "java") load( - "@rules_android//rules:processing_pipeline.bzl", + "//rules:processing_pipeline.bzl", "ProviderInfo", "processing_pipeline", ) -load("@rules_android//rules:resources.bzl", _resources = "resources") -load("@rules_android//rules:utils.bzl", "compilation_mode", "get_android_toolchain", "utils") +load("//rules:resources.bzl", _resources = "resources") +load("//rules:utils.bzl", "compilation_mode", "get_android_toolchain", "utils") def _process_manifest(ctx, **unused_ctxs): manifest_ctx = _resources.bump_min_sdk( diff --git a/rules/android_binary_internal/rule.bzl b/rules/android_binary_internal/rule.bzl index 6dc2589..ec8abf3 100644 --- a/rules/android_binary_internal/rule.bzl +++ b/rules/android_binary_internal/rule.bzl @@ -17,7 +17,7 @@ load(":attrs.bzl", "ATTRS") load(":impl.bzl", "impl") load( - "@rules_android//rules:attrs.bzl", + "//rules:attrs.bzl", _attrs = "attrs", ) @@ -43,7 +43,7 @@ def make_rule( attrs = attrs, implementation = implementation, provides = provides, - toolchains = ["@rules_android//toolchains/android:toolchain_type"], + toolchains = ["//toolchains/android:toolchain_type"], _skylark_testable = True, fragments = [ "android", diff --git a/rules/android_library/BUILD b/rules/android_library/BUILD index 09ca5b8..4eaeaf3 100644 --- a/rules/android_library/BUILD +++ b/rules/android_library/BUILD @@ -15,7 +15,7 @@ bzl_library( name = "bzl", srcs = glob(["*.bzl"]), deps = [ - "@rules_android//rules:common_bzl", - "@rules_android//rules/flags:bzl", + "//rules:common_bzl", + "//rules/flags:bzl", ], ) diff --git a/rules/android_library/attrs.bzl b/rules/android_library/attrs.bzl index db7ceb7..9fc3214 100644 --- a/rules/android_library/attrs.bzl +++ b/rules/android_library/attrs.bzl @@ -15,7 +15,7 @@ """Attributes.""" load( - "@rules_android//rules:attrs.bzl", + "//rules:attrs.bzl", _attrs = "attrs", ) @@ -183,7 +183,7 @@ ATTRS = _attrs.add( # TODO(str): Remove when fully migrated to android_instrumentation_test _android_test_migration = attr.bool(default = False), _flags = attr.label( - default = "@rules_android//rules/flags", + default = "//rules/flags", ), _package_name = attr.string(), # for sending the package name to the outputs callback ), diff --git a/rules/android_library/impl.bzl b/rules/android_library/impl.bzl index 10ba29b..919b9f3 100644 --- a/rules/android_library/impl.bzl +++ b/rules/android_library/impl.bzl @@ -14,23 +14,23 @@ """Implementation.""" -load("@rules_android//rules:acls.bzl", "acls") -load("@rules_android//rules:attrs.bzl", _attrs = "attrs") -load("@rules_android//rules:common.bzl", _common = "common") -load("@rules_android//rules:data_binding.bzl", _data_binding = "data_binding") -load("@rules_android//rules:idl.bzl", _idl = "idl") -load("@rules_android//rules:intellij.bzl", _intellij = "intellij") -load("@rules_android//rules:java.bzl", _java = "java") +load("//rules:acls.bzl", "acls") +load("//rules:attrs.bzl", _attrs = "attrs") +load("//rules:common.bzl", _common = "common") +load("//rules:data_binding.bzl", _data_binding = "data_binding") +load("//rules:idl.bzl", _idl = "idl") +load("//rules:intellij.bzl", _intellij = "intellij") +load("//rules:java.bzl", _java = "java") load( - "@rules_android//rules:processing_pipeline.bzl", + "//rules:processing_pipeline.bzl", "ProviderInfo", "processing_pipeline", ) -load("@rules_android//rules:proguard.bzl", _proguard = "proguard") -load("@rules_android//rules:providers.bzl", "AndroidLintRulesInfo") -load("@rules_android//rules:resources.bzl", _resources = "resources") -load("@rules_android//rules:utils.bzl", "get_android_sdk", "get_android_toolchain", "log", "utils") -load("@rules_android//rules/flags:flags.bzl", _flags = "flags") +load("//rules:proguard.bzl", _proguard = "proguard") +load("//rules:providers.bzl", "AndroidLintRulesInfo") +load("//rules:resources.bzl", _resources = "resources") +load("//rules:utils.bzl", "get_android_sdk", "get_android_toolchain", "log", "utils") +load("//rules/flags:flags.bzl", _flags = "flags") _USES_DEPRECATED_IMPLICIT_EXPORT_ERROR = ( "The android_library rule will be deprecating the use of deps to export " + diff --git a/rules/android_library/rule.bzl b/rules/android_library/rule.bzl index e421218..30b928a 100644 --- a/rules/android_library/rule.bzl +++ b/rules/android_library/rule.bzl @@ -14,11 +14,11 @@ """android_library rule.""" -load("@rules_android//rules:acls.bzl", "acls") +load("//rules:acls.bzl", "acls") load(":attrs.bzl", _ATTRS = "ATTRS") load(":impl.bzl", _impl = "impl") load( - "@rules_android//rules:attrs.bzl", + "//rules:attrs.bzl", _attrs = "attrs", ) @@ -160,8 +160,8 @@ def make_rule( ], outputs = outputs, toolchains = [ - "@rules_android//toolchains/android:toolchain_type", - "@rules_android//toolchains/android_sdk:toolchain_type", + "//toolchains/android:toolchain_type", + "//toolchains/android_sdk:toolchain_type", ] + additional_toolchains, _skylark_testable = True, ) diff --git a/rules/android_tools_defaults_jar.bzl b/rules/android_tools_defaults_jar.bzl index b226882..df937e6 100644 --- a/rules/android_tools_defaults_jar.bzl +++ b/rules/android_tools_defaults_jar.bzl @@ -28,5 +28,5 @@ android_tools_defaults_jar = rule( attrs = ANDROID_TOOLS_DEFAULTS_JAR_ATTRS, implementation = _impl, fragments = ["android"], - toolchains = ["@rules_android//toolchains/android_sdk:toolchain_type"], + toolchains = ["//toolchains/android_sdk:toolchain_type"], ) diff --git a/rules/attrs.bzl b/rules/attrs.bzl index e7917b3..43408ab 100644 --- a/rules/attrs.bzl +++ b/rules/attrs.bzl @@ -79,7 +79,7 @@ _tristate = struct( _JAVA_RUNTIME = dict( _host_javabase = attr.label( cfg = "host", - default = Label("@rules_android//tools/jdk:current_java_runtime"), + default = Label("//tools/jdk:current_java_runtime"), ), ) @@ -200,7 +200,7 @@ _DATA_CONTEXT = _add( # TODO(b/145617058) Switching back to head RPBB until the Android rules release process is improved _android_resources_busybox = attr.label( cfg = "host", - default = Label("@rules_android//rules:ResourceProcessorBusyBox"), + default = Label("//rules:ResourceProcessorBusyBox"), executable = True, ), _xsltproc_tool = attr.label( diff --git a/rules/flags/BUILD b/rules/flags/BUILD index 4895b09..a9a585c 100644 --- a/rules/flags/BUILD +++ b/rules/flags/BUILD @@ -1,7 +1,7 @@ # Flags for Android rules and mobile-install -load("@rules_android//rules/flags:flags.bzl", "flags") -load("@rules_android//rules/flags:flag_defs.bzl", "define_flags") +load("//rules/flags:flags.bzl", "flags") +load("//rules/flags:flag_defs.bzl", "define_flags") load("@bazel_skylib//:bzl_library.bzl", "bzl_library") diff --git a/rules/flags/flag_defs.bzl b/rules/flags/flag_defs.bzl index 08eee2d..9773ca9 100644 --- a/rules/flags/flag_defs.bzl +++ b/rules/flags/flag_defs.bzl @@ -14,7 +14,7 @@ """Flag definitions.""" -load("@rules_android//rules/flags:flags.bzl", "flags") +load("//rules/flags:flags.bzl", "flags") def define_flags(): flags.DEFINE_bool( diff --git a/rules/flags/flags.bzl b/rules/flags/flags.bzl index e6e25a6..5756592 100644 --- a/rules/flags/flags.bzl +++ b/rules/flags/flags.bzl @@ -14,7 +14,7 @@ """Bazel Flags.""" -load("@rules_android//rules:utils.bzl", "utils") +load("//rules:utils.bzl", "utils") _BoolFlagInfo = provider( doc = "Provides information about a boolean flag", diff --git a/rules/utils.bzl b/rules/utils.bzl index d796371..e9a28cc 100644 --- a/rules/utils.bzl +++ b/rules/utils.bzl @@ -414,11 +414,11 @@ VALIDATION_OUT={validation_out} ) def get_android_toolchain(ctx): - return ctx.toolchains["@rules_android//toolchains/android:toolchain_type"] + return ctx.toolchains["//toolchains/android:toolchain_type"] def get_android_sdk(ctx): if hasattr(ctx.fragments.android, "incompatible_use_toolchain_resolution") and ctx.fragments.android.incompatible_use_toolchain_resolution: - return ctx.toolchains["@rules_android//toolchains/android_sdk:toolchain_type"].android_sdk_info + return ctx.toolchains["//toolchains/android_sdk:toolchain_type"].android_sdk_info else: return ctx.attr._android_sdk[AndroidSdkInfo] -- cgit v1.2.3 From 1d03f62108319abe207f961de30bfba3e2dd3ed6 Mon Sep 17 00:00:00 2001 From: A Googler Date: Tue, 13 Sep 2022 10:18:16 -0700 Subject: Internal change PiperOrigin-RevId: 474055086 Change-Id: Ie69b060bf9de84d41b4dce9ac44e7085c81ae227 --- rules/bundletool.bzl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/rules/bundletool.bzl b/rules/bundletool.bzl index abb305e..cfc3e48 100644 --- a/rules/bundletool.bzl +++ b/rules/bundletool.bzl @@ -197,7 +197,8 @@ def _bundle_to_apks( ctx, out = None, bundle = None, - universal = False, + mode = None, + system_apk_options = None, device_spec = None, keystore = None, oldest_signer = None, @@ -214,8 +215,13 @@ def _bundle_to_apks( args.add("--bundle", bundle) args.add("--aapt2", aapt2.executable.path) - if universal: - args.add("--mode=universal") + if mode: + args.add("--mode", mode) + + if system_apk_options: + if mode != "SYSTEM": + fail("Unexpected system_apk_options specified, requires SYSTEM mode but got %s" % mode) + args.add_joined("--system-apk-options", system_apk_options, join_with = ",") if keystore: args.add("--ks", keystore.path) -- cgit v1.2.3 From fd4f7cea743e8ef038defcaf8e3c9e06b2132bc6 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Wed, 14 Sep 2022 12:26:41 -0700 Subject: Open-source AK tools: link, mindex PiperOrigin-RevId: 474360411 Change-Id: Idb156ed47d6de59fb276a89d9ca8b765febfb653 --- WORKSPACE | 16 +++++ src/tools/ak/link/BUILD | 28 +++++++++ src/tools/ak/link/link.go | 123 ++++++++++++++++++++++++++++++++++++++ src/tools/ak/link/link_bin.go | 29 +++++++++ src/tools/ak/mindex/BUILD | 21 +++++++ src/tools/ak/mindex/mindex.go | 108 +++++++++++++++++++++++++++++++++ src/tools/ak/mindex/mindex_bin.go | 29 +++++++++ 7 files changed, 354 insertions(+) create mode 100644 src/tools/ak/link/BUILD create mode 100644 src/tools/ak/link/link.go create mode 100644 src/tools/ak/link/link_bin.go create mode 100644 src/tools/ak/mindex/BUILD create mode 100644 src/tools/ak/mindex/mindex.go create mode 100644 src/tools/ak/mindex/mindex_bin.go diff --git a/WORKSPACE b/WORKSPACE index 06e7ab1..6949549 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -12,3 +12,19 @@ maybe( sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c", ) register_toolchains("//android/toolchains/emulator:all") + +maybe( + http_archive, + name = "io_bazel_rules_go", + sha256 = "16e9fca53ed6bd4ff4ad76facc9b7b651a89db1689a2877d6fd7b82aa824e366", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.34.0/rules_go-v0.34.0.zip", + "https://github.com/bazelbuild/rules_go/releases/download/v0.34.0/rules_go-v0.34.0.zip", + ], +) + +load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") + +go_rules_dependencies() + +go_register_toolchains(version = "1.18.3") diff --git a/src/tools/ak/link/BUILD b/src/tools/ak/link/BUILD new file mode 100644 index 0000000..21282db --- /dev/null +++ b/src/tools/ak/link/BUILD @@ -0,0 +1,28 @@ +load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test", "go_proto_library") +# Description: +# Package for link module +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +go_binary( + name = "link_bin", + srcs = ["link_bin.go"], + deps = [ + ":link", + "//src/common/golang:flagfile", + ], +) + +go_library( + name = "link", + srcs = [ + "link.go", + ], + deps = [ + "//src/common/golang:flags", + "//src/common/golang:walk", + "//src/common/golang:ziputils", + "//src/tools/ak:types", + ], +) diff --git a/src/tools/ak/link/link.go b/src/tools/ak/link/link.go new file mode 100644 index 0000000..4850ae7 --- /dev/null +++ b/src/tools/ak/link/link.go @@ -0,0 +1,123 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package link is a thin wrapper around aapt2 to link android resources. +package link + +import ( + "flag" + "io/ioutil" + "log" + "os/exec" + "sync" + + "src/common/golang/flags" + "src/common/golang/walk" + "src/common/golang/ziputils" + "src/tools/ak/types" +) + +var ( + // Cmd defines the command to run link. + Cmd = types.Command{ + Init: Init, + Run: Run, + Desc: desc, + Flags: []string{ + "aapt2", + "sdk_jar", + "manifest", + "res_dirs", + "asset_dirs", + "pkg", + "src_jar", + "out", + }, + } + + aapt2 string + sdkJar string + manifest string + resDirs flags.StringList + assetDirs flags.StringList + pkg string + srcJar string + out string + + initOnce sync.Once +) + +// Init initializes link. +func Init() { + initOnce.Do(func() { + flag.StringVar(&aapt2, "aapt2", "", "Path to the aapt2 binary.") + flag.StringVar(&sdkJar, "sdk_jar", "", "Path to the android jar.") + flag.StringVar(&manifest, "manifest", "", "Path to the application AndroidManifest.xml.") + flag.Var(&resDirs, "res_dirs", "List of resource archives to link.") + flag.Var(&assetDirs, "asset_dirs", "Paths to asset directories..") + flag.StringVar(&pkg, "pkg", "", "Package for R.java.") + flag.StringVar(&srcJar, "src_jar", "", "R java source jar path.") + flag.StringVar(&out, "out", "", "Output path for linked archive.") + }) +} + +func desc() string { + return "Link compiled Android resources." +} + +// Run is the entry point for link. +func Run() { + if aapt2 == "" || + sdkJar == "" || + manifest == "" || + resDirs == nil || + pkg == "" || + srcJar == "" || + out == "" { + log.Fatal("Flags -aapt2 -sdk_jar -manifest -res_dirs -pkg -src_jar and -out must be specified.") + } + + // Note that relative order between directories needs to be respected by traversal function. + // I.e. all files in dir n most come before all files in directory n+1. + resArchives, err := walk.Files(resDirs) + if err != nil { + log.Fatalf("error getting resource archives: %v", err) + } + + rjavaDir, err := ioutil.TempDir("", "rjava") + if err != nil { + log.Fatalf("error creating temp dir: %v", err) + } + + args := []string{ + "link", "--manifest", manifest, "--auto-add-overlay", "--no-static-lib-packages", + "--java", rjavaDir, "--custom-package", pkg, "-I", sdkJar} + + for _, r := range resArchives { + args = append(args, "-R", r) + } + + for _, a := range assetDirs { + args = append(args, "-A", a) + } + + args = append(args, "-o", out) + + if out, err := exec.Command(aapt2, args...).CombinedOutput(); err != nil { + log.Fatalf("error linking Android resources: %v\n %s", err, string(out)) + } + if err := ziputils.Zip(rjavaDir, srcJar); err != nil { + log.Fatalf("error unable to create resources src jar: %v", err) + } +} diff --git a/src/tools/ak/link/link_bin.go b/src/tools/ak/link/link_bin.go new file mode 100644 index 0000000..26efddb --- /dev/null +++ b/src/tools/ak/link/link_bin.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// The link_bin is a command line tool to wrap around aapt2 rough edges. +package main + +import ( + "flag" + + _ "src/common/golang/flagfile" + "src/tools/ak/link/link" +) + +func main() { + link.Init() + flag.Parse() + link.Run() +} diff --git a/src/tools/ak/mindex/BUILD b/src/tools/ak/mindex/BUILD new file mode 100644 index 0000000..32e8f14 --- /dev/null +++ b/src/tools/ak/mindex/BUILD @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test", "go_proto_library") +# Description: +# Package for mindex module +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +go_binary( + name = "mindex_bin", + srcs = ["mindex_bin.go"], + deps = [ + ":mindex", + "//src/common/golang:flagfile", + ], +) + +go_library( + name = "mindex", + srcs = ["mindex.go"], + deps = ["//src/tools/ak:types"], +) diff --git a/src/tools/ak/mindex/mindex.go b/src/tools/ak/mindex/mindex.go new file mode 100644 index 0000000..9bdbefa --- /dev/null +++ b/src/tools/ak/mindex/mindex.go @@ -0,0 +1,108 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package mindex writes a minimal dex into a file. +// It encodes the minimal necessary fields to pass muster with the android runtime. +package mindex + +import ( + "flag" + "io/ioutil" + "log" + "sync" + + "src/tools/ak/types" +) + +var ( + // Cmd defines the command to run mindex + Cmd = types.Command{ + Init: Init, + Run: Run, + Desc: desc, + Flags: []string{ + "out", + }, + } + + // Variables that hold flag values + out string + initOnce sync.Once + + // see https://android.googlesource.com/platform/art/+/android-5.0.1_r1/runtime/dex_file.h + + minDex = []byte{ + 0x64, 0x65, 0x78, 0x0A, // 0x00: Dex magic + 0x30, 0x33, 0x35, 0x00, // 0x04: Dex version + 0x44, 0x0E, 0xC1, 0x0F, // 0x08: Adler32 checksum + 0x8B, 0x9B, 0x61, 0xEA, // 0x0C: Sha1 digest + 0xC1, 0x7F, 0x94, 0x5A, // 0x10: Sha1 digest cont. + 0xE9, 0xC0, 0x8A, 0x70, // 0x14: Sha1 digest cont. + 0xFD, 0xED, 0x4F, 0x53, // 0x18: Sha1 digest cont. + 0x0F, 0x10, 0x51, 0x75, // 0x1C: Sha1 digest cont. + + // Header + 0x8C, 0x00, 0x00, 0x00, // 0x20: Files size + 0x70, 0x00, 0x00, 0x00, // 0x24: Header size + 0x78, 0x56, 0x34, 0x12, // 0x28: Endian tag + 0x00, 0x00, 0x00, 0x00, // 0x2C: Link size + 0x00, 0x00, 0x00, 0x00, // 0x30: Link offset + 0x70, 0x00, 0x00, 0x00, // 0x34: Map offset + 0x00, 0x00, 0x00, 0x00, // 0x38: String ids size + 0x00, 0x00, 0x00, 0x00, // 0x3C: String ids offset + 0x00, 0x00, 0x00, 0x00, // 0x40: Type ids size + 0x00, 0x00, 0x00, 0x00, // 0x44: Type ids offset + 0x00, 0x00, 0x00, 0x00, // 0x48: Proto ids size + 0x00, 0x00, 0x00, 0x00, // 0x4C: Proto ids offset + 0x00, 0x00, 0x00, 0x00, // 0x50: Field ids size + 0x00, 0x00, 0x00, 0x00, // 0x54: Field ids offset + 0x00, 0x00, 0x00, 0x00, // 0x58: Method ids size + 0x00, 0x00, 0x00, 0x00, // 0x5C: Method ids offset + 0x00, 0x00, 0x00, 0x00, // 0x60: Class defs size + 0x00, 0x00, 0x00, 0x00, // 0x64: Class defs offset + 0x1C, 0x00, 0x00, 0x00, // 0x68: Data size + 0x70, 0x00, 0x00, 0x00, // 0x6C: Data offset + + // Data + 0x02, 0x00, 0x00, 0x00, // 0x70: Map list size (header and map list) + 0x00, 0x00, 0x00, 0x00, // 0x74: kDexTypeHeaderItem + 0x01, 0x00, 0x00, 0x00, // 0x78: HeaderItem count + 0x00, 0x00, 0x00, 0x00, // 0x7C: Offset 0x00 + 0x00, 0x10, 0x00, 0x00, // 0x80: kDexTypeMapList + 0x01, 0x00, 0x00, 0x00, // 0x84: MapList count + 0x70, 0x00, 0x00, 0x00, // 0x88: Offset 0x70 + } +) + +// Init initializes mindex. +func Init() { + initOnce.Do(func() { + flag.StringVar(&out, "out", "", "Path to output.") + }) +} + +func desc() string { + return "Mindex writes the smallest possible dex to a file." +} + +// Run is the entry point for mindex. +func Run() { + if out == "" { + log.Fatal("Flags -out must be specified.") + } + + if err := ioutil.WriteFile(out, minDex, 0655); err != nil { + log.Fatalf("Error writing minimal dex %v", err) + } +} diff --git a/src/tools/ak/mindex/mindex_bin.go b/src/tools/ak/mindex/mindex_bin.go new file mode 100644 index 0000000..c41dc69 --- /dev/null +++ b/src/tools/ak/mindex/mindex_bin.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// The mindex_bin is a command line tool to mindex a zip archive. +package main + +import ( + "flag" + + _ "src/common/golang/flagfile" + "src/tools/ak/mindex/mindex" +) + +func main() { + mindex.Init() + flag.Parse() + mindex.Run() +} -- cgit v1.2.3 From 51c8e578d66c593143b32a17954b114ea8687487 Mon Sep 17 00:00:00 2001 From: nickreid Date: Thu, 15 Sep 2022 11:04:33 -0700 Subject: Convert deps-as-exports deprecation into an error, and eliminate allowlists. PiperOrigin-RevId: 474607185 Change-Id: I02c24bf901ea6f745e9e3b3693bb679cfac02e73 --- rules/acls.bzl | 11 --------- rules/acls/android_library_implicit_exports.bzl | 23 ----------------- rules/android_library/impl.bzl | 33 ++++++++----------------- 3 files changed, 10 insertions(+), 57 deletions(-) delete mode 100644 rules/acls/android_library_implicit_exports.bzl diff --git a/rules/acls.bzl b/rules/acls.bzl index a1bdb1e..4ba44f6 100644 --- a/rules/acls.bzl +++ b/rules/acls.bzl @@ -42,7 +42,6 @@ load("//rules/acls:android_device_plugin_rollout.bzl", "ANDROID_DEVICE_PLUGIN_FA load("//rules/acls:android_instrumentation_binary_starlark_resources.bzl", "ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK", "ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT") load("//rules/acls:android_binary_starlark_javac.bzl", "ANDROID_BINARY_STARLARK_JAVAC_FALLBACK", "ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT") load("//rules/acls:android_feature_splits_dogfood.bzl", "ANDROID_FEATURE_SPLITS_DOGFOOD") -load("//rules/acls:android_library_implicit_exports.bzl", "ANDROID_LIBRARY_IMPLICIT_EXPORTS", "ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS") load("//rules/acls:android_library_resources_without_srcs.bzl", "ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS", "ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS") load("//rules/acls:android_library_starlark_resource_outputs.bzl", "ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK", "ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT") load("//rules/acls:android_lint_checks_rollout.bzl", "ANDROID_LINT_CHECKS_FALLBACK", "ANDROID_LINT_CHECKS_ROLLOUT") @@ -130,12 +129,6 @@ def _in_b122039567(fqn): def _in_b123854163(fqn): return matches(fqn, B123854163_DICT) -def _in_android_library_implicit_exports(fqn): - return matches(fqn, ANDROID_LIBRARY_IMPLICIT_EXPORTS_DICT) - -def _in_android_library_implicit_exports_generator_functions(gfn): - return gfn in ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS_DICT - def _in_android_library_resources_without_srcs(fqn): return matches(fqn, ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_DICT) @@ -229,8 +222,6 @@ ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK_DICT = make_dict(ANDR ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT_DICT = make_dict(ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT) ANDROID_BINARY_STARLARK_JAVAC_FALLBACK_DICT = make_dict(ANDROID_BINARY_STARLARK_JAVAC_FALLBACK) ANDROID_FEATURE_SPLITS_DOGFOOD_DICT = make_dict(ANDROID_FEATURE_SPLITS_DOGFOOD) -ANDROID_LIBRARY_IMPLICIT_EXPORTS_DICT = make_dict(ANDROID_LIBRARY_IMPLICIT_EXPORTS) -ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS_DICT = make_dict(ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS) ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_DICT = make_dict(ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS) ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS_DICT = make_dict(ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS) ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK_DICT = make_dict(ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK) @@ -333,8 +324,6 @@ acls = struct( in_android_instrumentation_binary_starlark_resources = _in_android_instrumentation_binary_starlark_resources, in_android_binary_starlark_javac = _in_android_binary_starlark_javac, in_android_feature_splits_dogfood = _in_android_feature_splits_dogfood, - in_android_library_implicit_exports = _in_android_library_implicit_exports, - in_android_library_implicit_exports_generator_functions = _in_android_library_implicit_exports_generator_functions, in_android_library_starlark_resource_outputs_rollout = _in_android_library_starlark_resource_outputs_rollout, in_android_library_resources_without_srcs = _in_android_library_resources_without_srcs, in_android_library_resources_without_srcs_generator_functions = _in_android_library_resources_without_srcs_generator_functions, diff --git a/rules/acls/android_library_implicit_exports.bzl b/rules/acls/android_library_implicit_exports.bzl deleted file mode 100644 index 521c2ac..0000000 --- a/rules/acls/android_library_implicit_exports.bzl +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2020 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Allow list for b/144163743 - deprecated implicit exports.""" - -# These macros can create android_library targets with deps and no srcs with no way for -# their users to control this. -ANDROID_LIBRARY_IMPLICIT_EXPORTS_GENERATOR_FUNCTIONS = [ -] - -ANDROID_LIBRARY_IMPLICIT_EXPORTS = [ -] diff --git a/rules/android_library/impl.bzl b/rules/android_library/impl.bzl index 919b9f3..f508c1b 100644 --- a/rules/android_library/impl.bzl +++ b/rules/android_library/impl.bzl @@ -33,9 +33,7 @@ load("//rules:utils.bzl", "get_android_sdk", "get_android_toolchain", "log", "ut load("//rules/flags:flags.bzl", _flags = "flags") _USES_DEPRECATED_IMPLICIT_EXPORT_ERROR = ( - "The android_library rule will be deprecating the use of deps to export " + - "targets implicitly. " + - "Please use android_library.exports to explicitly specify the exported " + + "Use android_library.exports to explicitly specify the exported " + "targets of %s." ) @@ -67,34 +65,23 @@ _AARContextInfo = provider( ) def _uses_deprecated_implicit_export(ctx): - if not ctx.attr.deps: - return False - return not (ctx.files.srcs or - ctx.files.idl_srcs or - ctx.attr._defined_assets or - ctx.files.resource_files or - ctx.attr.manifest) + return (ctx.attr.deps and not (ctx.files.srcs or + ctx.files.idl_srcs or + ctx.attr._defined_assets or + ctx.files.resource_files or + ctx.attr.manifest)) def _uses_resources_and_deps_without_srcs(ctx): - if not ctx.attr.deps: - return False - if not (ctx.attr._defined_assets or - ctx.files.resource_files or - ctx.attr.manifest): - return False - return not (ctx.files.srcs or ctx.files.idl_srcs) + return (ctx.attr.deps and + (ctx.attr._defined_assets or ctx.files.resource_files or ctx.attr.manifest) and + not (ctx.files.srcs or ctx.files.idl_srcs)) def _check_deps_without_java_srcs(ctx): if not ctx.attr.deps or ctx.files.srcs or ctx.files.idl_srcs: return False gfn = getattr(ctx.attr, "generator_function", "") if _uses_deprecated_implicit_export(ctx): - if (acls.in_android_library_implicit_exports_generator_functions(gfn) or - acls.in_android_library_implicit_exports(str(ctx.label))): - return True - else: - # TODO(b/144163743): add a test for this. - log.error(_USES_DEPRECATED_IMPLICIT_EXPORT_ERROR % ctx.label) + log.error(_USES_DEPRECATED_IMPLICIT_EXPORT_ERROR % ctx.label) if _uses_resources_and_deps_without_srcs(ctx): if (acls.in_android_library_resources_without_srcs_generator_functions(gfn) or acls.in_android_library_resources_without_srcs(str(ctx.label))): -- cgit v1.2.3 From 6f398f8d50f8e54c8652ee1744561d0bfec22959 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Tue, 20 Sep 2022 06:51:21 -0700 Subject: internal change PiperOrigin-RevId: 475543359 Change-Id: I47db2a2a194bdd2440887f02d68420ed2edc89fc --- src/tools/ak/compile/BUILD | 35 +++++ src/tools/ak/compile/compile.go | 135 +++++++++++++++++ src/tools/ak/compile/compile_bin.go | 29 ++++ src/tools/ak/compile/compile_test.go | 107 +++++++++++++ src/tools/ak/dex/BUILD | 25 ++++ src/tools/ak/dex/dex.go | 281 +++++++++++++++++++++++++++++++++++ src/tools/ak/dex/dex_bin.go | 29 ++++ 7 files changed, 641 insertions(+) create mode 100644 src/tools/ak/compile/BUILD create mode 100644 src/tools/ak/compile/compile.go create mode 100644 src/tools/ak/compile/compile_bin.go create mode 100644 src/tools/ak/compile/compile_test.go create mode 100644 src/tools/ak/dex/BUILD create mode 100644 src/tools/ak/dex/dex.go create mode 100644 src/tools/ak/dex/dex_bin.go diff --git a/src/tools/ak/compile/BUILD b/src/tools/ak/compile/BUILD new file mode 100644 index 0000000..18dddfd --- /dev/null +++ b/src/tools/ak/compile/BUILD @@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test", "go_proto_library") +# Description: +# Package for compile module +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +go_binary( + name = "compile_bin", + srcs = ["compile_bin.go"], + deps = [ + ":compile", + "//src/common/golang:flagfile", + ], +) + +go_library( + name = "compile", + srcs = [ + "compile.go", + ], + deps = [ + "//src/common/golang:ziputils", + "//src/tools/ak:types", + ], +) + +go_test( + name = "compile_test", + size = "small", + srcs = [ + "compile_test.go", + ], + library = ":compile", +) diff --git a/src/tools/ak/compile/compile.go b/src/tools/ak/compile/compile.go new file mode 100644 index 0000000..39d55be --- /dev/null +++ b/src/tools/ak/compile/compile.go @@ -0,0 +1,135 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package compile is a thin wrapper around aapt2 to compile android resources. +package compile + +import ( + "flag" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + "sync" + + "src/common/golang/ziputils" + "src/tools/ak/types" +) + +var ( + // Cmd defines the command to run compile + Cmd = types.Command{ + Init: Init, + Run: Run, + Desc: desc, + Flags: []string{ + "aapt2", + "in", + "out", + }, + } + + in string + aapt2 string + out string + + initOnce sync.Once + + dirPerm os.FileMode = 0755 + dirReplacer = strings.NewReplacer("sr-rLatn", "b+sr+Latn", "es-419", "b+es+419") + archiveSuffix = ".zip" +) + +// Init initializes compile. +func Init() { + initOnce.Do(func() { + flag.StringVar(&aapt2, "aapt2", "", "Path to the aapt2 binary.") + flag.StringVar(&in, "in", "", "Input res bucket/dir to compile.") + flag.StringVar(&out, "out", "", "The compiled resource archive.") + }) +} + +func desc() string { + return "Compile android resources directory." +} + +// Run is the entry point for compile. +func Run() { + if in == "" || aapt2 == "" || out == "" { + log.Fatal("Flags -in and -aapt2 and -out must be specified.") + } + + fi, err := os.Stat(in) + if err != nil { + log.Fatal(err) + } + + resDir := in + if !fi.IsDir() { + if strings.HasSuffix(resDir, archiveSuffix) { + // We are dealing with a resource archive. + td, err := ioutil.TempDir("", "-res") + if err != nil { + log.Fatal(err) + } + + resDir = filepath.Join(td, "res/") + if err := os.MkdirAll(resDir, dirPerm); err != nil { + log.Fatal(err) + } + if err := ziputils.Unzip(in, td); err != nil { + log.Fatal(err) + } + } else { + // We are compiling a single file, but we need to provide dir. + resDir = filepath.Dir(filepath.Dir(resDir)) + } + } + + if err := sanitizeDirs(resDir, dirReplacer); err != nil { + log.Fatal(err) + } + + cmd := exec.Command(aapt2, []string{"compile", "--legacy", "-o", out, "--dir", resDir}...) + if out, err := cmd.CombinedOutput(); err != nil { + log.Fatalf("error compiling resources for resource directory %s: %v\n%s", resDir, err, string(out)) + } +} + +// sanitizeDirs renames the directories that aapt is unable to parse +func sanitizeDirs(dir string, r *strings.Replacer) error { + src, err := os.Open(dir) + if err != nil { + return err + } + defer src.Close() + + fs, err := src.Readdir(-1) + if err != nil { + return err + } + + for _, f := range fs { + if f.Mode().IsDir() { + if qd := r.Replace(f.Name()); qd != f.Name() { + if err := os.Rename(filepath.Join(dir, f.Name()), filepath.Join(dir, qd)); err != nil { + return err + } + } + } + } + return nil +} diff --git a/src/tools/ak/compile/compile_bin.go b/src/tools/ak/compile/compile_bin.go new file mode 100644 index 0000000..c9efd76 --- /dev/null +++ b/src/tools/ak/compile/compile_bin.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// The compile_bin is a command line tool to wrap around aapt2 rough edges. +package main + +import ( + "flag" + + _ "src/common/golang/flagfile" + "src/tools/ak/compile/compile" +) + +func main() { + compile.Init() + flag.Parse() + compile.Run() +} diff --git a/src/tools/ak/compile/compile_test.go b/src/tools/ak/compile/compile_test.go new file mode 100644 index 0000000..e639cb7 --- /dev/null +++ b/src/tools/ak/compile/compile_test.go @@ -0,0 +1,107 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package compile + +import ( + "io/ioutil" + "os" + "path/filepath" + "reflect" + "sort" + "strings" + "testing" +) + +func TestDirReplacer(t *testing.T) { + + qualified := []string{ + "res/values-en-rGB/strings.xml", + "res/values-es-rMX/strings.xml", + "res/values-sr-rLatn/strings.xml", + "res/values-sr-rLatn-xhdpi/strings.xml", + "res/values-es-419/strings.xml", + "res/values-es-419-xhdpi/strings.xml"} + + expected := []string{ + "res/values-en-rGB/strings.xml", + "res/values-es-rMX/strings.xml", + "res/values-b+sr+Latn/strings.xml", + "res/values-b+sr+Latn-xhdpi/strings.xml", + "res/values-b+es+419/strings.xml", + "res/values-b+es+419-xhdpi/strings.xml"} + + var actual []string + for _, d := range qualified { + actual = append(actual, dirReplacer.Replace(d)) + } + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("dirReplacer.Replace(%v) = %v want %v", qualified, actual, expected) + } +} + +func TestSanitizeDirs(t *testing.T) { + base, err := ioutil.TempDir("", "res-") + dirs := []string{ + "values", + "values-bas-foo", + "values-foo-rNOTGOOD", + "values-foo-rNOBUENO-baz", + } + for _, dir := range dirs { + if err := os.Mkdir(filepath.Join(base, dir), 0777); err != nil { + t.Fatal(err) + } + } + + var expected sort.StringSlice + expected = append(expected, []string{ + "values", + "values-bas-foo", + "values-foo-rVERY-GOOD", + "values-foo-rMUCHO-BUENO-baz"}...) + + r := strings.NewReplacer("NOTGOOD", "VERY-GOOD", "NOBUENO", "MUCHO-BUENO") + if err := sanitizeDirs(base, r); err != nil { + t.Fatalf("sanitizeDirs(%s, %v) failed %v", base, r, err) + } + + src, err := os.Open(base) + if err != nil { + t.Fatal(err) + } + defer src.Close() + + fs, err := src.Readdir(-1) + if err != nil { + t.Fatal(err) + } + + actual := make(map[string]bool) + for _, f := range fs { + actual[f.Name()] = true + } + + for _, dir := range dirs { + expected := r.Replace(dir) + if expected != dir && actual[dir] { + t.Errorf("sanitizeDirs(%s) = %v got invalid dir %s. Expected %s ", base, actual, dir, expected) + } + if _, ok := actual[expected]; !ok { + t.Errorf("sanitizeDirs(%s) = %v missing dir %s", base, actual, expected) + } + } + +} diff --git a/src/tools/ak/dex/BUILD b/src/tools/ak/dex/BUILD new file mode 100644 index 0000000..2fbfbb4 --- /dev/null +++ b/src/tools/ak/dex/BUILD @@ -0,0 +1,25 @@ +load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test", "go_proto_library") +# Package for dex compilation module +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +go_binary( + name = "dex_bin", + srcs = ["dex_bin.go"], + deps = [ + ":dex", + "//src/common/golang:flagfile", + ], +) + +go_library( + name = "dex", + srcs = ["dex.go"], + deps = [ + "//src/common/golang:flags", + "//src/common/golang:shard", + "//src/common/golang:ziputils", + "//src/tools/ak:types", + ], +) diff --git a/src/tools/ak/dex/dex.go b/src/tools/ak/dex/dex.go new file mode 100644 index 0000000..c024e44 --- /dev/null +++ b/src/tools/ak/dex/dex.go @@ -0,0 +1,281 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package dex provides a thin wrapper around d8 to handle corner cases +package dex + +import ( + "archive/zip" + "bufio" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + "sync" + + "src/common/golang/flags" + "src/common/golang/shard" + "src/common/golang/ziputils" + "src/tools/ak/types" +) + +var ( + // Cmd defines the command to run + Cmd = types.Command{ + Init: Init, + Run: Run, + Desc: desc, + Flags: []string{ + "desugar", + "android_jar", + "desugar_core_libs", + "classpath", + "d8", + "intermediate", + "in", + "out", + }, + } + + tmp struct { + Dir string + } + + // Flag variables + desugar, androidJar, d8, in string + classpaths, outs, outputDir flags.StringList + desugarCoreLibs, intermediate bool + + initOnce sync.Once +) + +// Init initializes manifest flags +func Init() { + initOnce.Do(func() { + flag.StringVar(&desugar, "desugar", "", "Path to desugar tool") + flag.StringVar(&androidJar, "android_jar", "", "Required for desugar, path to android.jar") + flag.Var(&classpaths, "classpath", "(Optional) Path to library resource(s) for desugar") + flag.BoolVar(&desugarCoreLibs, "desugar_core_libs", false, "Desugar Java 8 core libs, default false") + flag.StringVar(&d8, "d8", "", "Path to d8 dexer") + flag.BoolVar(&intermediate, "intermediate", false, "Compile for later merging, default false") + flag.StringVar(&in, "in", "", "Path to input") + flag.Var(&outs, "out", "Path to output, if more than one specified, output is sharded across files.") + }) +} + +func desc() string { + return "Dex converts Java byte code to Dex code." +} + +// Run is the main entry point +func Run() { + if desugar != "" && androidJar == "" { + log.Fatal("--android_jar is required for desugaring") + } + if d8 == "" || in == "" || outs == nil { + log.Fatal("Missing required flags. Must specify --d8 --in --out") + } + sc := len(outs) + if sc > 256 { + log.Fatalf("%d: is an unreasonable shard count (want [1 to 256])", sc) + } + + var err error + tmp.Dir, err = ioutil.TempDir("", "dex") + if err != nil { + log.Fatalf("Error creating temp dir: %v", err) + } + defer os.RemoveAll(tmp.Dir) + + notEmpty, err := hasCode(in) + if err != nil { + log.Fatal(err) + } + + if notEmpty { + jar := in + if desugar != "" { + jar = filepath.Join(tmp.Dir, "desugared.jar") + if err = desugarJar(in, jar); err != nil { + log.Fatalf("Error desugaring %v: %v", in, err) + } + } + if sc == 1 { + if err = dex(jar, outs[0]); err != nil { + log.Fatalf("Dex error: %v", err) + } + } else { + out := filepath.Join(tmp.Dir, "dexed.zip") + if err = dex(jar, out); err != nil { + log.Fatalf("Dex error: %v", err) + } + if err = zipShard(out, outs); err != nil { + log.Fatalf("ZipShard error: %v", err) + } + } + } else { + for _, out := range outs { + if err := ziputils.EmptyZip(out); err != nil { + log.Fatalf("Error creating empty zip archive: %v", err) + } + } + } +} + +func createFlagFile(args []string) (string, error) { + f, err := ioutil.TempFile(tmp.Dir, "flags") + if err != nil { + return "", err + } + for _, arg := range args { + if _, err := f.WriteString(arg + "\n"); err != nil { + return "", err + } + } + if err := f.Close(); err != nil { + return "", err + } + return f.Name(), nil +} + +func hasCode(f string) (bool, error) { + reader, err := zip.OpenReader(f) + if err != nil { + return false, fmt.Errorf("Opening zip %q failed: %v", f, err) + } + defer reader.Close() + + for _, file := range reader.File { + ext := filepath.Ext(file.Name) + if ext == ".class" || ext == ".dex" { + return true, nil + } + } + return false, nil +} + +func desugarJar(in, out string) error { + args := []string{ + "--input", + in, + "--bootclasspath_entry", + androidJar, + "--output", + out, + } + if desugarCoreLibs { + args = append(args, "--desugar_supported_core_libs") + } + for _, cp := range classpaths { + args = append(args, "--classpath_entry", cp) + } + return runCmd(desugar, args) +} + +func dex(in, out string) error { + args := []string{ + "--min-api", + "21", + "--no-desugaring", + "--output", + out, + } + if intermediate { + args = append(args, "--file-per-class") + args = append(args, "--intermediate") + } + args = append(args, in) + return runCmd(d8, args) +} + +func runCmd(cmd string, args []string) error { + flagFile, err := createFlagFile(args) + if err != nil { + return fmt.Errorf("Error creating flag file: %v", err) + } + output, err := exec.Command(cmd, "@"+flagFile).CombinedOutput() + if err != nil { + return fmt.Errorf("%v:\n%s", err, output) + } + return nil +} + +func zipShard(input string, outs []string) error { + zr, err := zip.OpenReader(input) + if err != nil { + return fmt.Errorf("%s: cannot open for input: %v", input, err) + } + defer zr.Close() + + if len(outs) < 2 { + log.Fatalf("Need at least two output shards)") + } + + zws := make([]*zip.Writer, len(outs)) + for i, out := range outs { + outDir := filepath.Dir(out) + if _, err := os.Stat(outDir); os.IsNotExist(err) { + if err := os.MkdirAll(outDir, 0755); err != nil { + return fmt.Errorf("%s: could not make dir: %v", input, outDir) + } + } + outF, err := os.Create(out) + if err != nil { + return fmt.Errorf("%s: could not create output file: %s %v", out, outDir, err) + } + w := bufio.NewWriterSize(outF, 2<<16) + zw := zip.NewWriter(w) + defer func() error { + if err := zw.Close(); err != nil { + return fmt.Errorf("%s: closing zip failed: %v", out, err) + } + if err := w.Flush(); err != nil { + return fmt.Errorf("%s: flushing output file failed: %v", out, err) + } + if err := outF.Close(); err != nil { + return fmt.Errorf("%s: closing output file failed: %v", out, err) + } + return nil + }() + zws[i] = zw + } + + err = shard.ZipShard(&zr.Reader, zws, shardFn) + if err != nil { + return fmt.Errorf("%s: sharder failed: %v", input, err) + } + return nil +} + +func shardFn(name string, shardCount int) int { + // Sharding function which ensures that a class and all its inner classes are + // placed in the same shard. An important side effect of this is that all D8 + // synthetics are in the same shard as their context, as a synthetic is named + // $$ExternalSyntheticXXXN. + index := len(name) + if strings.HasSuffix(name, ".dex") { + index -= 4 + } else { + log.Fatalf("Name expected to end with '.dex', was: %s", name) + } + trimIndex := strings.IndexAny(name, "$-") + if trimIndex > -1 { + index = trimIndex + } + return shard.FNV(name[:index], shardCount) +} diff --git a/src/tools/ak/dex/dex_bin.go b/src/tools/ak/dex/dex_bin.go new file mode 100644 index 0000000..3114d70 --- /dev/null +++ b/src/tools/ak/dex/dex_bin.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// dex_bin is a command line tool that wraps d8 to handle corner cases. +package main + +import ( + "flag" + + _ "src/common/golang/flagfile" + "src/tools/ak/dex/dex" +) + +func main() { + dex.Init() + flag.Parse() + dex.Run() +} -- cgit v1.2.3 From 317b9d914eb4716150a1124541c3a2effb22b64d Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Tue, 20 Sep 2022 10:28:15 -0400 Subject: Release AK's manifest library PiperOrigin-RevId: 475549570 Change-Id: I4240a8addb1e2a960df365de5f62a524d7d3e031 --- src/tools/ak/manifest/BUILD | 26 ++++++ src/tools/ak/manifest/manifest.go | 158 ++++++++++++++++++++++++++++++++++ src/tools/ak/manifest/manifest_bin.go | 29 +++++++ 3 files changed, 213 insertions(+) create mode 100644 src/tools/ak/manifest/BUILD create mode 100644 src/tools/ak/manifest/manifest.go create mode 100644 src/tools/ak/manifest/manifest_bin.go diff --git a/src/tools/ak/manifest/BUILD b/src/tools/ak/manifest/BUILD new file mode 100644 index 0000000..8dc7e3f --- /dev/null +++ b/src/tools/ak/manifest/BUILD @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test", "go_proto_library") +# Package for manifest compilation module +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +go_binary( + name = "manifest_bin", + srcs = ["manifest_bin.go"], + deps = [ + ":manifest", + "//src/common/golang:flagfile", + ], +) + +go_library( + name = "manifest", + srcs = [ + "manifest.go", + ], + deps = [ + "//src/common/golang:flags", + "//src/tools/ak:manifestutils", + "//src/tools/ak:types", + ], +) diff --git a/src/tools/ak/manifest/manifest.go b/src/tools/ak/manifest/manifest.go new file mode 100644 index 0000000..c519545 --- /dev/null +++ b/src/tools/ak/manifest/manifest.go @@ -0,0 +1,158 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package manifest provides a thin wrapper around aapt2 to compile an AndroidManifest.xml +package manifest + +import ( + "archive/zip" + "bytes" + "flag" + "io" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "sync" + + "src/common/golang/flags" + "src/tools/ak/manifestutils" + "src/tools/ak/types" +) + +const errMsg string = ` ++----------------------------------------------------------- +| Error while compiling AndroidManifest.xml +| If your build succeeds with Blaze/Bazel build, this is most +| likely due to the stricter aapt2 used by mobile-install +` + + ` ++----------------------------------------------------------- +ERROR: %s +` + +var ( + // Cmd defines the command to run + Cmd = types.Command{ + Init: Init, + Run: Run, + Desc: desc, + Flags: []string{ + "aapt2", + "manifest", + "out", + "sdk_jar", + "res", + "attr", + }, + } + + // Flag variables + aapt2, manifest, out, sdkJar, res string + attr flags.StringList + + initOnce sync.Once +) + +// Init initializes manifest flags +func Init() { + initOnce.Do(func() { + flag.StringVar(&aapt2, "aapt2", "", "Path to aapt2") + flag.StringVar(&manifest, "manifest", "", "Path to manifest") + flag.StringVar(&out, "out", "", "Path to output") + flag.StringVar(&sdkJar, "sdk_jar", "", "Path to sdk jar") + flag.StringVar(&res, "res", "", "Path to res") + flag.Var(&attr, "attr", "(optional) attr(s) to set. {element}:{attr}:{value}.") + }) +} + +func desc() string { + return "Compile an AndroidManifest.xml" +} + +// Run is the main entry point +func Run() { + if aapt2 == "" || manifest == "" || out == "" || sdkJar == "" || res == "" { + log.Fatal("Missing required flags. Must specify --aapt2 --manifest --out --sdk_jar --res") + } + + aaptOut, err := ioutil.TempFile("", "manifest_apk") + if err != nil { + log.Fatalf("Creating temp file failed: %v", err) + } + defer os.Remove(aaptOut.Name()) + + manifestPath := manifest + if len(attr) > 0 { + patchedManifest, err := ioutil.TempFile("", "AndroidManifest_patched.xml") + if err != nil { + log.Fatalf("Creating temp file failed: %v", err) + } + defer os.Remove(patchedManifest.Name()) + manifestPath = patchManifest(manifest, patchedManifest, attr) + } + + stdoutStderr, err := exec.Command(aapt2, "link", "-o", aaptOut.Name(), "--manifest", manifestPath, "-I", sdkJar, "-I", res).CombinedOutput() + if err != nil { + log.Fatalf(errMsg, stdoutStderr) + } + + reader, err := zip.OpenReader(aaptOut.Name()) + if err != nil { + log.Fatalf("Opening zip %q failed: %v", aaptOut.Name(), err) + } + defer reader.Close() + + for _, file := range reader.File { + if file.Name == "AndroidManifest.xml" { + err = os.MkdirAll(filepath.Dir(out), os.ModePerm) + if err != nil { + log.Fatalf("Creating output directory for %q failed: %v", out, err) + } + + fileReader, err := file.Open() + if err != nil { + log.Fatalf("Opening file %q inside zip %q failed: %v", file.Name, aaptOut.Name(), err) + } + defer fileReader.Close() + + outFile, err := os.Create(out) + if err != nil { + log.Fatalf("Creating output %q failed: %v", out, err) + } + + if _, err := io.Copy(outFile, fileReader); err != nil { + log.Fatalf("Writing to output %q failed: %v", out, err) + } + + if err = outFile.Close(); err != nil { + log.Fatal(err) + } + break + } + } +} + +func patchManifest(manifest string, patchedManifest *os.File, attrs []string) string { + b, err := ioutil.ReadFile(manifest) + if err != nil { + log.Fatalf("Failed to read manifest: %v", err) + } + err = manifestutils.WriteManifest(patchedManifest, bytes.NewReader(b), manifestutils.CreatePatchElements(attrs)) + if err != nil { + log.Fatalf("Failed to update manifest: %v", err) + } + return patchedManifest.Name() +} diff --git a/src/tools/ak/manifest/manifest_bin.go b/src/tools/ak/manifest/manifest_bin.go new file mode 100644 index 0000000..6248bc8 --- /dev/null +++ b/src/tools/ak/manifest/manifest_bin.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// manifest_bin is a command line tool to manifest an AndroidManifest.xml +package main + +import ( + "flag" + + _ "src/common/golang/flagfile" + "src/tools/ak/manifest/manifest" +) + +func main() { + manifest.Init() + flag.Parse() + manifest.Run() +} -- cgit v1.2.3 From 9904a44791a6ad8239b07a570a103c9ef4d0b364 Mon Sep 17 00:00:00 2001 From: Tim Peut Date: Tue, 20 Sep 2022 14:05:34 -0400 Subject: Use new standalone tool for feature module manifest merging. PiperOrigin-RevId: 475601539 Change-Id: Ia8e6458d0a392501c2b4159fd643dfc574457b6e --- .../android_application/android_application_rule.bzl | 19 ++++++++++--------- rules/android_application/attrs.bzl | 6 ++++++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/rules/android_application/android_application_rule.bzl b/rules/android_application/android_application_rule.bzl index 05b3b22..d539620 100644 --- a/rules/android_application/android_application_rule.bzl +++ b/rules/android_application/android_application_rule.bzl @@ -204,15 +204,16 @@ def _create_feature_manifest( progress_message = "Generating Priority AndroidManifest.xml for " + feature_target.label.name, ) - _busybox.merge_manifests( - ctx, - out_file = manifest, - manifest = priority_manifest, - mergee_manifests = depset([info.manifest]), - java_package = java_package, - busybox = android_resources_busybox.files_to_run, - host_javabase = host_javabase, - manifest_values = {"MODULE_TITLE": "@string/" + info.title_id}, + args = ctx.actions.args() + args.add("--main_manifest", priority_manifest.path) + args.add("--feature_manifest", info.manifest.path) + args.add("--feature_title", "@string/" + info.title_id) + args.add("--out", manifest.path) + ctx.actions.run( + executable = ctx.attr._merge_manifests.files_to_run, + inputs = [priority_manifest, info.manifest], + outputs = [manifest], + arguments = [args], ) return manifest diff --git a/rules/android_application/attrs.bzl b/rules/android_application/attrs.bzl index 77f24e3..8084388 100644 --- a/rules/android_application/attrs.bzl +++ b/rules/android_application/attrs.bzl @@ -59,6 +59,12 @@ ANDROID_APPLICATION_ATTRS = _attrs.add( _java_toolchain = attr.label( default = Label("//tools/jdk:toolchain_android_only"), ), + _merge_manifests = attr.label( + default = ":merge_feature_manifests.par", + allow_single_file = True, + cfg = "exec", + executable = True, + ), _priority_feature_manifest_script = attr.label( allow_single_file = True, cfg = "host", -- cgit v1.2.3 From c05db3f36c906ee16cc76be6ae872df6f5d1ca9c Mon Sep 17 00:00:00 2001 From: A Googler Date: Tue, 20 Sep 2022 12:09:12 -0700 Subject: Internal change PiperOrigin-RevId: 475618650 Change-Id: I182729e48ce2fc9f69b5acc7e97b0d904c4ef932 --- rules/resources.bzl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rules/resources.bzl b/rules/resources.bzl index f4e9d90..014e52f 100644 --- a/rules/resources.bzl +++ b/rules/resources.bzl @@ -394,9 +394,7 @@ def _is_resource_shrinking_enabled( def _should_shrink_resource_cycles( use_android_resource_cycle_shrinking, resource_shrinking_enabled): - if use_android_resource_cycle_shrinking and not resource_shrinking_enabled: - fail("resource cycle shrinking can only be enabled when resource shrinking is enabled") - return use_android_resource_cycle_shrinking + return use_android_resource_cycle_shrinking and resource_shrinking_enabled def _filter_multi_cpu_configuration_targets( targets): -- cgit v1.2.3 From 031f284a0f335f979f2c8a8fe4336591901de068 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Thu, 22 Sep 2022 16:51:55 -0700 Subject: OSS release of AK's tool nativelib PiperOrigin-RevId: 476229796 Change-Id: I25b0164f6780c92f60624ede61d188590edce058 --- src/tools/ak/nativelib/BUILD | 36 +++++++ src/tools/ak/nativelib/nativelib.go | 162 +++++++++++++++++++++++++++++++ src/tools/ak/nativelib/nativelib_bin.go | 29 ++++++ src/tools/ak/nativelib/nativelib_test.go | 118 ++++++++++++++++++++++ src/tools/ak/nativelib/testdata/BUILD | 12 +++ 5 files changed, 357 insertions(+) create mode 100644 src/tools/ak/nativelib/BUILD create mode 100644 src/tools/ak/nativelib/nativelib.go create mode 100644 src/tools/ak/nativelib/nativelib_bin.go create mode 100644 src/tools/ak/nativelib/nativelib_test.go create mode 100644 src/tools/ak/nativelib/testdata/BUILD diff --git a/src/tools/ak/nativelib/BUILD b/src/tools/ak/nativelib/BUILD new file mode 100644 index 0000000..c5d3cb2 --- /dev/null +++ b/src/tools/ak/nativelib/BUILD @@ -0,0 +1,36 @@ +load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test", "go_proto_library") +# Description: +# Package for nativelib module +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +go_library( + name = "nativelib", + srcs = ["nativelib.go"], + deps = [ + "//src/common/golang:fileutils", + "//src/common/golang:flags", + "//src/common/golang:ziputils", + "//src/tools/ak:types", + ], +) + +go_binary( + name = "nativelib_bin", + srcs = ["nativelib_bin.go"], + deps = [ + ":nativelib", + "//src/common/golang:flagfile", + ], +) + +go_test( + name = "nativelib_test", + size = "small", + srcs = ["nativelib_test.go"], + data = [ + "//src/tools/ak/nativelib/testdata:dummy_so", + ], + library = ":nativelib", +) diff --git a/src/tools/ak/nativelib/nativelib.go b/src/tools/ak/nativelib/nativelib.go new file mode 100644 index 0000000..d8ca988 --- /dev/null +++ b/src/tools/ak/nativelib/nativelib.go @@ -0,0 +1,162 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package nativelib creates the native library zip. +package nativelib + +import ( + "archive/zip" + "bufio" + "errors" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "sort" + "strings" + "sync" + + "src/common/golang/fileutils" + "src/common/golang/flags" + "src/common/golang/ziputils" + "src/tools/ak/types" +) + +var ( + // Cmd defines the command to run nativelib. + Cmd = types.Command{ + Init: Init, + Run: Run, + Desc: desc, + Flags: []string{"lib", "native_libs_zip", "out"}, + } + + // Variables to hold flag values + nativeLibs flags.StringList + nativeLibsZip flags.StringList + out string + + initOnce sync.Once +) + +// Init initializes nativelib. +func Init() { + initOnce.Do(func() { + flag.Var(&nativeLibs, "lib", "Path to native lib.") + flag.Var(&nativeLibsZip, "native_libs_zip", "Zip(s) containing native libs.") + flag.StringVar(&out, "out", "", "Native libraries files.") + }) +} + +func desc() string { + return "Nativelib creates the native lib zip." +} + +// Run is the entry point for nativelib. +func Run() { + if nativeLibsZip != nil { + dstDir, err := ioutil.TempDir("", "ziplibs") + if err != nil { + log.Fatalf("Error creating native lib zip: %v", err) + } + + for _, native := range nativeLibsZip { + libs, err := extractLibs(native, dstDir) + if err != nil { + log.Fatalf("Error creating native lib zip: %v", err) + } + nativeLibs = append(nativeLibs, libs...) + } + } + + if err := doWork(nativeLibs, out); err != nil { + log.Fatalf("Error creating native lib zip: %v", err) + } +} + +func extractLibs(libZip, dstDir string) ([]string, error) { + zr, err := zip.OpenReader(libZip) + if err != nil { + return nil, err + } + defer zr.Close() + + libs := []string{} + for _, f := range zr.File { + if f.Mode().IsDir() { + continue + } + arch := filepath.Base(filepath.Dir(f.Name)) + libs = append(libs, fmt.Sprintf("%s:%s", arch, filepath.Join(dstDir, f.Name))) + } + if err := ziputils.Unzip(libZip, dstDir); err != nil { + return nil, err + } + return libs, nil +} + +func doWork(nativeLibs []string, out string) error { + nativeDir, err := ioutil.TempDir("", "nativelib") + if err != nil { + return err + } + defer os.RemoveAll(nativeDir) + nativePaths, err := copyNativeLibs(nativeLibs, nativeDir) + if err != nil { + return err + } + zipFile, err := os.Create(out) + if err != nil { + return err + } + writer := bufio.NewWriter(zipFile) + zipWriter := zip.NewWriter(writer) + sort.Strings(nativePaths) + for _, f := range nativePaths { + p, err := filepath.Rel(nativeDir, f) + if err != nil { + return err + } + ziputils.WriteFile(zipWriter, f, p) + } + zipWriter.Close() + return nil +} + +func copyNativeLibs(nativeLibs []string, dir string) ([]string, error) { + var paths []string + for _, cpuNativeLib := range nativeLibs { + r := strings.SplitN(cpuNativeLib, ":", 2) + if len(r) != 2 { + return nil, errors.New("error parsing native lib") + } + arch := r[0] + nativeLib := r[1] + if arch == "armv7a" { + arch = "armeabi-v7a" + } + libOutDir := filepath.Join(dir, "lib", arch) + if err := os.MkdirAll(libOutDir, 0777); err != nil && !os.IsExist(err) { + return nil, err + } + outNativeLibPath := filepath.Join(libOutDir, filepath.Base(nativeLib)) + if err := fileutils.Copy(nativeLib, outNativeLibPath); err != nil { + return nil, err + } + paths = append(paths, outNativeLibPath) + } + return paths, nil +} diff --git a/src/tools/ak/nativelib/nativelib_bin.go b/src/tools/ak/nativelib/nativelib_bin.go new file mode 100644 index 0000000..58bb6b9 --- /dev/null +++ b/src/tools/ak/nativelib/nativelib_bin.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Nativelib_bin is a command line tool to extract native libraries for the shell apk. +package main + +import ( + "flag" + + _ "src/common/golang/flagfile" + "src/tools/ak/nativelib/nativelib" +) + +func main() { + nativelib.Init() + flag.Parse() + nativelib.Run() +} diff --git a/src/tools/ak/nativelib/nativelib_test.go b/src/tools/ak/nativelib/nativelib_test.go new file mode 100644 index 0000000..238e62d --- /dev/null +++ b/src/tools/ak/nativelib/nativelib_test.go @@ -0,0 +1,118 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package nativelib + +import ( + "archive/zip" + "fmt" + "io" + "io/ioutil" + "os" + "path" + "path/filepath" + "testing" +) + +const ( + expectedName = "lib/x86/dummy.so" + dummyLib = "src/tools/ak/nativelib/testdata/dummy.so" +) + +func makeLibZip(t *testing.T, entry io.Reader, entryName, zipPath string) error { + f, err := os.Create(zipPath) + if err != nil { + return err + } + defer func() { + if err := f.Close(); err != nil { + t.Error(err) + } + }() + + archive := zip.NewWriter(f) + wr, err := archive.CreateHeader(&zip.FileHeader{Name: entryName, Method: zip.Store}) + if err != nil { + return err + } + if _, err := io.Copy(wr, entry); err != nil { + return err + } + return archive.Close() +} + +func TestCreateNativeLibZip(t *testing.T) { + tmpDir, err := ioutil.TempDir("", "shelltest") + if err != nil { + t.Fatalf("Error creating temp directory: %v", err) + } + defer os.RemoveAll(tmpDir) + out := filepath.Join(tmpDir, "lib.zip") + in := []string{"x86:" + path.Join(os.Getenv("TEST_SRCDIR"), dummyLib)} + if err := doWork(in, out); err != nil { + t.Fatalf("Error creating native lib zip: %v", err) + } + + z, err := zip.OpenReader(out) + if err != nil { + t.Fatalf("Error opening output zip: %v", err) + } + defer z.Close() + + if len(z.File) != 1 { + t.Fatalf("Got %d files in zip, expected 1", len(z.File)) + } + + if z.File[0].Name != expectedName { + t.Fatalf("Got .so file %s, expected %s", z.File[0].Name, expectedName) + } +} + +func TestExtractLibs(t *testing.T) { + tmpDir, err := ioutil.TempDir("", "shelltest") + if err != nil { + t.Fatalf("Error creating temp directory: %v", err) + } + defer os.RemoveAll(tmpDir) + + lib, err := os.Create(filepath.Join(tmpDir, "dmmylib.so")) + if err != nil { + t.Fatalf("Error creating dummy lib: %v", err) + } + + libZip := filepath.Join(tmpDir, "libs.zip") + if err := makeLibZip(t, lib, expectedName, libZip); err != nil { + t.Fatalf("error creating aar lib zip: %v", err) + } + + dstDir, err := ioutil.TempDir("", "ziplibs") + if err != nil { + t.Fatalf("Error extracting creating zip dir: %v", err) + } + defer os.RemoveAll(dstDir) + + libs, err := extractLibs(libZip, dstDir) + if err != nil { + t.Fatalf("Error extracting libs from zip: %v", err) + } + + if len(libs) != 1 { + t.Fatalf("Got %d files in zip, expected 1", len(libs)) + } + expected := fmt.Sprintf("x86:%s", filepath.Join(dstDir, "lib/x86/dummy.so")) + if libs[0] != expected { + t.Fatalf("Got %s lib, expected %s", libs[0], expected) + } + +} diff --git a/src/tools/ak/nativelib/testdata/BUILD b/src/tools/ak/nativelib/testdata/BUILD new file mode 100644 index 0000000..fda740f --- /dev/null +++ b/src/tools/ak/nativelib/testdata/BUILD @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test", "go_proto_library") +# Creates test data for testing the nativelib action. + +package(default_visibility = ["//src/tools/ak/nativelib:__subpackages__"]) + +licenses(["notice"]) + +genrule( + name = "dummy_so", + outs = ["dummy.so"], + cmd = "touch $@", +) -- cgit v1.2.3 From 8859ea50626ed065871e914bbd76ab419a1b195d Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Mon, 26 Sep 2022 13:51:39 -0700 Subject: Release rules_android/src/tools/ak/liteparse PiperOrigin-RevId: 476982608 Change-Id: Ibb209eb95a6dc987506804ff10d9414d7962611e --- BUILD | 4 + WORKSPACE | 33 +- src/tools/ak/compile/BUILD | 2 +- src/tools/ak/dex/BUILD | 2 +- src/tools/ak/link/BUILD | 2 +- src/tools/ak/liteparse/BUILD | 54 +++ src/tools/ak/liteparse/liteparse.go | 436 +++++++++++++++++++++ src/tools/ak/liteparse/liteparse_bin.go | 30 ++ src/tools/ak/liteparse/liteparse_test.go | 375 ++++++++++++++++++ src/tools/ak/liteparse/non_values_parse.go | 61 +++ src/tools/ak/liteparse/non_values_parse_test.go | 88 +++++ .../testdata/mini-1/res/values/strings.xml | 3 + .../ak/liteparse/testdata/mini-2/res/xml/foo.xml | 3 + .../liteparse/testdata/res/drawable-ldpi/foo.9.png | 2 + .../ak/liteparse/testdata/res/menu/simple.xml | 6 + .../ak/liteparse/testdata/res/raw/garbage.xml | 6 + .../liteparse/testdata/res/values-v19/strings.xml | 5 + .../ak/liteparse/testdata/res/values/other.xml | 3 + .../ak/liteparse/testdata/res/values/vals.xml | 15 + src/tools/ak/liteparse/values_parse.go | 226 +++++++++++ src/tools/ak/liteparse/values_parse_test.go | 171 ++++++++ src/tools/ak/manifest/BUILD | 2 +- src/tools/ak/mindex/BUILD | 2 +- src/tools/ak/nativelib/BUILD | 2 +- src/tools/ak/nativelib/testdata/BUILD | 2 +- toolchains/emulator/toolchain.bzl | 59 +++ 26 files changed, 1585 insertions(+), 9 deletions(-) create mode 100644 src/tools/ak/liteparse/BUILD create mode 100644 src/tools/ak/liteparse/liteparse.go create mode 100644 src/tools/ak/liteparse/liteparse_bin.go create mode 100644 src/tools/ak/liteparse/liteparse_test.go create mode 100644 src/tools/ak/liteparse/non_values_parse.go create mode 100644 src/tools/ak/liteparse/non_values_parse_test.go create mode 100644 src/tools/ak/liteparse/testdata/mini-1/res/values/strings.xml create mode 100644 src/tools/ak/liteparse/testdata/mini-2/res/xml/foo.xml create mode 100644 src/tools/ak/liteparse/testdata/res/drawable-ldpi/foo.9.png create mode 100644 src/tools/ak/liteparse/testdata/res/menu/simple.xml create mode 100644 src/tools/ak/liteparse/testdata/res/raw/garbage.xml create mode 100644 src/tools/ak/liteparse/testdata/res/values-v19/strings.xml create mode 100644 src/tools/ak/liteparse/testdata/res/values/other.xml create mode 100644 src/tools/ak/liteparse/testdata/res/values/vals.xml create mode 100644 src/tools/ak/liteparse/values_parse.go create mode 100644 src/tools/ak/liteparse/values_parse_test.go create mode 100644 toolchains/emulator/toolchain.bzl diff --git a/BUILD b/BUILD index e69de29..4415141 100644 --- a/BUILD +++ b/BUILD @@ -0,0 +1,4 @@ +load("@bazel_gazelle//:def.bzl", "gazelle") + +# gazelle:prefix github.com/bazelbuild/rules_android +gazelle(name = "gazelle") diff --git a/WORKSPACE b/WORKSPACE index 6949549..0c59e27 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,17 +1,18 @@ workspace(name = "build_bazel_rules_android") + load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") maybe( http_archive, name = "bazel_skylib", + sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c", urls = [ "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz", "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz", ], - sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c", ) -register_toolchains("//android/toolchains/emulator:all") +register_toolchains("//toolchains/emulator:all") maybe( http_archive, @@ -23,8 +24,36 @@ maybe( ], ) +maybe( + http_archive, + name = "bazel_gazelle", + sha256 = "5982e5463f171da99e3bdaeff8c0f48283a7a5f396ec5282910b9e8a49c0dd7e", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.25.0/bazel-gazelle-v0.25.0.tar.gz", + "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.25.0/bazel-gazelle-v0.25.0.tar.gz", + ], +) + load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") +load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") + +go_repository( + name = "com_github_golang_protobuf", + importpath = "github.com/golang/protobuf", + sum = "h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=", + version = "v1.5.2", +) + +go_repository( + name = "com_github_google_go_cmp", + importpath = "github.com/google/go-cmp", + sum = "h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=", + version = "v0.5.9", +) go_rules_dependencies() go_register_toolchains(version = "1.18.3") + +gazelle_dependencies() +# gazelle:repository go_repository name=org_golang_x_xerrors importpath=golang.org/x/xerrors diff --git a/src/tools/ak/compile/BUILD b/src/tools/ak/compile/BUILD index 18dddfd..8980b3a 100644 --- a/src/tools/ak/compile/BUILD +++ b/src/tools/ak/compile/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test", "go_proto_library") +load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test") # Description: # Package for compile module package(default_visibility = ["//visibility:public"]) diff --git a/src/tools/ak/dex/BUILD b/src/tools/ak/dex/BUILD index 2fbfbb4..6da7928 100644 --- a/src/tools/ak/dex/BUILD +++ b/src/tools/ak/dex/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test", "go_proto_library") +load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test") # Package for dex compilation module package(default_visibility = ["//visibility:public"]) diff --git a/src/tools/ak/link/BUILD b/src/tools/ak/link/BUILD index 21282db..275a2b7 100644 --- a/src/tools/ak/link/BUILD +++ b/src/tools/ak/link/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test", "go_proto_library") +load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test") # Description: # Package for link module package(default_visibility = ["//visibility:public"]) diff --git a/src/tools/ak/liteparse/BUILD b/src/tools/ak/liteparse/BUILD new file mode 100644 index 0000000..cce8d1d --- /dev/null +++ b/src/tools/ak/liteparse/BUILD @@ -0,0 +1,54 @@ +load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test") +# Description: +# Package for parse module +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +go_library( + name = "liteparse", + srcs = [ + "liteparse.go", + "non_values_parse.go", + "values_parse.go", + ], + deps = [ + "//src/common/golang:flags", + "//src/common/golang:walk", + "//src/tools/ak:types", + "//src/tools/ak/res", + "//src/tools/ak/res/proto:res_data_go_proto", + "//src/tools/ak/res/proto:res_meta_go_proto", + "//src/tools/ak/res/respipe", + "//src/tools/ak/res/resxml", + "@com_github_golang_protobuf//proto", + ], +) + +go_test( + name = "liteparse_test", + size = "small", + srcs = [ + "liteparse_test.go", + "non_values_parse_test.go", + "values_parse_test.go", + ], + data = glob(["testdata/**"]), + library = ":liteparse", + deps = [ + "//src/tools/ak/res", + "//src/tools/ak/res/proto:res_data_go_proto", + "//src/tools/ak/res/respipe", + "//src/tools/ak/res/resxml", + "@com_github_google_go_cmp//cmp:go_default_library", + ], +) + +go_binary( + name = "liteparse_bin", + srcs = ["liteparse_bin.go"], + deps = [ + ":liteparse", + "//src/common/golang:flagfile", + ], +) diff --git a/src/tools/ak/liteparse/liteparse.go b/src/tools/ak/liteparse/liteparse.go new file mode 100644 index 0000000..55f4d9a --- /dev/null +++ b/src/tools/ak/liteparse/liteparse.go @@ -0,0 +1,436 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package liteparse does a light parsing of android resources files that can be used at a later +// stage to generate R.java files. +package liteparse + +import ( + "bytes" + "context" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path" + "path/filepath" + "strings" + "sync" + + "src/common/golang/flags" + "src/common/golang/walk" + rdpb "src/tools/ak/res/proto/res_data_go_proto" + "src/tools/ak/res/res" + "src/tools/ak/res/respipe/respipe" + "src/tools/ak/res/resxml/resxml" + "src/tools/ak/types" + "github.com/golang/protobuf/proto" +) + +var ( + // Cmd defines the command to run the res parser. + Cmd = types.Command{ + Init: Init, + Run: Run, + Desc: desc, + Flags: []string{"resourceFiles", "rPbOutput"}, + } + + resourceFiles flags.StringList + rPbOutput string + pkg string + + initOnce sync.Once +) + +const ( + numParsers = 25 +) + +// Init initializes parse. Flags here need to match flags in AndroidResourceParsingAction. +func Init() { + initOnce.Do(func() { + flag.Var(&resourceFiles, "res_files", "Resource files and asset directories to parse.") + flag.StringVar(&rPbOutput, "out", "", "Path to the output proto file.") + flag.StringVar(&pkg, "pkg", "", "Java package name.") + }) +} + +func desc() string { + return "Lite parses the resource files to generate an R.pb." +} + +// Run runs the parser. +func Run() { + rscs := ParseAll(context.Background(), resourceFiles, pkg) + b, err := proto.Marshal(rscs) + if err != nil { + log.Fatal(err) + } + if err = ioutil.WriteFile(rPbOutput, b, 0644); err != nil { + log.Fatal(err) + } +} + +type resourceFile struct { + pathInfo *res.PathInfo + contents []byte +} + +// ParseAll parses all the files in resPaths, which can contain both files and directories, +// and returns pb. +func ParseAll(ctx context.Context, resPaths []string, packageName string) *rdpb.Resources { + resFiles, err := walk.Files(resPaths) + if err != nil { + log.Fatal(err) + } + pifs, rscs, err := initializeFileParse(resFiles, packageName) + if err != nil { + log.Fatal(err) + } + if len(pifs) == 0 { + return rscs + } + + piC := make(chan *res.PathInfo, len(pifs)) + for _, pi := range pifs { + piC <- pi + } + close(piC) + + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + resC, errC := ResParse(ctx, piC) + rscs.Resource, err = processResAndErr(resC, errC) + if err != nil { + cancel() + log.Fatal(err) + } + return rscs +} + +// ResParse consumes a stream of resource paths and converts them into resource protos. These +// protos will only have the minimal name/type info set. +func ResParse(ctx context.Context, piC <-chan *res.PathInfo) (<-chan *rdpb.Resource, <-chan error) { + parserC := make(chan *res.PathInfo) + var parsedResCs []<-chan *rdpb.Resource + var parsedErrCs []<-chan error + + for i := 0; i < numParsers; i++ { + parsedResC, parsedErrC := xmlParser(ctx, parserC) + parsedResCs = append(parsedResCs, parsedResC) + parsedErrCs = append(parsedErrCs, parsedErrC) + } + pathResC := make(chan *rdpb.Resource) + pathErrC := make(chan error) + go func() { + defer close(pathResC) + defer close(pathErrC) + defer close(parserC) + + for pi := range piC { + np, err := needsParse(pi) + if err != nil { + pathErrC <- err + return + } else if np { + parserC <- pi + } + if !parsePathInfo(ctx, pi, pathResC, pathErrC) { + return + } + } + }() + parsedResCs = append(parsedResCs, pathResC) + parsedErrCs = append(parsedErrCs, pathErrC) + resC := respipe.MergeResStreams(ctx, parsedResCs) + errC := respipe.MergeErrStreams(ctx, parsedErrCs) + + return resC, errC +} + +// ParseAllContents parses all resource files with paths and contents and returns pb representing +// the R class that is generated from the files with the package packageName. +// paths and contents must have the same length, and a file with paths[i] file path +// has file contents contents[i]. +func ParseAllContents(ctx context.Context, paths []string, contents [][]byte, packageName string) (*rdpb.Resources, error) { + if len(paths) != len(contents) { + return nil, fmt.Errorf("length of paths (%v) and contents (%v) are not equal", len(paths), len(contents)) + } + pifs, rscs, err := initializeFileParse(paths, packageName) + if err != nil { + return nil, err + } + if len(pifs) == 0 { + return rscs, nil + } + + var rfC []*resourceFile + for i, pi := range pifs { + rfC = append(rfC, &resourceFile{ + pathInfo: pi, + contents: contents[i], + }) + } + + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + resC, errC := resParseContents(ctx, rfC) + rscs.Resource, err = processResAndErr(resC, errC) + if err != nil { + return nil, err + } + return rscs, nil +} + +// resParseContents consumes resource files and converts them into resource protos. +// These protos will only have the minimal name/type info set. +// The returned channels will be consumed by processRessAndErr. +func resParseContents(ctx context.Context, rfC []*resourceFile) (<-chan *rdpb.Resource, <-chan error) { + parserC := make(chan *resourceFile) + var parsedResCs []<-chan *rdpb.Resource + var parsedErrCs []<-chan error + + for i := 0; i < numParsers; i++ { + parsedResC, parsedErrC := xmlParserContents(ctx, parserC) + parsedResCs = append(parsedResCs, parsedResC) + parsedErrCs = append(parsedErrCs, parsedErrC) + } + pathResC := make(chan *rdpb.Resource) + pathErrC := make(chan error) + go func() { + defer close(pathResC) + defer close(pathErrC) + defer close(parserC) + + for _, rf := range rfC { + if needsParseContents(rf.pathInfo, bytes.NewReader(rf.contents)) { + parserC <- rf + } + if !parsePathInfo(ctx, rf.pathInfo, pathResC, pathErrC) { + return + } + } + }() + parsedResCs = append(parsedResCs, pathResC) + parsedErrCs = append(parsedErrCs, pathErrC) + resC := respipe.MergeResStreams(ctx, parsedResCs) + errC := respipe.MergeErrStreams(ctx, parsedErrCs) + + return resC, errC +} + +// initializeFileParse returns a slice of all PathInfos of files contained in each file path, +// which must be a file (not a directory). It also returns Resources with packageName. +func initializeFileParse(filePaths []string, packageName string) ([]*res.PathInfo, *rdpb.Resources, error) { + rscs := &rdpb.Resources{ + Pkg: packageName, + } + + pifs, err := res.MakePathInfos(filePaths) + if err != nil { + return nil, nil, err + } + + return pifs, rscs, nil +} + +// parsePathInfo attempts to parse the PathInfo and send the provided Resource and error to the +// provided chan. If the context is canceled, returns false, and otherwise, returns true. +func parsePathInfo(ctx context.Context, pi *res.PathInfo, pathResC chan<- *rdpb.Resource, pathErrC chan<- error) bool { + if rawName, ok := pathAsRes(pi); ok { + fqn, err := res.ParseName(rawName, pi.Type) + if err != nil { + return respipe.SendErr(ctx, pathErrC, respipe.Errorf(ctx, "%s: name parse failed: %v", pi.Path, err)) + } + r := new(rdpb.Resource) + if err := fqn.SetResource(r); err != nil { + return respipe.SendErr(ctx, pathErrC, respipe.Errorf(ctx, "%s: name->proto failed: %v", fqn, err)) + } + return respipe.SendRes(ctx, pathResC, r) + } + return true +} + +// processResAndErr processes the res and err channels and returns the resources if successful +// or the first encountered error. +func processResAndErr(resC <-chan *rdpb.Resource, errC <-chan error) ([]*rdpb.Resource, error) { + parseErrChan := make(chan error, 1) + go func() { + for err := range errC { + if err != nil { + parseErrChan <- err + return + } + } + }() + + doneChan := make(chan struct{}, 1) + var res []*rdpb.Resource + go func() { + for r := range resC { + res = append(res, r) + } + doneChan <- struct{}{} + }() + + select { + case err := <-parseErrChan: + return nil, err + case <-doneChan: + } + + return res, nil +} + +// xmlParser consumes a stream of paths that need to have their xml contents parsed into resource +// protos. We only need to get names and types - so the parsing is very quick. +func xmlParser(ctx context.Context, piC <-chan *res.PathInfo) (<-chan *rdpb.Resource, <-chan error) { + resC := make(chan *rdpb.Resource) + errC := make(chan error) + go func() { + defer close(resC) + defer close(errC) + for p := range piC { + if !syncParse(respipe.PrefixErr(ctx, fmt.Sprintf("%s xml-parse: ", p.Path)), p, resC, errC) { + // ctx must have been canceled - exit. + return + } + } + }() + return resC, errC +} + +// xmlParserContents consumes a stream of resource files that need to have their xml contents +// parsed into resource protos. We only need to get names and types - so the parsing is very quick. +func xmlParserContents(ctx context.Context, rfC <-chan *resourceFile) (<-chan *rdpb.Resource, <-chan error) { + resC := make(chan *rdpb.Resource) + errC := make(chan error) + go func() { + defer close(resC) + defer close(errC) + for rf := range rfC { + if !syncParseContents(respipe.PrefixErr(ctx, fmt.Sprintf("%s xml-parse: ", rf.pathInfo.Path)), rf.pathInfo, bytes.NewReader(rf.contents), resC, errC) { + // ctx must have been canceled - exit. + return + } + } + }() + return resC, errC +} + +func syncParse(ctx context.Context, p *res.PathInfo, resC chan<- *rdpb.Resource, errC chan<- error) bool { + f, err := os.Open(p.Path) + if err != nil { + return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "open failed: %v", err)) + } + defer f.Close() + return syncParseContents(ctx, p, f, resC, errC) +} + +func syncParseContents(ctx context.Context, p *res.PathInfo, fileReader io.Reader, resC chan<- *rdpb.Resource, errC chan<- error) bool { + parsedResC, mergedErrC := parseContents(ctx, p, fileReader) + for parsedResC != nil || mergedErrC != nil { + select { + case r, ok := <-parsedResC: + if !ok { + parsedResC = nil + continue + } + if !respipe.SendRes(ctx, resC, r) { + return false + } + case e, ok := <-mergedErrC: + if !ok { + mergedErrC = nil + continue + } + if !respipe.SendErr(ctx, errC, e) { + return false + } + } + + } + return true +} + +func parseContents(ctx context.Context, filePathInfo *res.PathInfo, fileReader io.Reader) (resC <-chan *rdpb.Resource, errC <-chan error) { + xmlC, xmlErrC := resxml.StreamDoc(ctx, fileReader) + var parsedErrC <-chan error + if filePathInfo.Type == res.ValueType { + ctx := respipe.PrefixErr(ctx, "mini-values-parse: ") + resC, parsedErrC = valuesParse(ctx, xmlC) + } else { + ctx := respipe.PrefixErr(ctx, "mini-non-values-parse: ") + resC, parsedErrC = nonValuesParse(ctx, xmlC) + } + errC = respipe.MergeErrStreams(ctx, []<-chan error{parsedErrC, xmlErrC}) + return resC, errC +} + +// needsParse determines if a path needs to have a values / nonvalues xml parser run to extract +// resource information. +func needsParse(pi *res.PathInfo) (bool, error) { + r, err := os.Open(pi.Path) + if err != nil { + return false, fmt.Errorf("Unable to open file %s: %s", pi.Path, err) + } + defer r.Close() + + return needsParseContents(pi, r), nil +} + +// needsParseContents determines if a path with the corresponding reader for contents needs to have a +// values / nonvalues xml parser run to extract resource information. +func needsParseContents(pi *res.PathInfo, r io.Reader) bool { + if pi.Type == res.Raw { + return false + } + if filepath.Ext(pi.Path) == ".xml" { + return true + } + if filepath.Ext(pi.Path) == "" { + var header [5]byte + _, err := io.ReadFull(r, header[:]) + if err != nil && err != io.EOF { + log.Fatal("Unable to read file %s: %s", pi.Path, err) + } + if string(header[:]) == "= 0 && pi.Type == res.Raw { + return p[:dot], true + } + if dot := strings.Index(p, "."); dot >= 0 { + return p[:dot], true + } + return p, true +} diff --git a/src/tools/ak/liteparse/liteparse_bin.go b/src/tools/ak/liteparse/liteparse_bin.go new file mode 100644 index 0000000..cb76afe --- /dev/null +++ b/src/tools/ak/liteparse/liteparse_bin.go @@ -0,0 +1,30 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// liteparse_bin is a command line tool that does a light parsing of android resources files that +// can be used at a later stage to generate R.java files. +package main + +import ( + "flag" + + _ "src/common/golang/flagfile" + "src/tools/ak/liteparse/liteparse" +) + +func main() { + liteparse.Init() + flag.Parse() + liteparse.Run() +} diff --git a/src/tools/ak/liteparse/liteparse_test.go b/src/tools/ak/liteparse/liteparse_test.go new file mode 100644 index 0000000..0b97685 --- /dev/null +++ b/src/tools/ak/liteparse/liteparse_test.go @@ -0,0 +1,375 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package liteparse + +import ( + "context" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "reflect" + "sort" + "testing" + + rdpb "src/tools/ak/res/proto/res_data_go_proto" + "src/tools/ak/res/res" + "src/tools/ak/res/respipe/respipe" + "github.com/google/go-cmp/cmp" +) + +const ( + testdata = "src/tools/ak/liteparse/testdata/" +) + +func TestPathAsRes(t *testing.T) { + tests := []struct { + arg string + name string + ok bool + }{ + { + "foo/bar/res/values/strings.xml", + "", + false, + }, + { + "foo/bar/res/values-ldpi-v19/strings.xml", + "", + false, + }, + { + "foo/bar/res/layout-en-US-v19/hello_america.xml", + "hello_america", + true, + }, + { + "foo/bar/res/xml-land/perfs.xml", + "perfs", + true, + }, + { + "foo/bar/res/drawable-land/eagle.png", + "eagle", + true, + }, + { + "foo/bar/res/raw/vid.1080p.png", + "vid.1080p", + true, + }, + { + "foo/bar/res/drawable-land/circle.9.png", + "circle", + true, + }, + } + + for _, tc := range tests { + pi, err := res.ParsePath(tc.arg) + if err != nil { + t.Errorf("res.ParsePath(%q) returns %v unexpectedly", tc.arg, err) + continue + } + rawName, ok := pathAsRes(&pi) + if tc.name != rawName || ok != tc.ok { + t.Errorf("pathAsRes(%v) got %q, %t want %q, %t", pi, rawName, ok, tc.name, tc.ok) + } + } +} + +func TestNeedsParse(t *testing.T) { + tests := []struct { + arg string + content string + want bool + }{ + { + "foo/bar/res/values/strings.xml", + "", + true, + }, + { + "foo/bar/res/values-ldpi-v19/strings.xml", + "", + true, + }, + { + "foo/bar/res/layout-en-US-v19/hello_america.xml", + "", + true, + }, + { + "foo/bar/res/xml-land/perfs.xml", + "", + true, + }, + { + "foo/bar/res/drawable-land/eagle.png", + "", + false, + }, + { + "foo/bar/res/drawable-land/eagle", + "", + false, + }, + { + "foo/bar/res/drawable-land/eagle_xml", + "", + true, + }, + { + "foo/bar/res/drawable-land/eagle_txt", + "some non-xml file", + false, + }, + } + + for _, tc := range tests { + f := createTestFile(tc.arg, tc.content) + defer os.Remove(f) + pi, err := res.ParsePath(f) + if err != nil { + t.Errorf("res.ParsePath(%s) returns %v unexpectedly", f, err) + continue + } + got, err := needsParse(&pi) + if err != nil { + t.Errorf("needsParse(%v) returns %v unexpectedly", pi, err) + } + if got != tc.want { + t.Errorf("needsParse(%v) got %t want %t", pi, got, tc.want) + } + } +} + +func createTestFile(path, content string) string { + dir := filepath.Dir(path) + tmpDir, err := ioutil.TempDir("", "test") + if err != nil { + log.Fatal(err) + } + err = os.MkdirAll(tmpDir+"/"+dir, os.ModePerm) + if err != nil { + log.Fatal(err) + } + f, err := os.Create(tmpDir + "/" + path) + if err != nil { + log.Fatal(err) + } + if _, err := f.Write([]byte(content)); err != nil { + log.Fatal(err) + } + if err := f.Close(); err != nil { + log.Fatal(err) + } + return f.Name() +} + +func TestParse(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + testRes := createResFile("res") + piC, pathErrC := respipe.EmitPathInfosDir(ctx, testRes) + resC, parseErrC := ResParse(ctx, piC) + errC := respipe.MergeErrStreams(ctx, []<-chan error{pathErrC, parseErrC}) + var parsedNames []string + for resC != nil || errC != nil { + select { + case r, ok := <-resC: + if !ok { + resC = nil + continue + } + pn, err := res.ParseName(r.GetName(), res.Type(r.ResourceType)) + if err != nil { + t.Errorf("res.ParseName(%q, %v) unexpected err: %v", r.GetName(), r.ResourceType, err) + fmt.Printf("parsename err: %v\n", err) + continue + } + parsedNames = append(parsedNames, pn.String()) + case e, ok := <-errC: + if !ok { + errC = nil + continue + } + t.Errorf("Unexpected err: %v", e) + } + } + sort.Strings(parsedNames) + expectedNames := []string{ + "res-auto:attr/bg", + "res-auto:attr/size", + "res-auto:drawable/foo", + "res-auto:id/item1", + "res-auto:id/item2", + "res-auto:id/large", + "res-auto:id/response", + "res-auto:id/small", + "res-auto:menu/simple", + "res-auto:raw/garbage", + "res-auto:string/exlusive", + "res-auto:string/greeting", + "res-auto:string/lonely", + "res-auto:string/title", + "res-auto:string/title2", + "res-auto:string/version", + "res-auto:string/version", // yes duplicated (appears in 2 different files, dupes get handled later in the pipeline) + "res-auto:styleable/absPieChart", + } + if !reflect.DeepEqual(parsedNames, expectedNames) { + t.Errorf("%s: has these resources: %s expected: %s", testRes, parsedNames, expectedNames) + } +} + +func TestParseAll(t *testing.T) { + tests := []struct { + resfiles []string + pkg string + want *rdpb.Resources + }{ + { + resfiles: createResfiles([]string{}), + pkg: "", + want: createResources("", []rdpb.Resource_Type{}, []string{}), + }, + { + resfiles: createResfiles([]string{"mini-1"}), + pkg: "example", + want: createResources("example", []rdpb.Resource_Type{rdpb.Resource_STRING}, []string{"greeting"}), + }, + { + resfiles: createResfiles([]string{"mini-2"}), + pkg: "com.example", + want: createResources("com.example", []rdpb.Resource_Type{rdpb.Resource_XML, rdpb.Resource_ID}, []string{"foo", "foobar"}), + }, + { + resfiles: createResfiles([]string{"res/drawable-ldpi/foo.9.png", "res/menu/simple.xml"}), + pkg: "com.example", + want: createResources("com.example", + []rdpb.Resource_Type{rdpb.Resource_DRAWABLE, rdpb.Resource_MENU, rdpb.Resource_ID, rdpb.Resource_ID}, + []string{"foo", "simple", "item1", "item2"}), + }, + } + + for _, tc := range tests { + if got := ParseAll(context.Background(), tc.resfiles, tc.pkg); !resourcesEqual(got, tc.want) { + t.Errorf("ParseAll(%v, %v) = {%v}, want {%v}", tc.resfiles, tc.pkg, got, tc.want) + } + } +} + +func TestParseAllContents(t *testing.T) { + tests := []struct { + resfiles []string + pkg string + want *rdpb.Resources + }{ + { + resfiles: createResfiles([]string{}), + pkg: "", + want: createResources("", []rdpb.Resource_Type{}, []string{}), + }, + { + resfiles: createResfiles([]string{"mini-1/res/values/strings.xml"}), + pkg: "example", + want: createResources("example", []rdpb.Resource_Type{rdpb.Resource_STRING}, []string{"greeting"}), + }, + { + resfiles: createResfiles([]string{"mini-2/res/xml/foo.xml"}), + pkg: "com.example", + want: createResources("com.example", []rdpb.Resource_Type{rdpb.Resource_XML, rdpb.Resource_ID}, []string{"foo", "foobar"}), + }, + { + resfiles: createResfiles([]string{"res/drawable-ldpi/foo.9.png", "res/menu/simple.xml"}), + pkg: "com.example", + want: createResources("com.example", + []rdpb.Resource_Type{rdpb.Resource_DRAWABLE, rdpb.Resource_MENU, rdpb.Resource_ID, rdpb.Resource_ID}, + []string{"foo", "simple", "item1", "item2"}), + }, + } + + for _, tc := range tests { + allContents := getAllContents(t, tc.resfiles) + got, err := ParseAllContents(context.Background(), tc.resfiles, allContents, tc.pkg) + if err != nil { + t.Errorf("ParseAllContents(%v, %v) failed with error %v", tc.resfiles, tc.pkg, err) + } + if !resourcesEqual(got, tc.want) { + t.Errorf("ParseAllContents(%v, %v) = {%v}, want {%v}", tc.resfiles, tc.pkg, got, tc.want) + } + } +} + +// createResFile creates filename with the testdata as the base +func createResFile(filename string) string { + return os.Getenv("TEST_SRCDIR") + "/" + os.Getenv("TEST_WORKSPACE") + "/" + testdata + filename +} + +// createResfiles creates filenames with the testdata as the base +func createResfiles(filenames []string) []string { + var resfiles []string + for _, filename := range filenames { + resfiles = append(resfiles, createResFile(filename)) + } + return resfiles +} + +func getAllContents(t *testing.T, paths []string) [][]byte { + var allContents [][]byte + for _, path := range paths { + contents, err := os.ReadFile(path) + if err != nil { + t.Errorf("cannot read file %v: %v", path, err) + } + allContents = append(allContents, contents) + } + return allContents +} + +// createResources creates rdpb.Resources with package name pkg and resources {names[i], resource[i]} +func createResources(pkg string, resources []rdpb.Resource_Type, names []string) *rdpb.Resources { + rscs := &rdpb.Resources{ + Pkg: pkg, + } + for i := 0; i < len(names); i++ { + r := &rdpb.Resource{Name: names[i], ResourceType: resources[i]} + rscs.Resource = append(rscs.Resource, r) + } + return rscs +} + +// resourcesEqual checks if the two resources have the same package names and resources +func resourcesEqual(rscs1 *rdpb.Resources, rscs2 *rdpb.Resources) bool { + return rscs1.Pkg == rscs2.Pkg && cmp.Equal(createResourcesMap(rscs1), createResourcesMap(rscs2)) +} + +// createResourcesMap creates a map of resources contained in rscs that maps the rdpb.Resource_Type to the names and the number of times the name appears. +func createResourcesMap(rscs *rdpb.Resources) map[rdpb.Resource_Type]map[string]int { + m := make(map[rdpb.Resource_Type]map[string]int) + for _, r := range rscs.Resource { + if _, ok := m[r.GetResourceType()]; !ok { + m[r.GetResourceType()] = make(map[string]int) + m[r.GetResourceType()][r.GetName()] = 1 + } else if _, ok := m[r.GetResourceType()][r.GetName()]; !ok { + m[r.GetResourceType()][r.GetName()] = 1 + } else { + m[r.GetResourceType()][r.GetName()]++ + } + } + return m +} diff --git a/src/tools/ak/liteparse/non_values_parse.go b/src/tools/ak/liteparse/non_values_parse.go new file mode 100644 index 0000000..248264c --- /dev/null +++ b/src/tools/ak/liteparse/non_values_parse.go @@ -0,0 +1,61 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package liteparse + +import ( + "context" + "strings" + + rdpb "src/tools/ak/res/proto/res_data_go_proto" + "src/tools/ak/res/res" + "src/tools/ak/res/respipe/respipe" + "src/tools/ak/res/resxml/resxml" +) + +// nonValuesParse searches a non-values xml document for ID declarations. It creates ID +// resources for any declarations it finds. +func nonValuesParse(ctx context.Context, xmlC <-chan resxml.XMLEvent) (<-chan *rdpb.Resource, <-chan error) { + resC := make(chan *rdpb.Resource) + errC := make(chan error) + go func() { + defer close(resC) + defer close(errC) + for xe := range xmlC { + for _, a := range resxml.Attrs(xe) { + if strings.HasPrefix(a.Value, res.GeneratedIDPrefix) { + unparsed := strings.Replace(a.Value, res.GeneratedIDPrefix, "@id", 1) + fqn, err := res.ParseName(unparsed, res.ID) + if err != nil { + if !respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%s: unparsable id attribute: %+v: %v", a.Value, xe, err)) { + return + } + continue + } + r := new(rdpb.Resource) + if err := fqn.SetResource(r); err != nil { + if !respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%s: name->proto failed: %+v", fqn, err)) { + return + } + continue + } + if !respipe.SendRes(ctx, resC, r) { + return + } + } + } + } + }() + return resC, errC +} diff --git a/src/tools/ak/liteparse/non_values_parse_test.go b/src/tools/ak/liteparse/non_values_parse_test.go new file mode 100644 index 0000000..9f6d3d6 --- /dev/null +++ b/src/tools/ak/liteparse/non_values_parse_test.go @@ -0,0 +1,88 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package liteparse + +import ( + "bytes" + "context" + "reflect" + "testing" + + "src/tools/ak/res/res" + "src/tools/ak/res/respipe/respipe" + "src/tools/ak/res/resxml/resxml" +) + +func TestResNonValuesParse(t *testing.T) { + tests := []struct { + doc string + wanted []string + }{ + { + ` + + + + + + + + + + `, + []string{ + "res-auto:id/new_tv", + "res-auto:id/bad_layouts", + "res-auto:id/id_here_too", + "res-auto:id/really_bad_layouts", + }, + }, + } + + for _, tc := range tests { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + xmlC, xmlErrC := resxml.StreamDoc(ctx, bytes.NewBufferString(tc.doc)) + resC, parseErrC := nonValuesParse(ctx, xmlC) + errC := respipe.MergeErrStreams(ctx, []<-chan error{xmlErrC, parseErrC}) + var parsedNames []string + for resC != nil || errC != nil { + select { + case r, ok := <-resC: + if !ok { + resC = nil + continue + } + pn, err := res.ParseName(r.GetName(), res.Type(r.ResourceType)) + if err != nil { + t.Errorf("res.ParseName(%s, %v) unexpected err: %v", r.GetName(), r.ResourceType, err) + } + parsedNames = append(parsedNames, pn.String()) + case e, ok := <-errC: + if !ok { + errC = nil + continue + } + t.Errorf("unexpected error: %v", e) + } + } + + if !reflect.DeepEqual(parsedNames, tc.wanted) { + t.Errorf("nonValuesParse of: %s got: %s wanted: %s", tc.doc, parsedNames, tc.wanted) + } + } + +} diff --git a/src/tools/ak/liteparse/testdata/mini-1/res/values/strings.xml b/src/tools/ak/liteparse/testdata/mini-1/res/values/strings.xml new file mode 100644 index 0000000..26d1276 --- /dev/null +++ b/src/tools/ak/liteparse/testdata/mini-1/res/values/strings.xml @@ -0,0 +1,3 @@ + + hello world + diff --git a/src/tools/ak/liteparse/testdata/mini-2/res/xml/foo.xml b/src/tools/ak/liteparse/testdata/mini-2/res/xml/foo.xml new file mode 100644 index 0000000..26c82a3 --- /dev/null +++ b/src/tools/ak/liteparse/testdata/mini-2/res/xml/foo.xml @@ -0,0 +1,3 @@ + + + diff --git a/src/tools/ak/liteparse/testdata/res/drawable-ldpi/foo.9.png b/src/tools/ak/liteparse/testdata/res/drawable-ldpi/foo.9.png new file mode 100644 index 0000000..f6d1995 --- /dev/null +++ b/src/tools/ak/liteparse/testdata/res/drawable-ldpi/foo.9.png @@ -0,0 +1,2 @@ + +dont parse me ! diff --git a/src/tools/ak/liteparse/testdata/res/menu/simple.xml b/src/tools/ak/liteparse/testdata/res/menu/simple.xml new file mode 100644 index 0000000..e9e33cb --- /dev/null +++ b/src/tools/ak/liteparse/testdata/res/menu/simple.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/tools/ak/liteparse/testdata/res/raw/garbage.xml b/src/tools/ak/liteparse/testdata/res/raw/garbage.xml new file mode 100644 index 0000000..4babd14 --- /dev/null +++ b/src/tools/ak/liteparse/testdata/res/raw/garbage.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/tools/ak/liteparse/testdata/res/values-v19/strings.xml b/src/tools/ak/liteparse/testdata/res/values-v19/strings.xml new file mode 100644 index 0000000..5839831 --- /dev/null +++ b/src/tools/ak/liteparse/testdata/res/values-v19/strings.xml @@ -0,0 +1,5 @@ + +to_v19 +beta + + diff --git a/src/tools/ak/liteparse/testdata/res/values/other.xml b/src/tools/ak/liteparse/testdata/res/values/other.xml new file mode 100644 index 0000000..796472a --- /dev/null +++ b/src/tools/ak/liteparse/testdata/res/values/other.xml @@ -0,0 +1,3 @@ + +string + diff --git a/src/tools/ak/liteparse/testdata/res/values/vals.xml b/src/tools/ak/liteparse/testdata/res/values/vals.xml new file mode 100644 index 0000000..47d8e20 --- /dev/null +++ b/src/tools/ak/liteparse/testdata/res/values/vals.xml @@ -0,0 +1,15 @@ + +Hello dear user + + + + + + + + + +alpha +title +title2 + diff --git a/src/tools/ak/liteparse/values_parse.go b/src/tools/ak/liteparse/values_parse.go new file mode 100644 index 0000000..048133b --- /dev/null +++ b/src/tools/ak/liteparse/values_parse.go @@ -0,0 +1,226 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package liteparse + +import ( + "context" + "encoding/xml" + "fmt" + + rdpb "src/tools/ak/res/proto/res_data_go_proto" + rmpb "src/tools/ak/res/proto/res_meta_go_proto" + "src/tools/ak/res/res" + "src/tools/ak/res/respipe/respipe" + "src/tools/ak/res/resxml/resxml" +) + +// valuesParse handles all tags beneath and extracts the associated +// ResourceType/names. Any encountered resources or errors are passed back on the returned channels. +func valuesParse(ctx context.Context, xmlC <-chan resxml.XMLEvent) (<-chan *rdpb.Resource, <-chan error) { + resC := make(chan *rdpb.Resource) + errC := make(chan error) + go func() { + defer close(resC) + defer close(errC) + for { + xe, ok := resxml.ConsumeUntil(res.ResourcesTagName, xmlC) + if !ok { + return + } + resChildrenC := resxml.ForwardChildren(ctx, xe, xmlC) + for xe := range resChildrenC { + se, ok := xe.Token.(xml.StartElement) + if !ok { + // we ignore all non-start elements during a mini-parse. + continue + } + + tagChildrenC := resxml.ForwardChildren(ctx, xe, resChildrenC) + ctx := respipe.PrefixErr(ctx, fmt.Sprintf("tag-name: %s at: %d: ", se.Name, xe.Offset)) + if t, ok := res.ResourcesTagToType[se.Name.Local]; ok { + if !minResChildParse(ctx, xe, t, tagChildrenC, resC, errC) { + return + } + } else if resxml.SloppyMatches(se.Name, res.ItemTagName) { + if !itemParse(ctx, xe, tagChildrenC, resC, errC) { + return + } + } + for range tagChildrenC { + // exhaust any children beneath this tag, we did not need them in the mini-parse. + } + } + } + }() + return resC, errC +} + +// itemParse handles tags that are children of +func itemParse(ctx context.Context, xe resxml.XMLEvent, childC <-chan resxml.XMLEvent, resC chan<- *rdpb.Resource, errC chan<- error) bool { + name, err := extractName(xe) + if err != nil { + return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%v: expected to encounter name attribute: %v", xe, err)) + } + var tv string + for _, a := range resxml.Attrs(xe) { + if resxml.SloppyMatches(res.TypeAttrName, a.Name) { + tv = a.Value + } + } + if tv == "" { + return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%v: needs type atttribute", xe)) + } + t, err := res.ParseType(tv) + if err != nil { + return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%q: cannot convert to type: %v", tv, err)) + } + fqn, err := res.ParseName(name, t) + if err != nil { + return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%q / type: %s: convert to fqn: %v", name, t, err)) + } + r := new(rdpb.Resource) + if err := fqn.SetResource(r); err != nil { + return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%v: name->proto failed: %v", fqn, err)) + } + return respipe.SendRes(ctx, resC, r) +} + +// Returns the value of the name attribute or an error. +func extractName(xe resxml.XMLEvent) (string, error) { + for _, a := range resxml.Attrs(xe) { + if resxml.SloppyMatches(res.NameAttrName, a.Name) { + return a.Value, nil + } + } + return "", fmt.Errorf("Expected to encounter name attribute within: %v", resxml.Attrs(xe)) +} + +// minResChildParse handles a single top-level tag beneath and extracts all ResourceTypes/Names beneath it. It returns false if it detects that the context is done. +func minResChildParse(ctx context.Context, xe resxml.XMLEvent, t res.Type, childC <-chan resxml.XMLEvent, resC chan<- *rdpb.Resource, errC chan<- error) bool { + name, err := extractName(xe) + if err != nil { + return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%#v: needs name attribute: %v", xe, err)) + } + + fqn, err := res.ParseName(name, t) + if err != nil { + return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%s: invalid name: %v", name, err)) + } + + r := new(rdpb.Resource) + if err := fqn.SetResource(r); err != nil { + return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%v: name->proto failed: %v", fqn, err)) + } + if fqn.Type == res.Styleable { + md, ok := parseStyleableChildren(ctx, childC, resC, errC) + if !ok { + return false + } + if err := fqn.SetMetaData(md); err != nil { + return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%v: could not set stylablemeta: %v", fqn, err)) + } + r.StyleableValue = md + } + if fqn.Type == res.Attr && !parseAttrChildren(ctx, childC, resC, errC) { + return false + } + + return respipe.SendRes(ctx, resC, r) +} + +// parseAttrChildren looks at the children of an tag and determines if any of them creates resources. +// If it realizes that the provided ctx is canceled, it returns true, otherwise false. +func parseAttrChildren(ctx context.Context, xmlC <-chan resxml.XMLEvent, resC chan<- *rdpb.Resource, errC chan<- error) bool { + for c := range xmlC { + ce, ok := c.Token.(xml.StartElement) + if !ok { + // do not care about non-start element events. + continue + } + if !resxml.SloppyMatches(res.EnumTagName, ce.Name) && !resxml.SloppyMatches(res.FlagTagName, ce.Name) { + // only want or elements + continue + } + + enumFlagName, err := extractName(c) + if err != nil { + return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%v: flag / enum should have had a name attribute: %v", ce, err)) + } + cFqn, err := res.ParseName(enumFlagName, res.ID) + if err != nil { + return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%v: could not parse child of : %v", ce, err)) + } + cr := new(rdpb.Resource) + if err := cFqn.SetResource(cr); err != nil { + return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%v: name->proto failed: %v", ce, err)) + } + if !respipe.SendRes(ctx, resC, cr) { + return false + } + } + return true +} + +// parseStyleableChildren looks at the children of a tag and determines what resources they create. +func parseStyleableChildren(ctx context.Context, xmlC <-chan resxml.XMLEvent, resC chan<- *rdpb.Resource, errC chan<- error) (*rmpb.StyleableMetaData, bool) { + var attrNames []string + for c := range xmlC { + if _, ok := c.Token.(xml.StartElement); !ok { + // skip events besides start element. + continue + } + name, err := extractName(c) + if err != nil { + // being liberal with what we can encounter under a tag. + continue + } + attrFqn, err := res.ParseName(name, res.Attr) + if err != nil { + return nil, respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%q: could not parse name to fqn: %v", name, err)) + } + if attrFqn.Type != res.Attr { + return nil, respipe.SendErr( + ctx, errC, respipe.Errorf(ctx, "%v: name->nameid proto failed: %v", attrFqn, res.ErrWrongType)) + } + + attrNames = append(attrNames, attrFqn.String()) + if attrFqn.Package == "android" { + // since we're not generating android attributes (they already exist already) + // omit the resource proto for these attrs. + continue + } + + if attrFqn.Type == res.Attr { + ctx := respipe.PrefixErr(ctx, fmt.Sprintf("%q: child: ", name)) + childC := resxml.ForwardChildren(ctx, c, xmlC) + if !parseAttrChildren(ctx, childC, resC, errC) { + return nil, false + } + } + + attrR := new(rdpb.Resource) + if err := attrFqn.SetResource(attrR); err != nil { + return nil, respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%v: name->proto failed: %v", attrFqn, err)) + } + + if !respipe.SendRes(ctx, resC, attrR) { + return nil, false + } + + } + return &rmpb.StyleableMetaData{ + FqnAttributes: attrNames, + }, true +} diff --git a/src/tools/ak/liteparse/values_parse_test.go b/src/tools/ak/liteparse/values_parse_test.go new file mode 100644 index 0000000..a46f45b --- /dev/null +++ b/src/tools/ak/liteparse/values_parse_test.go @@ -0,0 +1,171 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package liteparse + +import ( + "bytes" + "context" + "reflect" + "strings" + "testing" + + "src/tools/ak/res/res" + "src/tools/ak/res/respipe/respipe" + "src/tools/ak/res/resxml/resxml" +) + +func TestResValuesParse(t *testing.T) { + tests := []struct { + doc string + wanted []string + wantedErr []string + }{ + { + doc: ` + 2 + hi there + `, + wanted: []string{ + "res-auto:integer/two", + "res-auto:string/embedded_stuff", + }, + }, + { + doc: ` + 12dp + + + true + `, + wanted: []string{ + "res-auto:fraction/frac", + "res-auto:id/foo", + "res-auto:id/two", + "res-auto:bool/on", + }, + }, + { + doc: ` + #fff + 100% + + + + + + + + `, + wanted: []string{ + "res-auto:color/red", + "res-auto:dimen/hundred", + "res-auto:id/cars", + "res-auto:id/planes", + "res-auto:attr/custom", + "res-auto:attr/textSize", + }, + }, + { + doc: ` + + + + + + + + 1 + @integer/two + + `, + + wanted: []string{ + "res-auto:id/tap", + "res-auto:id/double_tap", + "res-auto:attr/touch", + "res-auto:array/empty", + "res-auto:array/five", + }, + }, + { + + doc: ` + + + + + + + + + `, + wanted: []string{ + "res-auto:attr/local", + "res-auto:id/transparent", + "res-auto:id/awesome", + "res-auto:attr/overlay", + "res-auto:styleable/absPieChart", + }, + }, + { + doc: `2`, + wantedErr: []string{"Expected to encounter name attribute"}, + }, + } + + for _, tc := range tests { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + xmlC, xmlErrC := resxml.StreamDoc(ctx, bytes.NewBufferString(tc.doc)) + resC, parseErrC := valuesParse(ctx, xmlC) + errC := respipe.MergeErrStreams(ctx, []<-chan error{xmlErrC, parseErrC}) + var parsedNames []string + var errStrs []string + for resC != nil || errC != nil { + select { + case r, ok := <-resC: + if !ok { + resC = nil + continue + } + pn, err := res.ParseName(r.GetName(), res.Type(r.ResourceType)) + if err != nil { + t.Errorf("res.ParseName(%s, %v) unexpected err: %v", r.GetName(), r.ResourceType, err) + } + parsedNames = append(parsedNames, pn.String()) + case e, ok := <-errC: + if !ok { + errC = nil + continue + } + errStrs = append(errStrs, e.Error()) + } + + } + + if !reflect.DeepEqual(parsedNames, tc.wanted) { + t.Errorf("valuesParse of: %s got: %s wanted: %s", tc.doc, parsedNames, tc.wanted) + } + if len(errStrs) != len(tc.wantedErr) { + t.Errorf("%s: unexpected amount of errs: %v wanted: %v", tc.doc, errStrs, tc.wantedErr) + continue + } + for i, e := range errStrs { + if !strings.Contains(e, tc.wantedErr[i]) { + t.Errorf("doc: %q got err: %s should contain: %s", tc.doc, e, tc.wantedErr[i]) + } + } + } +} diff --git a/src/tools/ak/manifest/BUILD b/src/tools/ak/manifest/BUILD index 8dc7e3f..6d644c8 100644 --- a/src/tools/ak/manifest/BUILD +++ b/src/tools/ak/manifest/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test", "go_proto_library") +load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test") # Package for manifest compilation module package(default_visibility = ["//visibility:public"]) diff --git a/src/tools/ak/mindex/BUILD b/src/tools/ak/mindex/BUILD index 32e8f14..1054b08 100644 --- a/src/tools/ak/mindex/BUILD +++ b/src/tools/ak/mindex/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test", "go_proto_library") +load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test") # Description: # Package for mindex module package(default_visibility = ["//visibility:public"]) diff --git a/src/tools/ak/nativelib/BUILD b/src/tools/ak/nativelib/BUILD index c5d3cb2..fff7c01 100644 --- a/src/tools/ak/nativelib/BUILD +++ b/src/tools/ak/nativelib/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test", "go_proto_library") +load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test") # Description: # Package for nativelib module package(default_visibility = ["//visibility:public"]) diff --git a/src/tools/ak/nativelib/testdata/BUILD b/src/tools/ak/nativelib/testdata/BUILD index fda740f..2fc9b36 100644 --- a/src/tools/ak/nativelib/testdata/BUILD +++ b/src/tools/ak/nativelib/testdata/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test", "go_proto_library") +load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test") # Creates test data for testing the nativelib action. package(default_visibility = ["//src/tools/ak/nativelib:__subpackages__"]) diff --git a/toolchains/emulator/toolchain.bzl b/toolchains/emulator/toolchain.bzl new file mode 100644 index 0000000..61bfb77 --- /dev/null +++ b/toolchains/emulator/toolchain.bzl @@ -0,0 +1,59 @@ +# Copyright 2019 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Defines the emulator_toolchain rule to allow configuring emulator binaries to use.""" + +EmulatorInfo = provider( + doc = "Information used to launch a specific version of the emulator.", + fields = { + "emulator": "A label for the emulator launcher executable at stable version.", + "emulator_deps": "Additional files required to launch the stable version of emulator.", + "emulator_suffix": "An optional path suffix used to find emulator binary under the emulator label path", + }, +) + +def _emulator_toolchain_impl(ctx): + toolchain_info = platform_common.ToolchainInfo( + info = EmulatorInfo( + emulator = ctx.attr.emulator, + emulator_deps = ctx.attr.emulator_deps, + emulator_suffix = ctx.attr.emulator_suffix, + ), + ) + return [toolchain_info] + +emulator_toolchain = rule( + implementation = _emulator_toolchain_impl, + attrs = { + "emulator": attr.label( + allow_files = True, + cfg = "host", + mandatory = True, + ), + "emulator_deps": attr.label_list( + allow_files = True, + cfg = "host", + ), + "emulator_head": attr.label( + allow_files = True, + cfg = "host", + ), + "emulator_head_deps": attr.label_list( + allow_files = True, + cfg = "host", + ), + "emulator_suffix": attr.string(default = ""), + "emulator_head_suffix": attr.string(default = ""), + }, +) -- cgit v1.2.3 From f35a7c3f51b08700ccbf7e95d28d8f1dbfc4ba72 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Tue, 27 Sep 2022 07:21:30 -0700 Subject: Fix go-related load statements in src/tools/ak PiperOrigin-RevId: 477164631 Change-Id: I64e3abaf6a4ac36b7d1e3e934dba3ead04ea9d96 --- src/tools/ak/compile/BUILD | 2 +- src/tools/ak/dex/BUILD | 2 +- src/tools/ak/link/BUILD | 2 +- src/tools/ak/liteparse/BUILD | 2 +- src/tools/ak/manifest/BUILD | 2 +- src/tools/ak/mindex/BUILD | 2 +- src/tools/ak/nativelib/BUILD | 2 +- src/tools/ak/nativelib/testdata/BUILD | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/tools/ak/compile/BUILD b/src/tools/ak/compile/BUILD index 8980b3a..bcb5c20 100644 --- a/src/tools/ak/compile/BUILD +++ b/src/tools/ak/compile/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") # Description: # Package for compile module package(default_visibility = ["//visibility:public"]) diff --git a/src/tools/ak/dex/BUILD b/src/tools/ak/dex/BUILD index 6da7928..deb318f 100644 --- a/src/tools/ak/dex/BUILD +++ b/src/tools/ak/dex/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") # Package for dex compilation module package(default_visibility = ["//visibility:public"]) diff --git a/src/tools/ak/link/BUILD b/src/tools/ak/link/BUILD index 275a2b7..aac845c 100644 --- a/src/tools/ak/link/BUILD +++ b/src/tools/ak/link/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") # Description: # Package for link module package(default_visibility = ["//visibility:public"]) diff --git a/src/tools/ak/liteparse/BUILD b/src/tools/ak/liteparse/BUILD index cce8d1d..1e89f1f 100644 --- a/src/tools/ak/liteparse/BUILD +++ b/src/tools/ak/liteparse/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") # Description: # Package for parse module package(default_visibility = ["//visibility:public"]) diff --git a/src/tools/ak/manifest/BUILD b/src/tools/ak/manifest/BUILD index 6d644c8..48cb1a4 100644 --- a/src/tools/ak/manifest/BUILD +++ b/src/tools/ak/manifest/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") # Package for manifest compilation module package(default_visibility = ["//visibility:public"]) diff --git a/src/tools/ak/mindex/BUILD b/src/tools/ak/mindex/BUILD index 1054b08..f164fca 100644 --- a/src/tools/ak/mindex/BUILD +++ b/src/tools/ak/mindex/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") # Description: # Package for mindex module package(default_visibility = ["//visibility:public"]) diff --git a/src/tools/ak/nativelib/BUILD b/src/tools/ak/nativelib/BUILD index fff7c01..02dfd3d 100644 --- a/src/tools/ak/nativelib/BUILD +++ b/src/tools/ak/nativelib/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") # Description: # Package for nativelib module package(default_visibility = ["//visibility:public"]) diff --git a/src/tools/ak/nativelib/testdata/BUILD b/src/tools/ak/nativelib/testdata/BUILD index 2fc9b36..1887663 100644 --- a/src/tools/ak/nativelib/testdata/BUILD +++ b/src/tools/ak/nativelib/testdata/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//:def.bzl", "go_binary", "go_library", "go_test") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") # Creates test data for testing the nativelib action. package(default_visibility = ["//src/tools/ak/nativelib:__subpackages__"]) -- cgit v1.2.3 From 6af637d78b673d4fde830e6b440e47b6c0ddb39c Mon Sep 17 00:00:00 2001 From: Tim Peut Date: Tue, 27 Sep 2022 17:18:31 -0700 Subject: Clean up fully-enabled lint ACL and calls. PiperOrigin-RevId: 477314061 Change-Id: I5ff6e0081973f4ea850d1382fc89ccb4356ff97c --- rules/acls.bzl | 7 ------- 1 file changed, 7 deletions(-) diff --git a/rules/acls.bzl b/rules/acls.bzl index 4ba44f6..897b754 100644 --- a/rules/acls.bzl +++ b/rules/acls.bzl @@ -44,7 +44,6 @@ load("//rules/acls:android_binary_starlark_javac.bzl", "ANDROID_BINARY_STARLARK_ load("//rules/acls:android_feature_splits_dogfood.bzl", "ANDROID_FEATURE_SPLITS_DOGFOOD") load("//rules/acls:android_library_resources_without_srcs.bzl", "ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS", "ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS") load("//rules/acls:android_library_starlark_resource_outputs.bzl", "ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK", "ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT") -load("//rules/acls:android_lint_checks_rollout.bzl", "ANDROID_LINT_CHECKS_FALLBACK", "ANDROID_LINT_CHECKS_ROLLOUT") load("//rules/acls:android_lint_rollout.bzl", "ANDROID_LINT_FALLBACK", "ANDROID_LINT_ROLLOUT") load("//rules/acls:lint_registry_rollout.bzl", "LINT_REGISTRY_FALLBACK", "LINT_REGISTRY_ROLLOUT") load("//rules/acls:android_build_stamping_rollout.bzl", "ANDROID_BUILD_STAMPING_FALLBACK", "ANDROID_BUILD_STAMPING_ROLLOUT") @@ -106,9 +105,6 @@ def _in_android_binary_starlark_javac(fqn): def _in_android_feature_splits_dogfood(fqn): return matches(fqn, ANDROID_FEATURE_SPLITS_DOGFOOD_DICT) -def _in_android_lint_checks_rollout(fqn): - return not matches(fqn, ANDROID_LINT_CHECKS_FALLBACK_DICT) and matches(fqn, ANDROID_LINT_CHECKS_ROLLOUT_DICT) - def _in_android_lint_rollout(fqn): return not matches(fqn, ANDROID_LINT_FALLBACK_DICT) and matches(fqn, ANDROID_LINT_ROLLOUT_DICT) @@ -226,8 +222,6 @@ ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_DICT = make_dict(ANDROID_LIBRARY_RESOURCE ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS_DICT = make_dict(ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS) ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK_DICT = make_dict(ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK) ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT_DICT = make_dict(ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT) -ANDROID_LINT_CHECKS_FALLBACK_DICT = make_dict(ANDROID_LINT_CHECKS_FALLBACK) -ANDROID_LINT_CHECKS_ROLLOUT_DICT = make_dict(ANDROID_LINT_CHECKS_ROLLOUT) ANDROID_LINT_FALLBACK_DICT = make_dict(ANDROID_LINT_FALLBACK) ANDROID_LINT_ROLLOUT_DICT = make_dict(ANDROID_LINT_ROLLOUT) ANDROID_RULES_WITH_KT_ROLLOUT_DICT = make_dict(ANDROID_RULES_WITH_KT_ROLLOUT) @@ -327,7 +321,6 @@ acls = struct( in_android_library_starlark_resource_outputs_rollout = _in_android_library_starlark_resource_outputs_rollout, in_android_library_resources_without_srcs = _in_android_library_resources_without_srcs, in_android_library_resources_without_srcs_generator_functions = _in_android_library_resources_without_srcs_generator_functions, - in_android_lint_checks_rollout = _in_android_lint_checks_rollout, in_android_lint_rollout = _in_android_lint_rollout, in_lint_registry_rollout = _in_lint_registry_rollout, in_android_build_stamping_rollout = _in_android_build_stamping_rollout, -- cgit v1.2.3 From 20c9b3c7dfcfd8b88b8fe95a2d47b30929cafad7 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Wed, 28 Sep 2022 07:09:42 -0700 Subject: Internal change; simplify code export process PiperOrigin-RevId: 477443975 Change-Id: Ib17b12baf6acdf88ea3bcc13401675329851ec8c --- src/tools/ak/BUILD | 54 +++++++++++++++++++++++++++++++++++ src/tools/ak/compile/BUILD | 4 ++- src/tools/ak/dex/BUILD | 3 ++ src/tools/ak/link/BUILD | 3 ++ src/tools/ak/liteparse/BUILD | 4 ++- src/tools/ak/manifest/BUILD | 3 ++ src/tools/ak/mindex/BUILD | 3 ++ src/tools/ak/nativelib/BUILD | 4 ++- src/tools/ak/nativelib/testdata/BUILD | 4 ++- 9 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 src/tools/ak/BUILD diff --git a/src/tools/ak/BUILD b/src/tools/ak/BUILD new file mode 100644 index 0000000..b1b0546 --- /dev/null +++ b/src/tools/ak/BUILD @@ -0,0 +1,54 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +# Description: +# Top level package for ak, a "busybox" for various minor build-related tools. +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +go_binary( + name = "ak", + srcs = ["ak.go"], + deps = [ + ":types", + "//src/common/golang:flagfile", + "//src/tools/ak/bucketize", + "//src/tools/ak/compile", + "//src/tools/ak/dex", + "//src/tools/ak/extractaar", + "//src/tools/ak/finalrjar", + "//src/tools/ak/generatemanifest", + "//src/tools/ak/link", + "//src/tools/ak/liteparse", + "//src/tools/ak/manifest", + "//src/tools/ak/mindex", + "//src/tools/ak/nativelib", + "//src/tools/ak/patch", + "//src/tools/ak/repack", + "//src/tools/ak/rjar", + "//src/tools/ak/rstub", + "//src/tools/ak/shellapk", + ], +) + +go_library( + name = "types", + srcs = ["types.go"], + importpath = "github.com/bazelbuild/rules_android/src/tools/ak/types", +) + +go_library( + name = "akhelper", + srcs = ["akhelper.go"], + importpath = "github.com/bazelbuild/rules_android/src/tools/ak/akhelper", +) + +go_library( + name = "manifestutils", + srcs = ["manifestutils.go"], + importpath = "github.com/bazelbuild/rules_android/src/tools/ak/manifestutils", + deps = [ + "//src/common/golang:xml2", + ], +) diff --git a/src/tools/ak/compile/BUILD b/src/tools/ak/compile/BUILD index bcb5c20..7e9e3bb 100644 --- a/src/tools/ak/compile/BUILD +++ b/src/tools/ak/compile/BUILD @@ -1,4 +1,6 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + # Description: # Package for compile module package(default_visibility = ["//visibility:public"]) @@ -19,6 +21,7 @@ go_library( srcs = [ "compile.go", ], + importpath = "github.com/bazelbuild/rules_android/src/tools/ak/compile/compile", deps = [ "//src/common/golang:ziputils", "//src/tools/ak:types", @@ -31,5 +34,4 @@ go_test( srcs = [ "compile_test.go", ], - library = ":compile", ) diff --git a/src/tools/ak/dex/BUILD b/src/tools/ak/dex/BUILD index deb318f..ab27858 100644 --- a/src/tools/ak/dex/BUILD +++ b/src/tools/ak/dex/BUILD @@ -1,4 +1,6 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + # Package for dex compilation module package(default_visibility = ["//visibility:public"]) @@ -16,6 +18,7 @@ go_binary( go_library( name = "dex", srcs = ["dex.go"], + importpath = "github.com/bazelbuild/rules_android/src/tools/ak/dex/dex", deps = [ "//src/common/golang:flags", "//src/common/golang:shard", diff --git a/src/tools/ak/link/BUILD b/src/tools/ak/link/BUILD index aac845c..2b8cc1e 100644 --- a/src/tools/ak/link/BUILD +++ b/src/tools/ak/link/BUILD @@ -1,4 +1,6 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + # Description: # Package for link module package(default_visibility = ["//visibility:public"]) @@ -19,6 +21,7 @@ go_library( srcs = [ "link.go", ], + importpath = "github.com/bazelbuild/rules_android/src/tools/ak/link/link", deps = [ "//src/common/golang:flags", "//src/common/golang:walk", diff --git a/src/tools/ak/liteparse/BUILD b/src/tools/ak/liteparse/BUILD index 1e89f1f..d70bdf2 100644 --- a/src/tools/ak/liteparse/BUILD +++ b/src/tools/ak/liteparse/BUILD @@ -1,4 +1,6 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + # Description: # Package for parse module package(default_visibility = ["//visibility:public"]) @@ -12,6 +14,7 @@ go_library( "non_values_parse.go", "values_parse.go", ], + importpath = "github.com/bazelbuild/rules_android/src/tools/ak/liteparse/liteparse", deps = [ "//src/common/golang:flags", "//src/common/golang:walk", @@ -34,7 +37,6 @@ go_test( "values_parse_test.go", ], data = glob(["testdata/**"]), - library = ":liteparse", deps = [ "//src/tools/ak/res", "//src/tools/ak/res/proto:res_data_go_proto", diff --git a/src/tools/ak/manifest/BUILD b/src/tools/ak/manifest/BUILD index 48cb1a4..5a80c5b 100644 --- a/src/tools/ak/manifest/BUILD +++ b/src/tools/ak/manifest/BUILD @@ -1,4 +1,6 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + # Package for manifest compilation module package(default_visibility = ["//visibility:public"]) @@ -18,6 +20,7 @@ go_library( srcs = [ "manifest.go", ], + importpath = "github.com/bazelbuild/rules_android/src/tools/ak/manifest/manifest", deps = [ "//src/common/golang:flags", "//src/tools/ak:manifestutils", diff --git a/src/tools/ak/mindex/BUILD b/src/tools/ak/mindex/BUILD index f164fca..f124b6f 100644 --- a/src/tools/ak/mindex/BUILD +++ b/src/tools/ak/mindex/BUILD @@ -1,4 +1,6 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + # Description: # Package for mindex module package(default_visibility = ["//visibility:public"]) @@ -17,5 +19,6 @@ go_binary( go_library( name = "mindex", srcs = ["mindex.go"], + importpath = "github.com/bazelbuild/rules_android/src/tools/ak/mindex/mindex", deps = ["//src/tools/ak:types"], ) diff --git a/src/tools/ak/nativelib/BUILD b/src/tools/ak/nativelib/BUILD index 02dfd3d..733ebf4 100644 --- a/src/tools/ak/nativelib/BUILD +++ b/src/tools/ak/nativelib/BUILD @@ -1,4 +1,6 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + # Description: # Package for nativelib module package(default_visibility = ["//visibility:public"]) @@ -8,6 +10,7 @@ licenses(["notice"]) go_library( name = "nativelib", srcs = ["nativelib.go"], + importpath = "github.com/bazelbuild/rules_android/src/tools/ak/nativelib/nativelib", deps = [ "//src/common/golang:fileutils", "//src/common/golang:flags", @@ -32,5 +35,4 @@ go_test( data = [ "//src/tools/ak/nativelib/testdata:dummy_so", ], - library = ":nativelib", ) diff --git a/src/tools/ak/nativelib/testdata/BUILD b/src/tools/ak/nativelib/testdata/BUILD index 1887663..a0615ed0 100644 --- a/src/tools/ak/nativelib/testdata/BUILD +++ b/src/tools/ak/nativelib/testdata/BUILD @@ -1,6 +1,8 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") # Creates test data for testing the nativelib action. +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + package(default_visibility = ["//src/tools/ak/nativelib:__subpackages__"]) licenses(["notice"]) -- cgit v1.2.3 From 2f2b4e171d29cfe5da1eec1e3465a2d6a3710300 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Fri, 30 Sep 2022 11:02:26 -0700 Subject: Release rules_android sample app to OSS PiperOrigin-RevId: 478033728 Change-Id: I5c26d78edab58324ae1496d2ffd42d8b85a40b5e --- WORKSPACE | 24 +++++++++++++ examples/basicapp/WORKSPACE | 3 +- src/java/com/example/sampleapp/AndroidManifest.xml | 18 ++++++++++ src/java/com/example/sampleapp/BUILD | 31 ++++++++++++++++ src/java/com/example/sampleapp/SampleApp.java | 41 ++++++++++++++++++++++ src/java/com/example/sampleapp/native.c | 7 ++++ .../com/example/sampleapp/res/values/strings.xml | 4 +++ 7 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 src/java/com/example/sampleapp/AndroidManifest.xml create mode 100644 src/java/com/example/sampleapp/BUILD create mode 100644 src/java/com/example/sampleapp/SampleApp.java create mode 100644 src/java/com/example/sampleapp/native.c create mode 100644 src/java/com/example/sampleapp/res/values/strings.xml diff --git a/WORKSPACE b/WORKSPACE index 0c59e27..cee147f 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -3,6 +3,26 @@ workspace(name = "build_bazel_rules_android") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") +maybe( + http_archive, + name = "rules_jvm_external", + strip_prefix = "rules_jvm_external-fa73b1a8e4846cee88240d0019b8f80d39feb1c3", + sha256 = "7e13e48b50f9505e8a99cc5a16c557cbe826e9b68d733050cd1e318d69f94bb5", + url = "https://github.com/bazelbuild/rules_jvm_external/archive/fa73b1a8e4846cee88240d0019b8f80d39feb1c3.zip", +) + +load("defs.bzl", "rules_android_workspace") + +maybe( + android_sdk_repository, + name = "androidsdk", +) + +maybe( + android_ndk_repository, + name = "androidndk", +) + maybe( http_archive, name = "bazel_skylib", @@ -12,6 +32,8 @@ maybe( "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz", ], ) +register_toolchains("//toolchains/android:all") +register_toolchains("//toolchains/android_sdk:all") register_toolchains("//toolchains/emulator:all") maybe( @@ -57,3 +79,5 @@ go_register_toolchains(version = "1.18.3") gazelle_dependencies() # gazelle:repository go_repository name=org_golang_x_xerrors importpath=golang.org/x/xerrors +rules_android_workspace() + diff --git a/examples/basicapp/WORKSPACE b/examples/basicapp/WORKSPACE index 1748662..eebeac0 100644 --- a/examples/basicapp/WORKSPACE +++ b/examples/basicapp/WORKSPACE @@ -9,7 +9,8 @@ android_ndk_repository( name = "androidndk", ) -http_archive( +maybe( + http_archive, name = "rules_jvm_external", strip_prefix = "rules_jvm_external-fa73b1a8e4846cee88240d0019b8f80d39feb1c3", sha256 = "7e13e48b50f9505e8a99cc5a16c557cbe826e9b68d733050cd1e318d69f94bb5", diff --git a/src/java/com/example/sampleapp/AndroidManifest.xml b/src/java/com/example/sampleapp/AndroidManifest.xml new file mode 100644 index 0000000..b476bf3 --- /dev/null +++ b/src/java/com/example/sampleapp/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/src/java/com/example/sampleapp/BUILD b/src/java/com/example/sampleapp/BUILD new file mode 100644 index 0000000..db56adf --- /dev/null +++ b/src/java/com/example/sampleapp/BUILD @@ -0,0 +1,31 @@ +# Sample app to demonstrate proper rule structuring and mobile-install usage. + +load("//rules:rules.bzl", "android_binary", "android_library") + +package(default_visibility = ["//visibility:private"]) + +android_binary( + name = "sampleapp", + manifest = "AndroidManifest.xml", + multidex = "native", + deps = [ + ":lib", + ], +) + +android_library( + name = "lib", + srcs = glob(["*.java"]), + manifest = "AndroidManifest.xml", + resource_files = glob(["res/**"]), + deps = [ + ":native", + "@androidsdk//com.android.support:support-v4-25.2.0", + "@androidsdk//com.android.support:appcompat-v7-25.0.0", + ], +) + +cc_library( + name = "native", + srcs = ["native.c"], +) diff --git a/src/java/com/example/sampleapp/SampleApp.java b/src/java/com/example/sampleapp/SampleApp.java new file mode 100644 index 0000000..9564755 --- /dev/null +++ b/src/java/com/example/sampleapp/SampleApp.java @@ -0,0 +1,41 @@ +/* + * Copyright 2018 The Bazel Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.sampleapp; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.TextView; + +/** + * Minimal sample app to demonstrate mobile-install and rule best practices. + */ +public class SampleApp extends Activity { + + @Override + public void onCreate(Bundle state) { + super.onCreate(state); + + TextView view = new TextView(this); + view.setText(getString()); + setContentView(view); + } + + public native String getString(); + + static { + System.loadLibrary("sample"); + } +} diff --git a/src/java/com/example/sampleapp/native.c b/src/java/com/example/sampleapp/native.c new file mode 100644 index 0000000..d221826 --- /dev/null +++ b/src/java/com/example/sampleapp/native.c @@ -0,0 +1,7 @@ +#include +#include + +jstring +Java_com_example_sampleapp_SampleApp_getString(JNIEnv* env, jobject thiz) { + return (*env)->NewStringUTF(env, "Native String!"); +} diff --git a/src/java/com/example/sampleapp/res/values/strings.xml b/src/java/com/example/sampleapp/res/values/strings.xml new file mode 100644 index 0000000..28193f2 --- /dev/null +++ b/src/java/com/example/sampleapp/res/values/strings.xml @@ -0,0 +1,4 @@ + + + SampleApp + -- cgit v1.2.3 From cb1e62c7cc94bb7a1504192b53df01c58a4c5802 Mon Sep 17 00:00:00 2001 From: Joseph Park Date: Mon, 3 Oct 2022 15:29:41 -0700 Subject: Rollout for enforcing min SDK PiperOrigin-RevId: 478621295 Change-Id: I8357c95ac0b289e0dc67681eca3356861e683c58 --- rules/acls/enforce_min_sdk_floor_rollout.bzl | 2 +- src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/rules/acls/enforce_min_sdk_floor_rollout.bzl b/rules/acls/enforce_min_sdk_floor_rollout.bzl index 29c18ab..94643eb 100644 --- a/rules/acls/enforce_min_sdk_floor_rollout.bzl +++ b/rules/acls/enforce_min_sdk_floor_rollout.bzl @@ -15,8 +15,8 @@ """Rollout list for enabling enforce min SDK floor.""" ENFORCE_MIN_SDK_FLOOR_ROLLOUT = [ + "//:__subpackages__", ] ENFORCE_MIN_SDK_FLOOR_FALLBACK = [ - "//:__subpackages__", ] diff --git a/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py b/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py index 1a265f4..3059b98 100644 --- a/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py +++ b/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py @@ -23,7 +23,6 @@ import os import sys import xml.etree.ElementTree as ET -import google3 from absl import app from absl import flags -- cgit v1.2.3 From 6f6f4c45a060be13aae64a5386326ae30af54c99 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Tue, 4 Oct 2022 06:00:39 -0700 Subject: Release rules_android/src/common/golang to OSS PiperOrigin-RevId: 478762708 Change-Id: Id47b3bcec552e688b26826b83090c344cd2631c7 --- WORKSPACE | 7 + kokoro/presubmit/kokoro_presubmit.sh | 24 ++- src/common/golang/BUILD | 96 +++++++++++ src/common/golang/fileutils.go | 35 ++++ src/common/golang/flagfile.go | 118 +++++++++++++ src/common/golang/flagfile_test.go | 128 ++++++++++++++ src/common/golang/flags.go | 42 +++++ src/common/golang/ini.go | 86 ++++++++++ src/common/golang/ini_test.go | 113 ++++++++++++ src/common/golang/marshal.go | 322 +++++++++++++++++++++++++++++++++++ src/common/golang/marshal_test.go | 149 ++++++++++++++++ src/common/golang/pprint.go | 34 ++++ src/common/golang/shard.go | 90 ++++++++++ src/common/golang/shard_test.go | 98 +++++++++++ src/common/golang/walk.go | 51 ++++++ src/common/golang/zipshard_test.go | 193 +++++++++++++++++++++ src/common/golang/ziputils.go | 193 +++++++++++++++++++++ src/tools/ak/compile/BUILD | 1 + src/tools/ak/liteparse/BUILD | 1 + src/tools/ak/nativelib/BUILD | 1 + 20 files changed, 1773 insertions(+), 9 deletions(-) create mode 100644 src/common/golang/BUILD create mode 100644 src/common/golang/fileutils.go create mode 100644 src/common/golang/flagfile.go create mode 100644 src/common/golang/flagfile_test.go create mode 100644 src/common/golang/flags.go create mode 100644 src/common/golang/ini.go create mode 100644 src/common/golang/ini_test.go create mode 100644 src/common/golang/marshal.go create mode 100644 src/common/golang/marshal_test.go create mode 100644 src/common/golang/pprint.go create mode 100644 src/common/golang/shard.go create mode 100644 src/common/golang/shard_test.go create mode 100644 src/common/golang/walk.go create mode 100644 src/common/golang/zipshard_test.go create mode 100644 src/common/golang/ziputils.go diff --git a/WORKSPACE b/WORKSPACE index cee147f..a588f59 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -73,6 +73,13 @@ go_repository( version = "v0.5.9", ) +go_repository( + name = "org_golang_x_sync", + importpath = "golang.org/x/sync", + sum = "h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=", + version = "v0.0.0-20210220032951-036812b2e83c", +) + go_rules_dependencies() go_register_toolchains(version = "1.18.3") diff --git a/kokoro/presubmit/kokoro_presubmit.sh b/kokoro/presubmit/kokoro_presubmit.sh index 762e7b0..2b2214e 100644 --- a/kokoro/presubmit/kokoro_presubmit.sh +++ b/kokoro/presubmit/kokoro_presubmit.sh @@ -46,9 +46,6 @@ yes | tools/bin/sdkmanager --licenses &>/dev/null # ANDROID_HOME is already in the environment. export ANDROID_NDK_HOME="/opt/android-ndk-r16b" -# Go to basic app workspace in the source tree -cd "${KOKORO_ARTIFACTS_DIR}/git/rules_android/examples/basicapp" - # Create a tmpfs in the sandbox at "/tmp/hsperfdata_$USERNAME" to avoid the # problems described in https://github.com/bazelbuild/bazel/issues/3236 # Basically, the JVM creates a file at /tmp/hsperfdata_$USERNAME/$PID, but @@ -59,9 +56,18 @@ cd "${KOKORO_ARTIFACTS_DIR}/git/rules_android/examples/basicapp" hsperfdata_dir="/tmp/hsperfdata_$(whoami)_rules_android" mkdir "$hsperfdata_dir" -"$bazel" build \ - --sandbox_tmpfs_path="$hsperfdata_dir" \ - --verbose_failures \ - --experimental_google_legacy_api \ - --experimental_enable_android_migration_apis \ - //java/com/basicapp:basic_app +COMMON_ARGS=( + "--sandbox_tmpfs_path=$hsperfdata_dir" + "--verbose_failures" + "--experimental_google_legacy_api" + "--experimental_enable_android_migration_apis" +) + +# Go to rules_android workspace and run relevant tests. +cd "${KOKORO_ARTIFACTS_DIR}/git/rules_android" +"$bazel" test "${COMMON_ARGS[@]}" //src/common/golang/... + +# Go to basic app workspace in the source tree +cd "${KOKORO_ARTIFACTS_DIR}/git/rules_android/examples/basicapp" +"$bazel" build "${COMMON_ARGS[@]}" //java/com/basicapp:basic_app + diff --git a/src/common/golang/BUILD b/src/common/golang/BUILD new file mode 100644 index 0000000..016762d --- /dev/null +++ b/src/common/golang/BUILD @@ -0,0 +1,96 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +# Description: +# Common libraries and utilities. +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +go_library( + name = "xml2", + srcs = ["marshal.go"], + importpath = "github.com/bazelbuild/rules_android/src/common/golang/xml2", +) + +go_test( + name = "xml2_test", + size = "small", + srcs = [ + "marshal_test.go", + ], + embed = [":xml2"], +) + +go_library( + name = "shard", + srcs = ["shard.go"], + importpath = "github.com/bazelbuild/rules_android/src/common/golang/shard", +) + +go_test( + name = "shard_test", + size = "small", + srcs = [ + "shard_test.go", + "zipshard_test.go", + ], + embed = [":shard"], +) + +go_library( + name = "walk", + srcs = ["walk.go"], + importpath = "github.com/bazelbuild/rules_android/src/common/golang/walk", +) + +go_library( + name = "ziputils", + srcs = ["ziputils.go"], + importpath = "github.com/bazelbuild/rules_android/src/common/golang/ziputils", + deps = ["@org_golang_x_sync//errgroup"], +) + +go_library( + name = "fileutils", + srcs = ["fileutils.go"], + importpath = "github.com/bazelbuild/rules_android/src/common/golang/fileutils", +) + +go_library( + name = "flags", + srcs = ["flags.go"], + importpath = "github.com/bazelbuild/rules_android/src/common/golang/flags", +) + +go_test( + name = "flagfile_test", + size = "small", + srcs = ["flagfile_test.go"], + embed = [":flagfile"], +) + +go_library( + name = "ini", + srcs = ["ini.go"], + importpath = "github.com/bazelbuild/rules_android/src/common/golang/ini", +) + +go_test( + name = "ini_test", + size = "small", + srcs = ["ini_test.go"], + embed = [":ini"], +) + +go_library( + name = "pprint", + srcs = ["pprint.go"], + importpath = "github.com/bazelbuild/rules_android/src/common/golang/pprint", +) + +go_library( + name = "flagfile", + srcs = ["flagfile.go"], + importpath = "github.com/bazelbuild/rules_android/src/common/golang/flagfile", +) diff --git a/src/common/golang/fileutils.go b/src/common/golang/fileutils.go new file mode 100644 index 0000000..31ecc24 --- /dev/null +++ b/src/common/golang/fileutils.go @@ -0,0 +1,35 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package fileutils provides utility functions to work with files. +package fileutils + +import ( + "io" + "os" +) + +// Copy will copy a file. +func Copy(in, out string) error { + inF, err := os.Open(in) + if err != nil { + return err + } + outF, err := os.Create(out) + if err != nil { + return err + } + _, err = io.Copy(outF, inF) + return err +} diff --git a/src/common/golang/flagfile.go b/src/common/golang/flagfile.go new file mode 100644 index 0000000..805068e --- /dev/null +++ b/src/common/golang/flagfile.go @@ -0,0 +1,118 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package flagfile installs a -flagfile command line flag. +// This package is only imported for the side effect of installing the flag +package flagfile + +import ( + "bufio" + "flag" + "fmt" + "io" + "os" + "strings" +) + +type flagFile string + +func (f *flagFile) String() string { + return string(*f) +} + +func (f *flagFile) Get() interface{} { + return string(*f) +} + +func (f *flagFile) Set(fn string) error { + file, err := os.Open(fn) + if err != nil { + return fmt.Errorf("error parsing flagfile %s: %v", fn, err) + } + defer file.Close() + + fMap, err := parseFlags(bufio.NewReader(file)) + if err != nil { + return err + } + for k, v := range fMap { + flag.Set(k, v) + } + return nil +} + +// parseFlags parses the contents is a naive flag file parser. +func parseFlags(r *bufio.Reader) (map[string]string, error) { + fMap := make(map[string]string) + eof := false + for !eof { + line, err := r.ReadString('\n') + if err != nil && err != io.EOF { + return nil, err + } + if err == io.EOF { + eof = true + } + line = strings.TrimSpace(line) + if line == "" { + continue + } + // When Bazel is used to create flag files, it may create entries that are wrapped within + // quotations '--a=b'. Verify that it is balanced and strip first and last quotation. + if strings.HasPrefix(line, "'") || strings.HasPrefix(line, "\"") { + if !strings.HasSuffix(line, line[:1]) { + return nil, fmt.Errorf("error parsing flags, found unbalanced quotation marks around flag entry: %s", line) + } + line = line[1 : len(line)-1] + } + // Check that the flag has at least 1 "-" but no more than 2 ("-a" or "--a"). + if !strings.HasPrefix(line, "-") || strings.HasPrefix(line, "---") { + return nil, fmt.Errorf("error parsing flags, expected flag start definition ('-' or '--') but, got: %s", line) + } + split := strings.SplitN(strings.TrimLeft(line, "-"), "=", 2) + k := split[0] + if len(split) == 2 { + fMap[k] = split[1] + continue + } + v, err := parseFlagValue(r) + if err != nil { + return nil, fmt.Errorf("error parsing flag value, got: %v", err) + } + fMap[k] = v + } + return fMap, nil +} + +func parseFlagValue(r *bufio.Reader) (string, error) { + pBytes, err := r.Peek(2) + if err != nil && err != io.EOF { + return "", err + } + peeked := string(pBytes) + // If the next line starts with "-", "'-" or '"-' assume it is the beginning of a new flag definition. + if strings.HasPrefix(peeked, "-") || peeked == "'-" || peeked == "\"-" { + return "", nil + } + // Next line contains the flag value. + line, err := r.ReadString('\n') + if err != nil && err != io.EOF { + return "", err + } + return strings.TrimSpace(line), nil +} + +func init() { + flag.Var(new(flagFile), "flagfile", "Path to flagfile containing flag values, --key=val on each line") +} diff --git a/src/common/golang/flagfile_test.go b/src/common/golang/flagfile_test.go new file mode 100644 index 0000000..7f87c57 --- /dev/null +++ b/src/common/golang/flagfile_test.go @@ -0,0 +1,128 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Unit test for the flagfile module. +package flagfile + +import ( + "bufio" + "reflect" + "strings" + "testing" +) + +func TestParseFlags(t *testing.T) { + tcs := []struct { + name string + in string + want map[string]string + wantErr string + }{ + { + name: "SingleLineFlagDefinitions", + in: ` +--a=b +'--1=2' +-foo=bar +--enable +"--baz=qux=quux" +`, + want: map[string]string{ + "a": "b", + "1": "2", + "foo": "bar", + "enable": "", + "baz": "qux=quux", + }, + }, + { + name: "MultiLineFlagDefinitions", + in: ` +--a +b +--1 +2 +-foo +bar +--enable +--baz +qux=quux +`, + want: map[string]string{ + "a": "b", + "1": "2", + "foo": "bar", + "enable": "", + "baz": "qux=quux", + }, + }, + { + name: "MixedMultiSingleLineFlagDefinitions", + in: ` +--a +b +"-1=2" +-foo +bar +--enable +'--baz=--qux=quux' +`, + want: map[string]string{ + "a": "b", + "1": "2", + "foo": "bar", + "enable": "", + "baz": "--qux=quux", + }, + }, + { + name: "NoFlags", + in: "", + want: map[string]string{}, + }, + { + name: "MalformedFlagMissingDash", + in: "a=b", + wantErr: "expected flag start definition ('-' or '--')", + }, + { + name: "MalformedFlagTooManyDashes", + in: "---a=b", + wantErr: "expected flag start definition ('-' or '--')", + }, + { + name: "UnbalancedQuotationsAroundFlag", + in: "'--a=b", + wantErr: "found unbalanced quotation marks around flag entry", + }, + } + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + got, err := parseFlags(bufio.NewReader(strings.NewReader(tc.in))) + if err != nil { + if tc.wantErr != "" { + if !strings.Contains(err.Error(), tc.wantErr) { + t.Errorf("error got error: %s wanted to contain: %s", err, tc.wantErr) + } + return + } + t.Errorf("got unexpected error: %s", err) + return + } + if eq := reflect.DeepEqual(got, tc.want); !eq { + t.Errorf("error got: %v wanted: %v", got, tc.want) + } + }) + } +} diff --git a/src/common/golang/flags.go b/src/common/golang/flags.go new file mode 100644 index 0000000..be78af6 --- /dev/null +++ b/src/common/golang/flags.go @@ -0,0 +1,42 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package flags provides extensions to the built-in flag module. +package flags + +import ( + "flag" + "strings" +) + +// StringList provides a flag type that parses a,comma,separated,string into a []string. +type StringList []string + +func (i *StringList) String() string { + return strings.Join([]string(*i), ",") +} + +// Set sets the flag value. +func (i *StringList) Set(v string) error { + *i = strings.Split(v, ",") + return nil +} + +// NewStringList creates a new StringList flag +// var someFlag = flags.NewStringList("some_name", "some desc") +func NewStringList(name, help string) *StringList { + var r StringList + flag.Var(&r, name, help) + return &r +} diff --git a/src/common/golang/ini.go b/src/common/golang/ini.go new file mode 100644 index 0000000..92cffa5 --- /dev/null +++ b/src/common/golang/ini.go @@ -0,0 +1,86 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package ini provides utility functions to read and write ini files. +package ini + +import ( + "fmt" + "io" + "io/ioutil" + "log" + "os" + "sort" + "strings" +) + +func parse(in string) map[string]string { + m := make(map[string]string) + lines := strings.Split(in, "\n") + for i, l := range lines { + l = strings.TrimSpace(l) + if len(l) == 0 { + // Skip empty line + continue + } + if strings.HasPrefix(l, ";") || strings.HasPrefix(l, "#") { + // Skip comment + continue + } + kv := strings.SplitN(l, "=", 2) + if len(kv) < 2 { + log.Printf("Invalid line in ini file at line:%v %q\n", i, l) + // Skip invalid line + continue + } + k := strings.TrimSpace(kv[0]) + v := strings.TrimSpace(kv[1]) + if ov, ok := m[k]; ok { + log.Printf("Overwrite \"%s=%s\", duplicate found at line:%v %q\n", k, ov, i, l) + } + m[k] = v + } + return m +} + +func write(f io.Writer, m map[string]string) { + var keys []string + for k := range m { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + fmt.Fprintf(f, "%s=%s\n", k, m[k]) + } +} + +// Read reads an ini file. +func Read(n string) (map[string]string, error) { + c, err := ioutil.ReadFile(n) + if err != nil { + return nil, err + } + return parse(string(c)), nil +} + +// Write writes an ini file. +func Write(n string, m map[string]string) error { + f, err := os.Create(n) + if err != nil { + return err + } + defer f.Close() + write(f, m) + return nil +} diff --git a/src/common/golang/ini_test.go b/src/common/golang/ini_test.go new file mode 100644 index 0000000..a712e90 --- /dev/null +++ b/src/common/golang/ini_test.go @@ -0,0 +1,113 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package ini + +import ( + "bytes" + "reflect" + "strings" + "testing" +) + +func TestParseFunc(t *testing.T) { + tests := []struct { + name string + in string + want map[string]string + }{ + { + name: "ini_single_line", + in: "test=abc", + want: map[string]string{"test": "abc"}, + }, + { + name: "ini_multi_line", + in: `key=data +key2=more data`, + want: map[string]string{"key": "data", "key2": "more data"}, + }, + { + name: "ini_with_comment", + in: `key=data +;key2=irrelevant data +#key3=more irrelevant data`, + want: map[string]string{"key": "data"}, + }, + { + name: "ini_with_whitespace", + in: `key = data +another_key = The data +yet_another_key = more data`, + want: map[string]string{"key": "data", "another_key": "The data", "yet_another_key": "more data"}, + }, + { + name: "ini_with_empty_data", + in: `key=data +key2= +key3=more data`, + want: map[string]string{"key": "data", "key2": "", "key3": "more data"}, + }, + { + name: "invalid_ini", + in: `key=data +invalid line +key2=The data`, + want: map[string]string{"key": "data", "key2": "The data"}, + }, + { + name: "ini_with_duplicate", + in: `key=data +key=duplicate`, + want: map[string]string{"key": "duplicate"}, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + iniOut := parse(test.in) + if eq := reflect.DeepEqual(iniOut, test.want); !eq { + t.Errorf("Parsing ini failed for: %q got: %v wanted: %v", test.in, iniOut, test.want) + } + }) + } +} + +func TestWriteFunc(t *testing.T) { + tests := []struct { + name string + in map[string]string + want string + }{ + { + name: "ini_single_line", + in: map[string]string{"test": "abc"}, + want: "test=abc\n", + }, + { + name: "ini_multi_line", + in: map[string]string{"key": "data", "key2": "more data"}, + want: `key=data +key2=more data +`, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + b := new(bytes.Buffer) + write(b, test.in) + if strings.Compare(b.String(), test.want) != 0 { + t.Errorf("Writing ini failed for: %q got: %v wanted: %v", test.in, b.String(), test.want) + } + }) + } +} diff --git a/src/common/golang/marshal.go b/src/common/golang/marshal.go new file mode 100644 index 0000000..76b0114 --- /dev/null +++ b/src/common/golang/marshal.go @@ -0,0 +1,322 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package xml2 provides drop-in replacement functionality for encoding/xml. +// +// There are existing issues with the encoding/xml package that affect AK tools. +// +// xml2.Encoder: +// +// The current encoding/xml Encoder has several issues around xml namespacing +// that makes the output produced by it incompatible with AAPT. +// +// * Tracked here: https://golang.org/issue/7535 +// +// The xml2.Encoder.EncodeToken verifies the validity of namespaces and encodes +// them. For everything else, xml2.Encoder will fallback to the xml.Encoder. +package xml2 + +import ( + "bytes" + "encoding/xml" + "fmt" + "io" + "log" +) + +const xmlNS = "xmlns" + +// Encoder is an xml encoder which behaves much like the encoding/xml Encoder. +type Encoder struct { + *xml.Encoder + p printer + prefixURI map[string]string + state []state + uriPrefix *uriPrefixMap +} + +// ChildEncoder returns an encoder whose state is copied the given parent Encoder and writes to w. +func ChildEncoder(w io.Writer, parent *Encoder) *Encoder { + e := NewEncoder(w) + for k, v := range parent.prefixURI { + e.prefixURI[k] = v + } + for k, v := range parent.uriPrefix.up { + e.uriPrefix.up[k] = make([]string, len(v)) + copy(e.uriPrefix.up[k], v) + } + return e +} + +// NewEncoder returns a new encoder that writes to w. +func NewEncoder(w io.Writer) *Encoder { + e := &Encoder{ + Encoder: xml.NewEncoder(w), + p: printer{Writer: w}, + prefixURI: make(map[string]string), + uriPrefix: &uriPrefixMap{up: make(map[string][]string)}, + } + return e +} + +// EncodeToken behaves almost the same as encoding/xml.Encoder.EncodeToken +// but deals with StartElement and EndElement differently. +func (enc *Encoder) EncodeToken(t xml.Token) error { + switch t := t.(type) { + case xml.StartElement: + enc.Encoder.Flush() // Need to flush the wrapped encoder before we write. + if err := enc.writeStart(&t); err != nil { + return err + } + case xml.EndElement: + enc.Encoder.Flush() // Need to flush the wrapped encoder before we write. + if err := enc.writeEnd(t.Name); err != nil { + return err + } + default: + // Delegate to the embedded encoder for everything else. + return enc.Encoder.EncodeToken(t) + } + return nil +} + +func (enc *Encoder) writeStart(start *xml.StartElement) error { + if start.Name.Local == "" { + return fmt.Errorf("start tag with no name") + } + enc.setUpState(start) + + // Begin creating the start tag. + var st bytes.Buffer + st.WriteByte('<') + n, err := enc.translateName(start.Name) + if err != nil { + return fmt.Errorf("translating start tag name %q failed, got: %v", start.Name.Local, err) + } + st.Write(n) + for _, attr := range start.Attr { + name := attr.Name + if name.Local == "" { + continue + } + st.WriteByte(' ') + n, err := enc.translateName(attr.Name) + if err != nil { + return fmt.Errorf("translating attribute name %q failed, got: %v", start.Name.Local, err) + } + st.Write(n) + st.WriteString(`="`) + xml.EscapeText(&st, []byte(attr.Value)) + st.WriteByte('"') + } + st.WriteByte('>') + + enc.p.writeIndent(1) + enc.p.Write(st.Bytes()) + return nil +} + +func (enc *Encoder) writeEnd(name xml.Name) error { + if name.Local == "" { + return fmt.Errorf("end tag with no name") + } + n, err := enc.translateName(name) + if err != nil { + return fmt.Errorf("translating end tag name %q failed, got: %v", name.Local, err) + } + sn := enc.tearDownState() + if sn == nil || name.Local != sn.Local && name.Space != sn.Space { + return fmt.Errorf("tags are unbalanced, got: %v, wanted: %v", name, sn) + } + + // Begin creating the end tag + var et bytes.Buffer + et.WriteString("') + + enc.p.writeIndent(-1) + enc.p.Write(et.Bytes()) + return nil +} + +func (enc *Encoder) setUpState(start *xml.StartElement) { + enc.state = append(enc.state, element{n: &start.Name}) // Store start element to verify balanced close tags. + // Track attrs that affect the state of the xml (e.g. xmlns, xmlns:foo). + for _, attr := range start.Attr { + // push any xmlns type attrs as xml namespaces are valid within the tag they are declared in, and onward. + if attr.Name.Space == "xmlns" || attr.Name.Local == "xmlns" { + prefix := attr.Name.Local + if attr.Name.Local == "xmlns" { + prefix = "" // Default xml namespace is being set. + } + // Store the previous state, to be restored when exiting the tag. + enc.state = append(enc.state, xmlns{prefix: prefix, uri: enc.prefixURI[prefix]}) + enc.prefixURI[prefix] = attr.Value + enc.uriPrefix.put(attr.Value, prefix) + } + } +} + +func (enc *Encoder) tearDownState() *xml.Name { + // Unwind the state setup on start element. + for len(enc.state) > 0 { + s := enc.state[len(enc.state)-1] + enc.state = enc.state[:len(enc.state)-1] + switch s := s.(type) { + case element: + // Stop unwinding As soon as an element type is seen and verify that the + // tags are balanced + return s.n + case xmlns: + if p, ok := enc.uriPrefix.removeLast(enc.prefixURI[s.prefix]); !ok || p != s.prefix { + // Unexpected error, internal state is corrupt. + if !ok { + log.Fatalf("xmlns attribute state corrupt, uri %q does not exist", enc.prefixURI[s.prefix]) + } + log.Fatalf("xmlns attributes state corrupt, got: %q, wanted: %q", s.prefix, p) + } + if s.uri == "" { + delete(enc.prefixURI, s.prefix) + } else { + enc.prefixURI[s.prefix] = s.uri + } + } + } + return nil +} + +func (enc *Encoder) translateName(name xml.Name) ([]byte, error) { + var n bytes.Buffer + if name.Space != "" { + prefix := "" + if name.Space == xmlNS { + prefix = xmlNS + } else if ns, ok := enc.uriPrefix.getLast(name.Space); ok { + // URI Space is defined in current context, use the namespace. + prefix = ns + } else if _, ok := enc.prefixURI[name.Space]; ok { + // If URI Space is not defined in current context, there is a possibility + // that the Space is in fact a namespace prefix. If present use it. + prefix = name.Space + } else { + return nil, fmt.Errorf("unknown namespace: %s", name.Space) + } + if prefix != "" { + n.WriteString(prefix) + n.WriteByte(':') + } + } + n.WriteString(name.Local) + return n.Bytes(), nil +} + +type printer struct { + io.Writer + indent string + prefix string + depth int + indentedIn bool + putNewline bool +} + +// writeIndent is directly cribbed from encoding/xml/marshal.go to keep indentation behavior the same. +func (p *printer) writeIndent(depthDelta int) { + if len(p.prefix) == 0 && len(p.indent) == 0 { + return + } + if depthDelta < 0 { + p.depth-- + if p.indentedIn { + p.indentedIn = false + return + } + p.indentedIn = false + } + if p.putNewline { + p.Write([]byte("\n")) + } else { + p.putNewline = true + } + if len(p.prefix) > 0 { + p.Write([]byte(p.prefix)) + } + if len(p.indent) > 0 { + for i := 0; i < p.depth; i++ { + p.Write([]byte(p.indent)) + } + } + if depthDelta > 0 { + p.depth++ + p.indentedIn = true + } + +} + +// uriPrefixMap is a multimap, mapping a uri to many xml namespace prefixes. The +// difference with this and a a traditional multimap is that, you can only get +// or remove the last prefixed added. This is mainly due to the way xml decoding +// is implemented by the encoding/xml Decoder. +type uriPrefixMap struct { + up map[string][]string +} + +// getLast returns a boolean which signifies if the entry exists and the last +// prefix stored for the given uri. +func (u *uriPrefixMap) getLast(uri string) (string, bool) { + ps, ok := u.up[uri] + if !ok { + return "", ok + } + return ps[len(ps)-1], ok +} + +func (u *uriPrefixMap) put(uri, prefix string) { + if _, ok := u.up[uri]; !ok { + // Though the mapping of url-to-prefix is implemented for a multimap, in practice, + // there should never be more than a single prefix defined for any given uri within + // at any point in time in an xml file. + u.up[uri] = make([]string, 1) + } + u.up[uri] = append(u.up[uri], prefix) +} + +// removeLast a boolean which signifies if the entry exists and returns the last +// prefix removed for the given uri. If the last entry is removed the key is +// also deleted. +func (u *uriPrefixMap) removeLast(uri string) (string, bool) { + p, ok := u.getLast(uri) + if ok { + if len(u.up[uri]) > 1 { + u.up[uri] = u.up[uri][:len(u.up[uri])-1] + } else { + delete(u.up, uri) + } + } + return p, ok +} + +// state stores the state of the xml when a new start element is seen. +type state interface{} + +// xml element state entry. +type element struct { + n *xml.Name +} + +// xmlns attribute state entry. +type xmlns struct { + prefix string + uri string +} diff --git a/src/common/golang/marshal_test.go b/src/common/golang/marshal_test.go new file mode 100644 index 0000000..7e4d04b --- /dev/null +++ b/src/common/golang/marshal_test.go @@ -0,0 +1,149 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package xml2 + +import ( + "bufio" + "bytes" + "encoding/xml" + "io" + "strings" + "testing" +) + +func TestEncoderEncodeToken(t *testing.T) { + tests := []struct { + name string + in string + want string + wantErr string + }{ + { + name: "xmlnsPrefixForElement", + in: "", + want: "", + }, + { + name: "xmlnsPrefixForAttribute", + in: "", + want: "", + }, + { + name: "defaultXmlnsAttribute", + in: "", + want: "", + }, + { + // The return value of Decoder.Token() makes it + // impossible for a decode then encode of an xml file + // be isomorphic. This is mainly due to the fact that + // xml.Name.Space contains the uri, and xml.Name does + // not store the prefix. Instead, make sure that the + // behavior remains consistent. + // + // That is, the last prefix defined for the space is the + // one applied when encoding the token. + name: "multipleDefsXmlnsPrefixesSameUri", + in: ` + + + + +`, + want: ` + + + + +`, + }, + { + name: "xmlnsPrefixUsedOnElementButNotDefined", + in: "", + wantErr: "unknown namespace: foo", + }, + { + name: "xmlnsPrefixUsedOnAttrButNotDefined", + in: "", + wantErr: "unknown namespace: bar", + }, + { + name: "xmlnsPrefixUsedOutsideOfDefiningTag", + in: ` +corge +`, + wantErr: "unknown namespace: bar", + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + var b bytes.Buffer + e := NewEncoder(bufio.NewWriter(&b)) + d := xml.NewDecoder(strings.NewReader(test.in)) + for { + tkn, err := d.Token() + if err != nil { + if err == io.EOF { + break + } + t.Fatalf("Unexpected error got: %v while reading: %s", err, test.in) + } + if err := e.EncodeToken(tkn); err != nil { + if test.wantErr != "" && strings.Contains(err.Error(), test.wantErr) { + // Do nothing, error is expected. + } else { + t.Errorf("Unexpected error during encode: %v", err) + } + return + } + } + e.Flush() + if b.String() != test.want { + t.Errorf("got: <%s> expected: <%s>", b.String(), test.want) + } + }) + } +} + +func TestChildEncoder(t *testing.T) { + // Setup the parent Encoder with the namespace "bar". + d := xml.NewDecoder(strings.NewReader("Hello World")) + tkn, err := d.Token() + if err != nil { + t.Fatalf("Error occurred during decoding, got: %v", err) + } + parentEnc := NewEncoder(&bytes.Buffer{}) + if err := parentEnc.EncodeToken(tkn); err != nil { + t.Fatalf("Error occurred while the parent encoder was encoding token %q got: %v", tkn, err) + } + + // Without instantiating the Encoder as a child, the "bar" namespace will be unknown and cause an + // error to occur when trying to encode the "bar" namespaced element "". + tkn, err = d.Token() + if err != nil { + t.Fatalf("Error occurred during decoding, got: %v", err) + } + b := &bytes.Buffer{} + childEnc := ChildEncoder(b, parentEnc) + if err := childEnc.EncodeToken(tkn); err != nil { + t.Fatalf("Error occurred while the child encoder was encoding token %q got: %v", tkn, err) + } + childEnc.Flush() + + // Verify that the token is not mangled. + if want := ""; b.String() != want { + t.Errorf("Error, got %q, wanted %q", b.String(), want) + } +} diff --git a/src/common/golang/pprint.go b/src/common/golang/pprint.go new file mode 100644 index 0000000..e0d7e75 --- /dev/null +++ b/src/common/golang/pprint.go @@ -0,0 +1,34 @@ +// Package pprint provides colored "pretty print" output helper methods +package pprint + +import ( + "fmt" + "os" +) + +const ( + errorString = "\033[1m\033[31mERROR:\033[0m %s\n" + warningString = "\033[35mWARNING:\033[0m %s\n" + infoString = "\033[32mINFO:\033[0m %s\n" + clearLine = "\033[A\033[K" +) + +// Error prints an error message in bazel style colors +func Error(errorMsg string, args ...interface{}) { + fmt.Fprintf(os.Stderr, errorString, fmt.Sprintf(errorMsg, args...)) +} + +// Warning prints a warning message in bazel style colors +func Warning(warningMsg string, args ...interface{}) { + fmt.Fprintf(os.Stderr, warningString, fmt.Sprintf(warningMsg, args...)) +} + +// Info prints an info message in bazel style colors +func Info(infoMsg string, args ...interface{}) { + fmt.Fprintf(os.Stderr, infoString, fmt.Sprintf(infoMsg, args...)) +} + +// ClearLine deletes the line above the cursor's current position. +func ClearLine() { + fmt.Printf(clearLine) +} diff --git a/src/common/golang/shard.go b/src/common/golang/shard.go new file mode 100644 index 0000000..a805bdc --- /dev/null +++ b/src/common/golang/shard.go @@ -0,0 +1,90 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package shard provides functions to help sharding your data. +package shard + +import ( + "archive/zip" + "errors" + "fmt" + "hash/fnv" + "io" + "strings" +) + +// Func converts a name and a number of shards into a particular shard index. +type Func func(name string, shardCount int) int + +// FNV uses the FNV hash algo on the provided string and mods its result by shardCount. +func FNV(name string, shardCount int) int { + h := fnv.New32() + h.Write([]byte(name)) + return int(h.Sum32()) % shardCount +} + +// MakeSepFunc creates a shard function that takes a substring from 0 to the last occurrence of +// separator from the name to be sharded, and passes that onto the provided shard function. +func MakeSepFunc(sep string, s Func) Func { + return func(name string, shardCount int) int { + idx := strings.LastIndex(name, sep) + if idx == -1 { + return s(name, shardCount) + } + return s(name[:idx], shardCount) + } +} + +// ZipShard takes a given zip reader, and shards its content across the provided io.Writers +// utilizing the provided SharderFunc. +func ZipShard(r *zip.Reader, zws []*zip.Writer, fn Func) error { + sc := len(zws) + if sc == 0 { + return errors.New("no output writers") + } + + for _, f := range r.File { + if !f.Mode().IsRegular() { + continue + } + si := fn(f.Name, sc) + if si < 0 || si > sc { + return fmt.Errorf("s.Shard(%s, %d) yields invalid shard index: %d", f.Name, sc, si) + } + zw := zws[si] + var rc io.ReadCloser + rc, err := f.Open() + if err != nil { + return fmt.Errorf("%s: could not open: %v", f.Name, err) + } + var zo io.Writer + zo, err = zw.CreateHeader(&zip.FileHeader{ + Name: f.Name, + Method: zip.Store, + }) + if err != nil { + return fmt.Errorf("%s: could not create output entry: %v", f.Name, err) + } + if err := copyAndClose(zo, rc); err != nil { + return fmt.Errorf("%s: copy to output failed: %v", f.Name, err) + } + } + return nil +} + +func copyAndClose(w io.Writer, rc io.ReadCloser) error { + defer rc.Close() + _, err := io.Copy(w, rc) + return err +} diff --git a/src/common/golang/shard_test.go b/src/common/golang/shard_test.go new file mode 100644 index 0000000..5b60361 --- /dev/null +++ b/src/common/golang/shard_test.go @@ -0,0 +1,98 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shard + +import ( + "testing" +) + +func TestFNV(t *testing.T) { + tests := []struct { + name string + in []string + sc int + want []int + }{ + { + name: "shardCount2", + in: []string{"foo", "bar", "baz"}, + sc: 2, + want: []int{1, 0, 0}, + }, + { + name: "shardCount5", + in: []string{"foo", "bar", "baz"}, + sc: 5, + want: []int{0, 2, 0}, + }, + { + name: "shardCount9", + in: []string{"foo", "bar", "baz"}, + sc: 9, + want: []int{2, 7, 6}, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + for idx, in := range test.in { + if shard := FNV(in, test.sc); shard != test.want[idx] { + t.Errorf("FNV applied for: %q got: %v wanted: %v", in, shard, test.want[idx]) + } + } + }) + } +} + +func TestMakeSepFunc(t *testing.T) { + tests := []struct { + name string + sep string + in []string + sc int + want []int + }{ + { + name: "makeSepFunc", + sep: "@", + in: []string{"foo@postfix", "bar@postfix", "baz@postfix"}, + sc: 9, + want: []int{2, 7, 6}, + }, + { + name: "makeSepFuncWithNoSep", + sep: "", + in: []string{"foo", "bar", "baz"}, + sc: 9, + want: []int{2, 7, 6}, + }, + { + name: "makeSepFuncWithWrongSep", + sep: "*", + in: []string{"foo", "bar", "baz"}, + sc: 9, + want: []int{2, 7, 6}, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + for idx, in := range test.in { + shardFn := MakeSepFunc(test.sep, FNV) + if shard := shardFn(in, test.sc); shard != test.want[idx] { + t.Errorf("MakeSepFunc applied for: %q got: %v wanted: %v", in, shard, test.want[idx]) + } + } + }) + } +} diff --git a/src/common/golang/walk.go b/src/common/golang/walk.go new file mode 100644 index 0000000..7b73484 --- /dev/null +++ b/src/common/golang/walk.go @@ -0,0 +1,51 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package walk provides an utility function to walk a directory tree collecting and deduping files. +package walk + +import ( + "fmt" + "os" + "path/filepath" +) + +// Files traverses a list of paths and returns a list of all the seen files. +func Files(paths []string) ([]string, error) { + var files []string + seen := make(map[string]bool) + visitFunc := func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if seen[path] { + return nil + } + seen[path] = true + switch fType := info.Mode(); { + case fType.IsDir(): + // Do nothing. + default: + files = append(files, path) + } + return nil + } + for _, p := range paths { + err := filepath.Walk(p, visitFunc) + if err != nil { + return nil, fmt.Errorf("got error while walking %s got: %v", p, err) + } + } + return files, nil +} diff --git a/src/common/golang/zipshard_test.go b/src/common/golang/zipshard_test.go new file mode 100644 index 0000000..de6dedc --- /dev/null +++ b/src/common/golang/zipshard_test.go @@ -0,0 +1,193 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shard + +import ( + "archive/zip" + "bytes" + "errors" + "fmt" + "reflect" + "sort" + "strings" + "testing" +) + +func TestSepSharder(t *testing.T) { + tcs := []struct { + name string + sep string + wantName string + }{ + { + name: "Hello", + sep: "/", + wantName: "Hello", + }, + { + name: "foo/bar/baz", + sep: "/", + wantName: "foo/bar", + }, + { + name: "com@google@Foo.dex", + sep: "@", + wantName: "com@google", + }, + } + + for _, tc := range tcs { + checkShard := func(name string, sc int) int { + if name != tc.wantName { + t.Errorf("makeSepSharder(%s).Shard(%s, 1): got name: %s wanted: %s", tc.sep, tc.name, name, tc.wantName) + } + return 0 + } + + s := MakeSepFunc(tc.sep, Func(checkShard)) + s(tc.name, 1) + } + +} + +func TestBadSharder(t *testing.T) { + srcZip, err := makeZip(map[string]string{"hello": "world"}) + if err != nil { + t.Fatalf("Could not make initial zip: %v", err) + } + + for _, shardVal := range []int{-1, -244, 123} { + zr, err := zip.NewReader(bytes.NewReader(srcZip), int64(len(srcZip))) + if err != nil { + t.Fatalf("could not read initial zip: %v", err) + } + zws := []*zip.Writer{zip.NewWriter(&bytes.Buffer{})} + + s := Func(func(name string, sc int) int { + return shardVal + }) + err = ZipShard(zr, zws, s) + if err == nil || !strings.Contains(err.Error(), "invalid shard index") { + t.Errorf("Returning shard value: %d gave: %v wanted an error with invalid shard index", shardVal, err) + } + } +} + +func TestZipShard(t *testing.T) { + tcs := []struct { + name string + contents map[string]string + shardCount int + want map[int][]string + zipShardErr error + }{ + { + name: "Vanilla", + contents: map[string]string{ + "foo/hello": "world", + "bar/something": "stuff", + "blah/nothing": "here", + "blah/everything": "nowhere", + "hello/everything": "nowhere", + }, + shardCount: 5, + want: map[int][]string{ + 0: {"hello/everything"}, + 3: {"foo/hello", "bar/something"}, + 4: {"blah/nothing", "blah/everything"}, + }, + }, + { + name: "no output shards", + contents: map[string]string{"something": "something"}, + shardCount: 0, + zipShardErr: errors.New("no output writers"), + }, + { + name: "empty input zip", + contents: map[string]string{}, + shardCount: 5, + want: map[int][]string{}, + }, + } + + for _, tc := range tcs { + srcZip, err := makeZip(tc.contents) + if err != nil { + t.Errorf("%s: could not create initial zip: %v", tc.name, err) + } + zr, err := zip.NewReader(bytes.NewReader(srcZip), int64(len(srcZip))) + + if err != nil { + t.Errorf("%s: could not read initial zip: %v", tc.name, err) + continue + } + bufs := make([]*bytes.Buffer, tc.shardCount) + zws := make([]*zip.Writer, tc.shardCount) + for i := range zws { + bufs[i] = new(bytes.Buffer) + zws[i] = zip.NewWriter(bufs[i]) + } + s := MakeSepFunc("/", Func(func(name string, sc int) int { + return len(name) % sc + })) + err = ZipShard(zr, zws, s) + if !reflect.DeepEqual(err, tc.zipShardErr) { + t.Errorf("%s: got zipshard error: %v wanted: %v", tc.name, err, tc.zipShardErr) + continue + } + for i, s := range bufs { + if err := zws[i].Close(); err != nil { + t.Errorf("%s: shard: %d cannot close zip writer: %v", tc.name, tc.shardCount, err) + continue + } + z, err := zip.NewReader(bytes.NewReader(s.Bytes()), int64(s.Len())) + if err != nil { + t.Errorf("%s: shard: %d cannot create zip reader: %v", tc.name, tc.shardCount, err) + continue + } + var fileNames []string + for _, f := range z.File { + fileNames = append(fileNames, f.Name) + } + sort.Strings(fileNames) + want, _ := tc.want[i] + sort.Strings(want) + if !reflect.DeepEqual(want, fileNames) { + t.Errorf("%s: shard: %d got: %s wanted: %s", tc.name, i, fileNames, want) + } + } + } +} + +func makeZip(contents map[string]string) ([]byte, error) { + var zin bytes.Buffer + zw := zip.NewWriter(&zin) + for name, body := range contents { + f, err := zw.Create(name) + if err != nil { + return nil, fmt.Errorf("%s: could not create: %v", name, err) + } + _, err = f.Write([]byte(body)) + if err != nil { + return nil, fmt.Errorf("%s: could not write: %s due to %v: ", name, body, err) + } + + } + if err := zw.Close(); err != nil { + return nil, err + } + return zin.Bytes(), nil +} diff --git a/src/common/golang/ziputils.go b/src/common/golang/ziputils.go new file mode 100644 index 0000000..e1a7af9 --- /dev/null +++ b/src/common/golang/ziputils.go @@ -0,0 +1,193 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package ziputils provides utility functions to work with zip files. +package ziputils + +import ( + "archive/zip" + "bytes" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" + + "golang.org/x/sync/errgroup" +) + +// Empty file contains only the End of central directory record. 0x06054b50 +// https://en.wikipedia.org/wiki/Zip_(file_format) +var ( + emptyzip = append([]byte{0x50, 0x4b, 0x05, 0x06}, make([]byte, 18)...) + dirPerm os.FileMode = 0755 +) + +// EmptyZipReader wraps an reader whose contents are the empty zip. +type EmptyZipReader struct { + *bytes.Reader +} + +// NewEmptyZipReader creates and returns an EmptyZipReader struct. +func NewEmptyZipReader() *EmptyZipReader { + return &EmptyZipReader{bytes.NewReader(emptyzip)} +} + +// EmptyZip creates empty zip archive. +func EmptyZip(dst string) error { + zipfile, err := os.Create(dst) + if err != nil { + return err + } + defer zipfile.Close() + _, err = io.Copy(zipfile, NewEmptyZipReader()) + return err +} + +// Zip archives src into dst without compression. +func Zip(src, dst string) error { + fi, err := os.Stat(src) + if err != nil { + return err + } + + zipfile, err := os.Create(dst) + if err != nil { + return err + } + defer zipfile.Close() + + archive := zip.NewWriter(zipfile) + defer archive.Close() + + if !fi.Mode().IsDir() { + return WriteFile(archive, src, filepath.Base(src)) + } + + return filepath.Walk(src, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if info.IsDir() { + return nil + } + + return WriteFile(archive, path, strings.TrimPrefix(path, src+string(filepath.Separator))) + }) +} + +// WriteFile writes filename to the out zip writer. +func WriteFile(out *zip.Writer, filename, zipFilename string) error { + // It's important to set timestamps to zero, otherwise we would break caching for unchanged files + f, err := out.CreateHeader(&zip.FileHeader{Name: zipFilename, Method: zip.Store, Modified: time.Unix(0, 0)}) + if err != nil { + return err + } + contents, err := ioutil.ReadFile(filename) + if err != nil { + return err + } + _, err = f.Write(contents) + return err +} + +// WriteReader writes a reader to the out zip writer. +func WriteReader(out *zip.Writer, in io.Reader, filename string) error { + // It's important to set timestamps to zero, otherwise we would break caching for unchanged files + f, err := out.CreateHeader(&zip.FileHeader{Name: filename, Method: zip.Store, Modified: time.Unix(0, 0)}) + if err != nil { + return err + } + contents, err := ioutil.ReadAll(in) + if err != nil { + return err + } + _, err = f.Write(contents) + return err +} + +// Unzip expands srcZip in dst directory +func Unzip(srcZip, dst string) error { + reader, err := zip.OpenReader(srcZip) + if err != nil { + return err + } + defer reader.Close() + + _, err = os.Stat(dst) + if err != nil && !os.IsNotExist(err) { + return err + } + if os.IsNotExist(err) { + if err := os.MkdirAll(dst, dirPerm); err != nil { + return err + } + } + + for _, file := range reader.File { + path := filepath.Join(dst, file.Name) + + if file.FileInfo().IsDir() { + if err := os.MkdirAll(path, dirPerm); err != nil { + return err + } + continue + } + + dir := filepath.Dir(path) + _, err := os.Stat(dir) + if err != nil && !os.IsNotExist(err) { + return err + } + if os.IsNotExist(err) { + if err := os.MkdirAll(dir, dirPerm); err != nil { + return err + } + } + + if err := write(file, path); err != nil { + return err + } + } + + return nil +} + +// UnzipParallel expands zip archives in parallel. +// TODO(b/137549283) Update UnzipParallel and add test +func UnzipParallel(srcZipDestMap map[string]string) error { + var eg errgroup.Group + for z, d := range srcZipDestMap { + zip, dest := z, d + eg.Go(func() error { return Unzip(zip, dest) }) + } + return eg.Wait() +} + +func write(zf *zip.File, path string) error { + rc, err := zf.Open() + if err != nil { + return err + } + defer rc.Close() + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + _, err = io.Copy(f, rc) + return err +} diff --git a/src/tools/ak/compile/BUILD b/src/tools/ak/compile/BUILD index 7e9e3bb..eb0e8da 100644 --- a/src/tools/ak/compile/BUILD +++ b/src/tools/ak/compile/BUILD @@ -34,4 +34,5 @@ go_test( srcs = [ "compile_test.go", ], + embed = [":compile"], ) diff --git a/src/tools/ak/liteparse/BUILD b/src/tools/ak/liteparse/BUILD index d70bdf2..ed8fbf7 100644 --- a/src/tools/ak/liteparse/BUILD +++ b/src/tools/ak/liteparse/BUILD @@ -37,6 +37,7 @@ go_test( "values_parse_test.go", ], data = glob(["testdata/**"]), + embed = [":liteparse"], deps = [ "//src/tools/ak/res", "//src/tools/ak/res/proto:res_data_go_proto", diff --git a/src/tools/ak/nativelib/BUILD b/src/tools/ak/nativelib/BUILD index 733ebf4..6e79d9e 100644 --- a/src/tools/ak/nativelib/BUILD +++ b/src/tools/ak/nativelib/BUILD @@ -35,4 +35,5 @@ go_test( data = [ "//src/tools/ak/nativelib/testdata:dummy_so", ], + embed = [":nativelib"], ) -- cgit v1.2.3 From bbb685b41d937e44aa0b40f36a281d4b24bf86b6 Mon Sep 17 00:00:00 2001 From: Andrew Sinclair Date: Tue, 4 Oct 2022 10:38:38 -0700 Subject: Delete unused ACL. PiperOrigin-RevId: 478827465 Change-Id: I08e895401a52abdec5fc3b4eeea4f9e35d875531 --- rules/acls.bzl | 7 ------- 1 file changed, 7 deletions(-) diff --git a/rules/acls.bzl b/rules/acls.bzl index 897b754..00ad2ff 100644 --- a/rules/acls.bzl +++ b/rules/acls.bzl @@ -32,7 +32,6 @@ load("//rules/acls:aar_import_explicit_exports_manifest.bzl", "AAR_IMPORT_EXPLIC load("//rules/acls:aar_import_exports_r_java.bzl", "AAR_IMPORT_EXPORTS_R_JAVA") load("//rules/acls:aar_propagate_resources.bzl", "AAR_PROPAGATE_RESOURCES_FALLBACK", "AAR_PROPAGATE_RESOURCES_ROLLOUT") load("//rules/acls:ait_install_snapshots.bzl", "APP_INSTALLATION_SNAPSHOT", "APP_INSTALLATION_SNAPSHOT_FALLBACK") -load("//rules/acls:ait_virtual_device.bzl", "AIT_VIRTUAL_DEVICE_FALLBACK", "AIT_VIRTUAL_DEVICE_ROLLOUT") load("//rules/acls:allow_resource_conflicts.bzl", "ALLOW_RESOURCE_CONFLICTS") load("//rules/acls:android_archive_dogfood.bzl", "ANDROID_ARCHIVE_DOGFOOD") load("//rules/acls:android_archive_excluded_deps_denylist.bzl", "ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST") @@ -84,9 +83,6 @@ def _in_aar_import_exports_r_java(fqn): def _in_aar_propagate_resources(fqn): return not matches(fqn, AAR_PROPAGATE_RESOURCES_FALLBACK_DICT) and matches(fqn, AAR_PROPAGATE_RESOURCES_ROLLOUT_DICT) -def _in_ait_virtual_device(fqn): - return not matches(fqn, AIT_VIRTUAL_DEVICE_FALLBACK_DICT) and matches(fqn, AIT_VIRTUAL_DEVICE_ROLLOUT_DICT) - def _in_android_archive_dogfood(fqn): return matches(fqn, ANDROID_ARCHIVE_DOGFOOD_DICT) @@ -207,8 +203,6 @@ AAR_IMPORT_EXPLICIT_EXPORTS_MANIFEST_DICT = make_dict(AAR_IMPORT_EXPLICIT_EXPORT AAR_IMPORT_EXPORTS_R_JAVA_DICT = make_dict(AAR_IMPORT_EXPORTS_R_JAVA) AAR_PROPAGATE_RESOURCES_FALLBACK_DICT = make_dict(AAR_PROPAGATE_RESOURCES_FALLBACK) AAR_PROPAGATE_RESOURCES_ROLLOUT_DICT = make_dict(AAR_PROPAGATE_RESOURCES_ROLLOUT) -AIT_VIRTUAL_DEVICE_FALLBACK_DICT = make_dict(AIT_VIRTUAL_DEVICE_FALLBACK) -AIT_VIRTUAL_DEVICE_ROLLOUT_DICT = make_dict(AIT_VIRTUAL_DEVICE_ROLLOUT) ANDROID_ARCHIVE_DOGFOOD_DICT = make_dict(ANDROID_ARCHIVE_DOGFOOD) ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST_DICT = make_dict(ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST) ANDROID_DEVICE_PLUGIN_ROLLOUT_DICT = make_dict(ANDROID_DEVICE_PLUGIN_ROLLOUT) @@ -309,7 +303,6 @@ acls = struct( in_aar_import_explicit_exports_manifest = _in_aar_import_explicit_exports_manifest, in_aar_import_exports_r_java = _in_aar_import_exports_r_java, in_aar_propagate_resources = _in_aar_propagate_resources, - in_ait_virtual_device = _in_ait_virtual_device, in_b122039567 = _in_b122039567, in_b123854163 = _in_b123854163, in_android_archive_dogfood = _in_android_archive_dogfood, -- cgit v1.2.3 From 52766623bfab953d5175277d7760e10d047d7911 Mon Sep 17 00:00:00 2001 From: Tim Peut Date: Tue, 4 Oct 2022 14:34:13 -0700 Subject: Delete deprecated lint acl PiperOrigin-RevId: 478885386 Change-Id: Ifacdadb37338b46716768b60771f3a50249572d2 --- rules/acls/android_lint_checks_rollout.bzl | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 rules/acls/android_lint_checks_rollout.bzl diff --git a/rules/acls/android_lint_checks_rollout.bzl b/rules/acls/android_lint_checks_rollout.bzl deleted file mode 100644 index ee44280..0000000 --- a/rules/acls/android_lint_checks_rollout.bzl +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2021 The Bazel Authors. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Allow list for enabling Android Lint checks in the Android Rules.""" - -# keep sorted -ANDROID_LINT_CHECKS_ROLLOUT = [ -] - -ANDROID_LINT_CHECKS_FALLBACK = [ -] -- cgit v1.2.3 From 613457d0720c0988d9078c67543b942101ac53c0 Mon Sep 17 00:00:00 2001 From: Joseph Park Date: Wed, 5 Oct 2022 14:26:06 -0700 Subject: Fix the way flags are defined in enforce_min_sdk_floor.py PiperOrigin-RevId: 479143702 Change-Id: I9054657219cb7372ef26aa23c9b657e7a60516b3 --- .../enforce_min_sdk_floor/enforce_min_sdk_floor.py | 28 ++++++++++++---------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py b/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py index 3059b98..b7192f7 100644 --- a/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py +++ b/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py @@ -32,25 +32,27 @@ VALIDATE = "validate" USES_SDK = "uses-sdk" MIN_SDK_ATTRIB = "{http://schemas.android.com/apk/res/android}minSdkVersion" -_ACTION = flags.DEFINE_enum( +FLAGS = flags.FLAGS + +flags.DEFINE_enum( "action", None, [BUMP, VALIDATE], f"Action to perform, either {BUMP} or {VALIDATE}") -_MANIFEST = flags.DEFINE_string( +flags.DEFINE_string( "manifest", None, "AndroidManifest.xml of the instrumentation APK") -_MIN_SDK_FLOOR = flags.DEFINE_integer( +flags.DEFINE_integer( "min_sdk_floor", 0, "Min SDK floor", lower_bound=0) -_OUTPUT = flags.DEFINE_string( +flags.DEFINE_string( "output", None, f"Output AndroidManifest.xml to generate, only needed for {BUMP}") -_LOG = flags.DEFINE_string("log", None, "Path to write the log to") +flags.DEFINE_string("log", None, "Path to write the log to") class MinSdkError(Exception): @@ -162,30 +164,30 @@ def _ValidateMinSdk(xml_content, min_sdk_floor): def main(unused_argv): - manifest_path = _MANIFEST.value + manifest_path = FLAGS.manifest with open(manifest_path, "rb") as f: manifest = f.read() - if _ACTION.value == BUMP: - output_path = _OUTPUT.value + if FLAGS.action == BUMP: + output_path = FLAGS.output dirname = os.path.dirname(output_path) if not os.path.exists(dirname): os.makedirs(dirname) - out_contents, log_message = _BumpMinSdk(manifest, _MIN_SDK_FLOOR.value) + out_contents, log_message = _BumpMinSdk(manifest, FLAGS.min_sdk_floor) with open(output_path, "wb") as f: f.write(out_contents) - elif _ACTION.value == VALIDATE: + elif FLAGS.action == VALIDATE: try: - log_message = _ValidateMinSdk(manifest, _MIN_SDK_FLOOR.value) + log_message = _ValidateMinSdk(manifest, FLAGS.min_sdk_floor) except MinSdkError as e: sys.exit(str(e)) else: sys.exit(f"Action must be either {BUMP} or {VALIDATE}") - if _LOG.value is not None: - log_path = _LOG.value + if FLAGS.log is not None: + log_path = FLAGS.log dirname = os.path.dirname(log_path) if not os.path.exists(dirname): os.makedirs(dirname) -- cgit v1.2.3 From adc77f2ea0181095162ddaf1ea1fbda3c2713e13 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Thu, 6 Oct 2022 07:25:43 -0700 Subject: Fix up and enable tests for most rules_android golang tools PiperOrigin-RevId: 479308457 Change-Id: I271094431527a9d3b2d872b3ce40a787357a5009 --- kokoro/presubmit/kokoro_presubmit.sh | 3 +- src/common/golang/BUILD | 18 ++--- src/tools/ak/BUILD | 6 +- src/tools/ak/compile/BUILD | 2 +- src/tools/ak/dex/BUILD | 2 +- src/tools/ak/link/BUILD | 2 +- src/tools/ak/liteparse/BUILD | 2 +- src/tools/ak/manifest/BUILD | 2 +- src/tools/ak/manifestutils.go | 148 +++++++++++++++++++++++++++++++++++ src/tools/ak/mindex/BUILD | 2 +- src/tools/ak/nativelib/BUILD | 2 +- src/tools/ak/types.go | 39 +++++++++ 12 files changed, 208 insertions(+), 20 deletions(-) create mode 100644 src/tools/ak/manifestutils.go create mode 100644 src/tools/ak/types.go diff --git a/kokoro/presubmit/kokoro_presubmit.sh b/kokoro/presubmit/kokoro_presubmit.sh index 2b2214e..02b561f 100644 --- a/kokoro/presubmit/kokoro_presubmit.sh +++ b/kokoro/presubmit/kokoro_presubmit.sh @@ -65,7 +65,8 @@ COMMON_ARGS=( # Go to rules_android workspace and run relevant tests. cd "${KOKORO_ARTIFACTS_DIR}/git/rules_android" -"$bazel" test "${COMMON_ARGS[@]}" //src/common/golang/... +"$bazel" test "${COMMON_ARGS[@]}" //src/common/golang/... \ + //src/tools/ak/{compile,dex,link,manifest,mindex}/... # Go to basic app workspace in the source tree cd "${KOKORO_ARTIFACTS_DIR}/git/rules_android/examples/basicapp" diff --git a/src/common/golang/BUILD b/src/common/golang/BUILD index 016762d..23d2761 100644 --- a/src/common/golang/BUILD +++ b/src/common/golang/BUILD @@ -10,7 +10,7 @@ licenses(["notice"]) go_library( name = "xml2", srcs = ["marshal.go"], - importpath = "github.com/bazelbuild/rules_android/src/common/golang/xml2", + importpath = "src/common/golang/xml2", ) go_test( @@ -25,7 +25,7 @@ go_test( go_library( name = "shard", srcs = ["shard.go"], - importpath = "github.com/bazelbuild/rules_android/src/common/golang/shard", + importpath = "src/common/golang/shard", ) go_test( @@ -41,26 +41,26 @@ go_test( go_library( name = "walk", srcs = ["walk.go"], - importpath = "github.com/bazelbuild/rules_android/src/common/golang/walk", + importpath = "src/common/golang/walk", ) go_library( name = "ziputils", srcs = ["ziputils.go"], - importpath = "github.com/bazelbuild/rules_android/src/common/golang/ziputils", + importpath = "src/common/golang/ziputils", deps = ["@org_golang_x_sync//errgroup"], ) go_library( name = "fileutils", srcs = ["fileutils.go"], - importpath = "github.com/bazelbuild/rules_android/src/common/golang/fileutils", + importpath = "src/common/golang/fileutils", ) go_library( name = "flags", srcs = ["flags.go"], - importpath = "github.com/bazelbuild/rules_android/src/common/golang/flags", + importpath = "src/common/golang/flags", ) go_test( @@ -73,7 +73,7 @@ go_test( go_library( name = "ini", srcs = ["ini.go"], - importpath = "github.com/bazelbuild/rules_android/src/common/golang/ini", + importpath = "src/common/golang/ini", ) go_test( @@ -86,11 +86,11 @@ go_test( go_library( name = "pprint", srcs = ["pprint.go"], - importpath = "github.com/bazelbuild/rules_android/src/common/golang/pprint", + importpath = "src/common/golang/pprint", ) go_library( name = "flagfile", srcs = ["flagfile.go"], - importpath = "github.com/bazelbuild/rules_android/src/common/golang/flagfile", + importpath = "src/common/golang/flagfile", ) diff --git a/src/tools/ak/BUILD b/src/tools/ak/BUILD index b1b0546..985575f 100644 --- a/src/tools/ak/BUILD +++ b/src/tools/ak/BUILD @@ -35,19 +35,19 @@ go_binary( go_library( name = "types", srcs = ["types.go"], - importpath = "github.com/bazelbuild/rules_android/src/tools/ak/types", + importpath = "src/tools/ak/types", ) go_library( name = "akhelper", srcs = ["akhelper.go"], - importpath = "github.com/bazelbuild/rules_android/src/tools/ak/akhelper", + importpath = "src/tools/ak/akhelper", ) go_library( name = "manifestutils", srcs = ["manifestutils.go"], - importpath = "github.com/bazelbuild/rules_android/src/tools/ak/manifestutils", + importpath = "src/tools/ak/manifestutils", deps = [ "//src/common/golang:xml2", ], diff --git a/src/tools/ak/compile/BUILD b/src/tools/ak/compile/BUILD index eb0e8da..6b7f246 100644 --- a/src/tools/ak/compile/BUILD +++ b/src/tools/ak/compile/BUILD @@ -21,7 +21,7 @@ go_library( srcs = [ "compile.go", ], - importpath = "github.com/bazelbuild/rules_android/src/tools/ak/compile/compile", + importpath = "src/tools/ak/compile/compile", deps = [ "//src/common/golang:ziputils", "//src/tools/ak:types", diff --git a/src/tools/ak/dex/BUILD b/src/tools/ak/dex/BUILD index ab27858..80e4900 100644 --- a/src/tools/ak/dex/BUILD +++ b/src/tools/ak/dex/BUILD @@ -18,7 +18,7 @@ go_binary( go_library( name = "dex", srcs = ["dex.go"], - importpath = "github.com/bazelbuild/rules_android/src/tools/ak/dex/dex", + importpath = "src/tools/ak/dex/dex", deps = [ "//src/common/golang:flags", "//src/common/golang:shard", diff --git a/src/tools/ak/link/BUILD b/src/tools/ak/link/BUILD index 2b8cc1e..e52fb72 100644 --- a/src/tools/ak/link/BUILD +++ b/src/tools/ak/link/BUILD @@ -21,7 +21,7 @@ go_library( srcs = [ "link.go", ], - importpath = "github.com/bazelbuild/rules_android/src/tools/ak/link/link", + importpath = "src/tools/ak/link/link", deps = [ "//src/common/golang:flags", "//src/common/golang:walk", diff --git a/src/tools/ak/liteparse/BUILD b/src/tools/ak/liteparse/BUILD index ed8fbf7..0cb8fea 100644 --- a/src/tools/ak/liteparse/BUILD +++ b/src/tools/ak/liteparse/BUILD @@ -14,7 +14,7 @@ go_library( "non_values_parse.go", "values_parse.go", ], - importpath = "github.com/bazelbuild/rules_android/src/tools/ak/liteparse/liteparse", + importpath = "src/tools/ak/liteparse/liteparse", deps = [ "//src/common/golang:flags", "//src/common/golang:walk", diff --git a/src/tools/ak/manifest/BUILD b/src/tools/ak/manifest/BUILD index 5a80c5b..b8031a8 100644 --- a/src/tools/ak/manifest/BUILD +++ b/src/tools/ak/manifest/BUILD @@ -20,7 +20,7 @@ go_library( srcs = [ "manifest.go", ], - importpath = "github.com/bazelbuild/rules_android/src/tools/ak/manifest/manifest", + importpath = "src/tools/ak/manifest/manifest", deps = [ "//src/common/golang:flags", "//src/tools/ak:manifestutils", diff --git a/src/tools/ak/manifestutils.go b/src/tools/ak/manifestutils.go new file mode 100644 index 0000000..bea212b --- /dev/null +++ b/src/tools/ak/manifestutils.go @@ -0,0 +1,148 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package manifestutils provides common methods to interact with and modify AndroidManifest.xml files. +package manifestutils + +import ( + "encoding/xml" + "io" + "log" + "strings" + + "src/common/golang/xml2" +) + +// Constant attribute names used in an AndroidManifest. +const ( + NameSpace = "http://schemas.android.com/apk/res/android" + ElemManifest = "manifest" + AttrPackage = "package" + AttrSplit = "split" + AttrFeatureName = "featureName" + AttrSharedUserID = "sharedUserId" + AttrSharedUserLabel = "sharedUserLabel" + AttrVersionCode = "versionCode" + AttrVersionName = "versionName" +) + +var ( + // NoNSAttrs contains attributes that are not namespaced. + NoNSAttrs = map[string]bool{ + AttrPackage: true, + AttrSplit: true, + AttrFeatureName: true} +) + +// Manifest is the XML root that we want to parse. +type Manifest struct { + XMLName xml.Name `xml:"manifest"` + Package string `xml:"package,attr"` + SharedUserID string `xml:"sharedUserId,attr"` + SharedUserLabel string `xml:"sharedUserLabel,attr"` + VersionCode string `xml:"versionCode,attr"` + VersionName string `xml:"versionName,attr"` + Application Application `xml:"application"` +} + +// Application is the XML tag that we want to parse. +type Application struct { + XMLName xml.Name `xml:"application"` + Name string `xml:"http://schemas.android.com/apk/res/android name,attr"` +} + +// Encoder takes the xml.Token and encodes it, interface allows us to use xml2.Encoder. +type Encoder interface { + EncodeToken(xml.Token) error +} + +// Patch updates an AndroidManifest by patching the attributes of existing elements. +// +// Attributes that are already defined on the element are updated, while missing +// attributes are added to the element's attributes. Elements in patchElems that are +// missing from the manifest are ignored. +func Patch(dec *xml.Decoder, enc Encoder, patchElems map[string]map[string]xml.Attr) error { + for { + t, err := dec.Token() + if err != nil { + if err == io.EOF { + break + } + return err + } + switch tt := t.(type) { + case xml.StartElement: + elem := tt.Name.Local + if attrs, ok := patchElems[elem]; ok { + found := make(map[string]bool) + for i, a := range tt.Attr { + if attr, ok := attrs[a.Name.Local]; a.Name.Space == attr.Name.Space && ok { + found[a.Name.Local] = true + tt.Attr[i] = attr + } + } + for _, attr := range attrs { + if found[attr.Name.Local] { + continue + } + + tt.Attr = append(tt.Attr, attr) + } + } + enc.EncodeToken(tt) + default: + enc.EncodeToken(tt) + } + } + return nil +} + +// WriteManifest writes an AndroidManifest with updates to patched elements. +func WriteManifest(dst io.Writer, src io.Reader, patchElems map[string]map[string]xml.Attr) error { + e := xml2.NewEncoder(dst) + if err := Patch(xml.NewDecoder(src), e, patchElems); err != nil { + return err + } + return e.Flush() +} + +// CreatePatchElements creates an element map from a string array of "element:attr:attr_value" entries. +func CreatePatchElements(attr []string) map[string]map[string]xml.Attr { + patchElems := make(map[string]map[string]xml.Attr) + for _, a := range attr { + pts := strings.Split(a, ":") + if len(pts) < 3 { + log.Fatalf("Failed to parse attr to replace %s", a) + } + + elem := pts[0] + attr := pts[1] + ns := NameSpace + + // https://developer.android.com/guide/topics/manifest/manifest-element + if elem == ElemManifest && NoNSAttrs[attr] { + ns = "" + } + + if ais, ok := patchElems[elem]; ok { + ais[attr] = xml.Attr{ + Name: xml.Name{Space: ns, Local: attr}, Value: pts[2]} + } else { + patchElems[elem] = map[string]xml.Attr{ + attr: xml.Attr{ + Name: xml.Name{Space: ns, Local: attr}, Value: pts[2]}} + } + } + return patchElems +} diff --git a/src/tools/ak/mindex/BUILD b/src/tools/ak/mindex/BUILD index f124b6f..aeb40ec 100644 --- a/src/tools/ak/mindex/BUILD +++ b/src/tools/ak/mindex/BUILD @@ -19,6 +19,6 @@ go_binary( go_library( name = "mindex", srcs = ["mindex.go"], - importpath = "github.com/bazelbuild/rules_android/src/tools/ak/mindex/mindex", + importpath = "src/tools/ak/mindex/mindex", deps = ["//src/tools/ak:types"], ) diff --git a/src/tools/ak/nativelib/BUILD b/src/tools/ak/nativelib/BUILD index 6e79d9e..50bcdd6 100644 --- a/src/tools/ak/nativelib/BUILD +++ b/src/tools/ak/nativelib/BUILD @@ -10,7 +10,7 @@ licenses(["notice"]) go_library( name = "nativelib", srcs = ["nativelib.go"], - importpath = "github.com/bazelbuild/rules_android/src/tools/ak/nativelib/nativelib", + importpath = "src/tools/ak/nativelib/nativelib", deps = [ "//src/common/golang:fileutils", "//src/common/golang:flags", diff --git a/src/tools/ak/types.go b/src/tools/ak/types.go new file mode 100644 index 0000000..6416461 --- /dev/null +++ b/src/tools/ak/types.go @@ -0,0 +1,39 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package types provides globally used types. +package types + +type initFunc func() +type runFunc func() +type descFunc func() string + +/* +Command is used to specify a command. + + Init: + Entry point to initialize the command. + Run: + Entry point to run the command. + Flags: + (Optional) Flags that are used by the command. + Desc: + A short description of the command. +*/ +type Command struct { + Init initFunc + Run runFunc + Flags []string + Desc descFunc +} -- cgit v1.2.3 From 33f4a9a9f3dd2a15af4a55b9cf08a87a947da11e Mon Sep 17 00:00:00 2001 From: Tim Peut Date: Mon, 10 Oct 2022 15:35:06 -0700 Subject: Resurrect the fully-enabled lint config PiperOrigin-RevId: 480193174 Change-Id: I2f58a07d488cc9fd042852aa4dcd7c0cfb0368e2 --- rules/acls/android_lint_checks_rollout.bzl | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 rules/acls/android_lint_checks_rollout.bzl diff --git a/rules/acls/android_lint_checks_rollout.bzl b/rules/acls/android_lint_checks_rollout.bzl new file mode 100644 index 0000000..8fee3ff --- /dev/null +++ b/rules/acls/android_lint_checks_rollout.bzl @@ -0,0 +1,22 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for using the "fully enabled" lint check config.""" + +# keep sorted +ANDROID_LINT_CHECKS_ROLLOUT = [ +] + +ANDROID_LINT_CHECKS_FALLBACK = [ +] -- cgit v1.2.3 From 70b55396f060d126889bf7e393dfe26e756f5e06 Mon Sep 17 00:00:00 2001 From: Tim Peut Date: Mon, 10 Oct 2022 16:04:19 -0700 Subject: Restore "fully enabled" lint acl PiperOrigin-RevId: 480199563 Change-Id: I83dc106fbe594218a85ae89fb33780e2f36999bc --- rules/acls.bzl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rules/acls.bzl b/rules/acls.bzl index 00ad2ff..58cc53f 100644 --- a/rules/acls.bzl +++ b/rules/acls.bzl @@ -43,6 +43,7 @@ load("//rules/acls:android_binary_starlark_javac.bzl", "ANDROID_BINARY_STARLARK_ load("//rules/acls:android_feature_splits_dogfood.bzl", "ANDROID_FEATURE_SPLITS_DOGFOOD") load("//rules/acls:android_library_resources_without_srcs.bzl", "ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS", "ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS") load("//rules/acls:android_library_starlark_resource_outputs.bzl", "ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK", "ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT") +load("//rules/acls:android_lint_checks_rollout.bzl", "ANDROID_LINT_CHECKS_FALLBACK", "ANDROID_LINT_CHECKS_ROLLOUT") load("//rules/acls:android_lint_rollout.bzl", "ANDROID_LINT_FALLBACK", "ANDROID_LINT_ROLLOUT") load("//rules/acls:lint_registry_rollout.bzl", "LINT_REGISTRY_FALLBACK", "LINT_REGISTRY_ROLLOUT") load("//rules/acls:android_build_stamping_rollout.bzl", "ANDROID_BUILD_STAMPING_FALLBACK", "ANDROID_BUILD_STAMPING_ROLLOUT") @@ -101,6 +102,9 @@ def _in_android_binary_starlark_javac(fqn): def _in_android_feature_splits_dogfood(fqn): return matches(fqn, ANDROID_FEATURE_SPLITS_DOGFOOD_DICT) +def _in_android_lint_checks_rollout(fqn): + return not matches(fqn, ANDROID_LINT_CHECKS_FALLBACK_DICT) and matches(fqn, ANDROID_LINT_CHECKS_ROLLOUT_DICT) + def _in_android_lint_rollout(fqn): return not matches(fqn, ANDROID_LINT_FALLBACK_DICT) and matches(fqn, ANDROID_LINT_ROLLOUT_DICT) @@ -216,6 +220,8 @@ ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_DICT = make_dict(ANDROID_LIBRARY_RESOURCE ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS_DICT = make_dict(ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS) ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK_DICT = make_dict(ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK) ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT_DICT = make_dict(ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT) +ANDROID_LINT_CHECKS_FALLBACK_DICT = make_dict(ANDROID_LINT_CHECKS_FALLBACK) +ANDROID_LINT_CHECKS_ROLLOUT_DICT = make_dict(ANDROID_LINT_CHECKS_ROLLOUT) ANDROID_LINT_FALLBACK_DICT = make_dict(ANDROID_LINT_FALLBACK) ANDROID_LINT_ROLLOUT_DICT = make_dict(ANDROID_LINT_ROLLOUT) ANDROID_RULES_WITH_KT_ROLLOUT_DICT = make_dict(ANDROID_RULES_WITH_KT_ROLLOUT) @@ -314,6 +320,7 @@ acls = struct( in_android_library_starlark_resource_outputs_rollout = _in_android_library_starlark_resource_outputs_rollout, in_android_library_resources_without_srcs = _in_android_library_resources_without_srcs, in_android_library_resources_without_srcs_generator_functions = _in_android_library_resources_without_srcs_generator_functions, + in_android_lint_checks_rollout = _in_android_lint_checks_rollout, in_android_lint_rollout = _in_android_lint_rollout, in_lint_registry_rollout = _in_lint_registry_rollout, in_android_build_stamping_rollout = _in_android_build_stamping_rollout, -- cgit v1.2.3 From 0ca63ad8f76c2a46c037427e1682c35e7a5ea8dd Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Tue, 11 Oct 2022 13:29:23 -0700 Subject: Enable `--define=mi_desugar_java8_libs=True` by default PiperOrigin-RevId: 480439202 Change-Id: Ia8fc23114d6226577ca0d65d1f923c8757b2b9c5 --- rules/flags/flag_defs.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/flags/flag_defs.bzl b/rules/flags/flag_defs.bzl index 9773ca9..0a81805 100644 --- a/rules/flags/flag_defs.bzl +++ b/rules/flags/flag_defs.bzl @@ -76,7 +76,7 @@ def define_flags(): flags.DEFINE_bool( name = "mi_desugar_java8_libs", - default = False, + default = True, description = "Set True with --config=android_java8_libs", ) -- cgit v1.2.3 From 28be58502a250fdfa4ad9cb9d8701c75ffc10c7b Mon Sep 17 00:00:00 2001 From: Tim Peut Date: Wed, 12 Oct 2022 11:59:22 -0700 Subject: Internal change PiperOrigin-RevId: 480682363 Change-Id: I516d6c6367d802fd57c77195b31ad7073455dc34 --- rules/acls.bzl | 6 ++++++ rules/acls/android_apk_to_bundle_features_lockdown.bzl | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 rules/acls/android_apk_to_bundle_features_lockdown.bzl diff --git a/rules/acls.bzl b/rules/acls.bzl index 58cc53f..5c3a6b8 100644 --- a/rules/acls.bzl +++ b/rules/acls.bzl @@ -71,6 +71,7 @@ load("//rules/acls:android_instrumentation_test_prebuilt_test_apk.bzl", "ANDROID load("//rules/acls:android_rules_with_kt_rollout.bzl", "ANDROID_RULES_WITH_KT_FALLBACK", "ANDROID_RULES_WITH_KT_ROLLOUT") load("//rules/acls:baseline_profiles_rollout.bzl", "BASELINE_PROFILES_ROLLOUT") load("//rules/acls:enforce_min_sdk_floor_rollout.bzl", "ENFORCE_MIN_SDK_FLOOR_FALLBACK", "ENFORCE_MIN_SDK_FLOOR_ROLLOUT") +load("//rules/acls:android_apk_to_bundle_features_lockdown.bzl", "ANDROID_APK_TO_BUNDLE_FEATURES") def _in_aar_import_deps_checker(fqn): return not matches(fqn, AAR_IMPORT_DEPS_CHECKER_FALLBACK_DICT) and matches(fqn, AAR_IMPORT_DEPS_CHECKER_ROLLOUT_DICT) @@ -197,6 +198,9 @@ def _in_baseline_profiles_rollout(fqn): def _in_enforce_min_sdk_floor_rollout(fqn): return not matches(fqn, ENFORCE_MIN_SDK_FLOOR_FALLBACK_DICT) and matches(fqn, ENFORCE_MIN_SDK_FLOOR_ROLLOUT_DICT) +def _in_android_apk_to_bundle_features(fqn): + return matches(fqn, ANDROID_APK_TO_BUNDLE_FEATURES_DICT) + def make_dict(lst): """Do not use this method outside of acls directory.""" return {t: True for t in lst} @@ -267,6 +271,7 @@ ANDROID_INSTRUMENTATION_TEST_PREBUILT_TEST_APK_DICT = make_dict(ANDROID_INSTRUME BASELINE_PROFILES_ROLLOUT_DICT = make_dict(BASELINE_PROFILES_ROLLOUT) ENFORCE_MIN_SDK_FLOOR_ROLLOUT_DICT = make_dict(ENFORCE_MIN_SDK_FLOOR_ROLLOUT) ENFORCE_MIN_SDK_FLOOR_FALLBACK_DICT = make_dict(ENFORCE_MIN_SDK_FLOOR_FALLBACK) +ANDROID_APK_TO_BUNDLE_FEATURES_DICT = make_dict(ANDROID_APK_TO_BUNDLE_FEATURES) def matches(fqn, dct): # Labels with workspace names ("@workspace//pkg:target") are not supported. @@ -345,6 +350,7 @@ acls = struct( in_android_rules_with_kt_rollout = _in_android_rules_with_kt_rollout, in_baseline_profiles_rollout = _in_baseline_profiles_rollout, in_enforce_min_sdk_floor_rollout = _in_enforce_min_sdk_floor_rollout, + in_android_apk_to_bundle_features = _in_android_apk_to_bundle_features, ) # Visible for testing diff --git a/rules/acls/android_apk_to_bundle_features_lockdown.bzl b/rules/acls/android_apk_to_bundle_features_lockdown.bzl new file mode 100644 index 0000000..28bacb6 --- /dev/null +++ b/rules/acls/android_apk_to_bundle_features_lockdown.bzl @@ -0,0 +1,18 @@ +# Copyright 2022 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allowlist for specifying `features` on the android_apk_to_bundle rule.""" + +ANDROID_APK_TO_BUNDLE_FEATURES = [ +] -- cgit v1.2.3 From f88207a1b310cccf4954c3c8fec7409e4420272a Mon Sep 17 00:00:00 2001 From: A Googler Date: Thu, 13 Oct 2022 22:30:09 -0700 Subject: Add redirection to native android_common module PiperOrigin-RevId: 481062005 Change-Id: Ib909777a7a91d03c7f060cf1e574c86f37b1d4e1 --- rules/android_common/BUILD | 18 ++++++++++++++++++ rules/android_common/reexport_android_common.bzl | 21 +++++++++++++++++++++ rules/common.bzl | 3 +++ 3 files changed, 42 insertions(+) create mode 100644 rules/android_common/BUILD create mode 100644 rules/android_common/reexport_android_common.bzl diff --git a/rules/android_common/BUILD b/rules/android_common/BUILD new file mode 100644 index 0000000..241cf20 --- /dev/null +++ b/rules/android_common/BUILD @@ -0,0 +1,18 @@ +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") + +package( + default_visibility = + ["//:__subpackages__"], +) + +licenses(["notice"]) + +filegroup( + name = "all_files", + srcs = glob(["**"]), +) + +bzl_library( + name = "bzl", + srcs = glob(["*.bzl"]), +) diff --git a/rules/android_common/reexport_android_common.bzl b/rules/android_common/reexport_android_common.bzl new file mode 100644 index 0000000..22399ea --- /dev/null +++ b/rules/android_common/reexport_android_common.bzl @@ -0,0 +1,21 @@ +# Copyright 2022 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""A workaround to expose android_common native module in android/common.bzl. + +Redefine native symbols with a new name as a workaround for +exporting them in `rules:common.bzl` with their original name. +""" + +native_android_common = android_common diff --git a/rules/common.bzl b/rules/common.bzl index c507bb4..1cd512e 100644 --- a/rules/common.bzl +++ b/rules/common.bzl @@ -15,6 +15,7 @@ """Bazel common library for the Android rules.""" load(":utils.bzl", "get_android_toolchain", _log = "log") +load("//rules/android_common:reexport_android_common.bzl", _native_android_common = "native_android_common") # Validates that the packages listed under "deps" all have the given constraint. If a package # does not have this attribute, an error is generated. @@ -73,3 +74,5 @@ common = struct( get_java_toolchain = _get_java_toolchain, filter_zip = _filter_zip, ) + +android_common = _native_android_common -- cgit v1.2.3 From 1b21c740db654afb5a659d27294406d38a29b656 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Fri, 14 Oct 2022 07:36:10 -0700 Subject: Release AK tools rjar, finalrjar, and generatemanifest Note: rjar_test does not work yet. PiperOrigin-RevId: 481142512 Change-Id: Ie4da52d37436622723ffe1bc901053361bceeb6e --- kokoro/presubmit/kokoro_presubmit.sh | 2 +- src/java/com/example/sampleapp/BUILD | 4 +- src/tools/ak/finalrjar/BUILD | 35 ++ src/tools/ak/finalrjar/finalrjar.go | 451 +++++++++++++++++++++ src/tools/ak/finalrjar/finalrjar_bin.go | 29 ++ src/tools/ak/finalrjar/finalrjar_test.go | 351 ++++++++++++++++ src/tools/ak/generatemanifest/BUILD | 34 ++ src/tools/ak/generatemanifest/generatemanifest.go | 188 +++++++++ .../ak/generatemanifest/generatemanifest_bin.go | 29 ++ .../ak/generatemanifest/generatemanifest_test.go | 220 ++++++++++ src/tools/ak/rjar/BUILD | 44 ++ src/tools/ak/rjar/rjar.go | 295 ++++++++++++++ src/tools/ak/rjar/rjar_bin.go | 29 ++ src/tools/ak/rjar/rjar_test.go | 82 ++++ src/tools/ak/rjar/testdata/BUILD | 26 ++ 15 files changed, 1816 insertions(+), 3 deletions(-) create mode 100644 src/tools/ak/finalrjar/BUILD create mode 100644 src/tools/ak/finalrjar/finalrjar.go create mode 100644 src/tools/ak/finalrjar/finalrjar_bin.go create mode 100644 src/tools/ak/finalrjar/finalrjar_test.go create mode 100644 src/tools/ak/generatemanifest/BUILD create mode 100644 src/tools/ak/generatemanifest/generatemanifest.go create mode 100644 src/tools/ak/generatemanifest/generatemanifest_bin.go create mode 100644 src/tools/ak/generatemanifest/generatemanifest_test.go create mode 100644 src/tools/ak/rjar/BUILD create mode 100644 src/tools/ak/rjar/rjar.go create mode 100644 src/tools/ak/rjar/rjar_bin.go create mode 100644 src/tools/ak/rjar/rjar_test.go create mode 100644 src/tools/ak/rjar/testdata/BUILD diff --git a/kokoro/presubmit/kokoro_presubmit.sh b/kokoro/presubmit/kokoro_presubmit.sh index 02b561f..50ec5b5 100644 --- a/kokoro/presubmit/kokoro_presubmit.sh +++ b/kokoro/presubmit/kokoro_presubmit.sh @@ -66,7 +66,7 @@ COMMON_ARGS=( # Go to rules_android workspace and run relevant tests. cd "${KOKORO_ARTIFACTS_DIR}/git/rules_android" "$bazel" test "${COMMON_ARGS[@]}" //src/common/golang/... \ - //src/tools/ak/{compile,dex,link,manifest,mindex}/... + //src/tools/ak/{compile,dex,finalrjar,generatemanifest,link,manifest,mindex,rjar}/... # Go to basic app workspace in the source tree cd "${KOKORO_ARTIFACTS_DIR}/git/rules_android/examples/basicapp" diff --git a/src/java/com/example/sampleapp/BUILD b/src/java/com/example/sampleapp/BUILD index db56adf..8530ec2 100644 --- a/src/java/com/example/sampleapp/BUILD +++ b/src/java/com/example/sampleapp/BUILD @@ -2,7 +2,7 @@ load("//rules:rules.bzl", "android_binary", "android_library") -package(default_visibility = ["//visibility:private"]) +package(default_visibility = ["//src:__subpackages__"]) android_binary( name = "sampleapp", @@ -20,8 +20,8 @@ android_library( resource_files = glob(["res/**"]), deps = [ ":native", - "@androidsdk//com.android.support:support-v4-25.2.0", "@androidsdk//com.android.support:appcompat-v7-25.0.0", + "@androidsdk//com.android.support:support-v4-25.2.0", ], ) diff --git a/src/tools/ak/finalrjar/BUILD b/src/tools/ak/finalrjar/BUILD new file mode 100644 index 0000000..db487da --- /dev/null +++ b/src/tools/ak/finalrjar/BUILD @@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +# Description: +# Package for final R.jar module +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +go_library( + name = "finalrjar", + srcs = ["finalrjar.go"], + importpath = "src/tools/ak/finalrjar/finalrjar", + deps = [ + "//src/common/golang:ziputils", + "//src/tools/ak:types", + ], +) + +go_binary( + name = "finalrjar_bin", + srcs = ["finalrjar_bin.go"], + deps = [ + ":finalrjar", + "//src/common/golang:flagfile", + ], +) + +go_test( + name = "finalrjar_test", + size = "small", + srcs = ["finalrjar_test.go"], + embed = [":finalrjar"], + deps = ["@com_github_google_go_cmp//cmp:go_default_library"], +) diff --git a/src/tools/ak/finalrjar/finalrjar.go b/src/tools/ak/finalrjar/finalrjar.go new file mode 100644 index 0000000..4addc3a --- /dev/null +++ b/src/tools/ak/finalrjar/finalrjar.go @@ -0,0 +1,451 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package finalrjar generates a valid final R.jar. +package finalrjar + +import ( + "archive/zip" + "bufio" + "flag" + "fmt" + "io" + "log" + "os" + "os/exec" + "path/filepath" + "sort" + "strings" + "sync" + + "src/common/golang/ziputils" + "src/tools/ak/types" +) + +var ( + // Cmd defines the command. + Cmd = types.Command{ + Init: Init, + Run: Run, + Desc: desc, + Flags: []string{"package", "r_txts", "out_r_java", "root_pkg", "jdk", "jartool", "target_label"}, + } + + // Variables to hold flag values. + pkg string + rtxts string + outputRJar string + rootPackage string + jdk string + jartool string + targetLabel string + + initOnce sync.Once + + resTypes = []string{ + "anim", + "animator", + "array", + "attr", + "^attr-private", + "bool", + "color", + "configVarying", + "dimen", + "drawable", + "fraction", + "font", + "id", + "integer", + "interpolator", + "layout", + "menu", + "mipmap", + "navigation", + "plurals", + "raw", + "string", + "style", + "styleable", + "transition", + "xml", + } + + javaReserved = map[string]bool{ + "abstract": true, + "assert": true, + "boolean": true, + "break": true, + "byte": true, + "case": true, + "catch": true, + "char": true, + "class": true, + "const": true, + "continue": true, + "default": true, + "do": true, + "double": true, + "else": true, + "enum": true, + "extends": true, + "false": true, + "final": true, + "finally": true, + "float": true, + "for": true, + "goto": true, + "if": true, + "implements": true, + "import": true, + "instanceof": true, + "int": true, + "interface": true, + "long": true, + "native": true, + "new": true, + "null": true, + "package": true, + "private": true, + "protected": true, + "public": true, + "return": true, + "short": true, + "static": true, + "strictfp": true, + "super": true, + "switch": true, + "synchronized": true, + "this": true, + "throw": true, + "throws": true, + "transient": true, + "true": true, + "try": true, + "void": true, + "volatile": true, + "while": true} +) + +type rtxtFile interface { + io.Reader + io.Closer +} + +type resource struct { + ID string + resType string + varType string +} + +func (r *resource) String() string { + return fmt.Sprintf("{%s %s %s}", r.varType, r.resType, r.ID) +} + +// Init initializes finalrjar action. +func Init() { + initOnce.Do(func() { + flag.StringVar(&pkg, "package", "", "Package for the R.jar") + flag.StringVar(&rtxts, "r_txts", "", "Comma separated list of R.txt files") + flag.StringVar(&outputRJar, "out_rjar", "", "Output R.jar path") + flag.StringVar(&rootPackage, "root_pkg", "mi.rjava", "Package to use for root R.java") + flag.StringVar(&jdk, "jdk", "", "Jdk path") + flag.StringVar(&jartool, "jartool", "", "Jartool path") + flag.StringVar(&targetLabel, "target_label", "", "The target label") + }) +} + +func desc() string { + return "finalrjar creates a platform conform R.jar from R.txt files" +} + +// Run is the entry point for finalrjar. Will exit on error. +func Run() { + if err := doWork(pkg, rtxts, outputRJar, rootPackage, jdk, jartool, targetLabel); err != nil { + log.Fatalf("error creating final R.jar: %v", err) + } +} + +func doWork(pkg, rtxts, outputRJar, rootPackage, jdk, jartool, targetLabel string) error { + pkgParts := strings.Split(pkg, ".") + // Check if the package is invalid. + if hasJavaReservedWord(pkgParts) { + return ziputils.EmptyZip(outputRJar) + } + + rtxtFiles, err := openRtxts(strings.Split(rtxts, ",")) + if err != nil { + return err + } + + resC := getIds(rtxtFiles) + // Resources need to be grouped by type to write the R.java classes. + resMap := groupResByType(resC) + + srcDir, err := os.MkdirTemp("", "rjar") + if err != nil { + return err + } + defer os.RemoveAll(srcDir) + + rJava, outRJava, err := createTmpRJava(srcDir, pkgParts) + if err != nil { + return err + } + defer outRJava.Close() + + rootPkgParts := strings.Split(rootPackage, ".") + rootRJava, outRootRJava, err := createTmpRJava(srcDir, rootPkgParts) + if err != nil { + return err + } + defer outRootRJava.Close() + + if err := writeRJavas(outRJava, outRootRJava, resMap, pkg, rootPackage); err != nil { + return err + } + + fullRJar := filepath.Join(srcDir, "R.jar") + if err := compileRJar([]string{rJava, rootRJava}, fullRJar, jdk, jartool, targetLabel); err != nil { + return err + } + + return filterZip(fullRJar, outputRJar, filepath.Join(rootPkgParts...)) +} + +func getIds(rtxtFiles []rtxtFile) <-chan *resource { + // Sending all res to the same channel, even duplicates. + resC := make(chan *resource) + var wg sync.WaitGroup + wg.Add(len(rtxtFiles)) + + for _, file := range rtxtFiles { + go func(file rtxtFile) { + defer wg.Done() + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + // Each line is in the following format: + // [int|int[]] resType resID value + // Ex: int anim abc_fade_in 0 + parts := strings.Split(line, " ") + if len(parts) < 3 { + continue + } + // Aapt2 will sometime add resources containing the char '$'. + // Those should be ignored - they are derived from an actual resource. + if strings.Contains(parts[2], "$") { + continue + } + resC <- &resource{ID: parts[2], resType: parts[1], varType: parts[0]} + } + file.Close() + }(file) + } + + go func() { + wg.Wait() + close(resC) + }() + + return resC +} + +func groupResByType(resC <-chan *resource) map[string][]*resource { + // Set of resType.ID seen to ignore duplicates from different R.txt files. + // Resources of different types can have the same ID, so we merge the values + // to get a unique string. Ex: integer.btn_background_alpa + seen := make(map[string]bool) + + // Map of resource type to list of resources. + resMap := make(map[string][]*resource) + for res := range resC { + uniqueID := fmt.Sprintf("%s.%s", res.resType, res.ID) + if _, ok := seen[uniqueID]; ok { + continue + } + seen[uniqueID] = true + resMap[res.resType] = append(resMap[res.resType], res) + } + return resMap +} + +func writeRJavas(outRJava, outRootRJava io.Writer, resMap map[string][]*resource, pkg, rootPackage string) error { + // The R.java points to the same resources ID in the root R.java. + // The root R.java uses 0 or null for simplicity and does not use final fields to avoid inlining. + // That way we can strip it from the compiled R.jar later and replace it with the real one. + rJavaWriter := bufio.NewWriter(outRJava) + rJavaWriter.WriteString(fmt.Sprintf("package %s;\n", pkg)) + rJavaWriter.WriteString("public class R {\n") + rootRJavaWriter := bufio.NewWriter(outRootRJava) + rootRJavaWriter.WriteString(fmt.Sprintf("package %s;\n", rootPackage)) + rootRJavaWriter.WriteString("public class R {\n") + + for _, resType := range resTypes { + if resources, ok := resMap[resType]; ok { + rJavaWriter.WriteString(fmt.Sprintf(" public static class %s {\n", resType)) + rootRJavaWriter.WriteString(fmt.Sprintf(" public static class %s {\n", resType)) + rootID := fmt.Sprintf("%s.R.%s.", rootPackage, resType) + + // Sorting resources before writing to class + sort.Slice(resources, func(i, j int) bool { + return resources[i].ID < resources[j].ID + }) + for _, res := range resources { + defaultValue := "0" + if res.varType == "int[]" { + defaultValue = "null" + } + rJavaWriter.WriteString(fmt.Sprintf(" public static final %s %s=%s%s;\n", res.varType, res.ID, rootID, res.ID)) + rootRJavaWriter.WriteString(fmt.Sprintf(" public static %s %s=%s;\n", res.varType, res.ID, defaultValue)) + } + rJavaWriter.WriteString(" }\n") + rootRJavaWriter.WriteString(" }\n") + } + } + rJavaWriter.WriteString("}\n") + rootRJavaWriter.WriteString("}\n") + + if err := rJavaWriter.Flush(); err != nil { + return err + } + return rootRJavaWriter.Flush() +} + +func createTmpRJava(srcDir string, pkgParts []string) (string, *os.File, error) { + pkgDir := filepath.Join(append([]string{srcDir}, pkgParts...)...) + if err := os.MkdirAll(pkgDir, 0777); err != nil { + return "", nil, err + } + file := filepath.Join(pkgDir, "R.java") + out, err := os.Create(file) + return file, out, err +} + +func openRtxts(filePaths []string) ([]rtxtFile, error) { + var rtxtFiles []rtxtFile + for _, filePath := range filePaths { + in, err := os.Open(filePath) + if err != nil { + return nil, err + } + rtxtFiles = append(rtxtFiles, in) + } + return rtxtFiles, nil + +} + +func createOuput(output string) (io.Writer, error) { + if _, err := os.Lstat(output); err == nil { + if err := os.Remove(output); err != nil { + return nil, err + } + } + if err := os.MkdirAll(filepath.Dir(output), 0777); err != nil { + return nil, err + } + + return os.Create(output) +} + +func filterZip(in, output, ignorePrefix string) error { + w, err := createOuput(output) + if err != nil { + return err + } + + zipOut := zip.NewWriter(w) + defer zipOut.Close() + + zipIn, err := zip.OpenReader(in) + if err != nil { + return err + } + defer zipIn.Close() + + for _, f := range zipIn.File { + // Ignoring the dummy root R.java. + if strings.HasPrefix(f.Name, ignorePrefix) { + continue + } + reader, err := f.Open() + if err != nil { + return err + } + if err := writeToZip(zipOut, reader, f.Name, f.Method); err != nil { + return err + } + if err := reader.Close(); err != nil { + return err + } + } + return nil +} + +func writeToZip(out *zip.Writer, in io.Reader, name string, method uint16) error { + writer, err := out.CreateHeader(&zip.FileHeader{ + Name: name, + Method: method, + }) + if err != nil { + return err + } + + if !strings.HasSuffix(name, "/") { + if _, err := io.Copy(writer, in); err != nil { + return err + } + } + return nil +} + +func compileRJar(srcs []string, rjar, jdk, jartool string, targetLabel string) error { + control, err := os.CreateTemp("", "control") + if err != nil { + return err + } + defer os.Remove(control.Name()) + + args := []string{"--javacopts", + "-source", "7", + "-target", "7", + "-nowarn", "--", "--sources"} + args = append(args, srcs...) + args = append(args, + "--strict_java_deps", "ERROR", + "--output", rjar) + if len(targetLabel) > 0 { + args = append(args, "--target_label", targetLabel) + } + if _, err := fmt.Fprint(control, strings.Join(args, "\n")); err != nil { + return err + } + if err := control.Sync(); err != nil { + return err + } + c, err := exec.Command(jdk, "-jar", jartool, fmt.Sprintf("@%s", control.Name())).CombinedOutput() + if err != nil { + return fmt.Errorf("error compiling R.jar (using command: %s): %v", c, err) + } + return nil +} + +func hasJavaReservedWord(parts []string) bool { + for _, p := range parts { + if javaReserved[p] { + return true + } + } + return false +} diff --git a/src/tools/ak/finalrjar/finalrjar_bin.go b/src/tools/ak/finalrjar/finalrjar_bin.go new file mode 100644 index 0000000..eff376c --- /dev/null +++ b/src/tools/ak/finalrjar/finalrjar_bin.go @@ -0,0 +1,29 @@ +// Copyright 2020 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// finalrjar_bin is the command line tool to create an R.jar +package main + +import ( + "flag" + + _ "src/common/golang/flagfile" + "src/tools/ak/finalrjar/finalrjar" +) + +func main() { + finalrjar.Init() + flag.Parse() + finalrjar.Run() +} diff --git a/src/tools/ak/finalrjar/finalrjar_test.go b/src/tools/ak/finalrjar/finalrjar_test.go new file mode 100644 index 0000000..2cfcf94 --- /dev/null +++ b/src/tools/ak/finalrjar/finalrjar_test.go @@ -0,0 +1,351 @@ +package finalrjar + +import ( + "bytes" + "sort" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" +) + +type fakeFile struct { + reader *strings.Reader +} + +func (f fakeFile) Read(b []byte) (int, error) { + return f.reader.Read(b) +} + +func (f fakeFile) Close() error { + return nil +} + +func TestGetIds(t *testing.T) { + tests := []struct { + name string + rtxtFiles []*strings.Reader + expectedResources []*resource + }{ + { + name: "one R.txt", + rtxtFiles: []*strings.Reader{ + strings.NewReader( + `int anim abc_fade_in 0 +int anim abc_fade_out 0 +int attr actionBarDivider 0 +int bool abc_action_bar_embed_tabs 0 +int color abc_background_cache_hint_selector_material_dark 0 +int[] color abc_background_cache_hint_selector_material_light 0 +int color abc_btn_colored_borderless_text_material 0 +int dimen tooltip_y_offset_non_touch 0 +int dimen $avd_hide_password__0 0 +int[] dimen tooltip_y_offset_touch 0 +int drawable abc_ab_share_pack_mtrl_alpha 0`), + }, + expectedResources: []*resource{ + &resource{ID: "abc_ab_share_pack_mtrl_alpha", resType: "drawable", varType: "int"}, + &resource{ID: "abc_action_bar_embed_tabs", resType: "bool", varType: "int"}, + &resource{ID: "abc_background_cache_hint_selector_material_dark", resType: "color", varType: "int"}, + &resource{ID: "abc_background_cache_hint_selector_material_light", resType: "color", varType: "int[]"}, + &resource{ID: "abc_btn_colored_borderless_text_material", resType: "color", varType: "int"}, + &resource{ID: "abc_fade_in", resType: "anim", varType: "int"}, + &resource{ID: "abc_fade_out", resType: "anim", varType: "int"}, + &resource{ID: "actionBarDivider", resType: "attr", varType: "int"}, + &resource{ID: "tooltip_y_offset_non_touch", resType: "dimen", varType: "int"}, + &resource{ID: "tooltip_y_offset_touch", resType: "dimen", varType: "int[]"}, + }, + }, + { + name: "multiple R.txt files", + rtxtFiles: []*strings.Reader{ + strings.NewReader( + `int styleable toolbar_logo 0 +int[] style widget_appcompat_dark 0`), + strings.NewReader( + `int layout custom_dialog 0 +int interpolator btn_checkbox 0`), + strings.NewReader( + `int id view_tree 0 +int integer cancel_button_image_alpha 0`), + }, + expectedResources: []*resource{ + &resource{ID: "btn_checkbox", resType: "interpolator", varType: "int"}, + &resource{ID: "cancel_button_image_alpha", resType: "integer", varType: "int"}, + &resource{ID: "custom_dialog", resType: "layout", varType: "int"}, + &resource{ID: "toolbar_logo", resType: "styleable", varType: "int"}, + &resource{ID: "view_tree", resType: "id", varType: "int"}, + &resource{ID: "widget_appcompat_dark", resType: "style", varType: "int[]"}, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + rtxts := make([]rtxtFile, 0, len(tc.rtxtFiles)) + for _, f := range tc.rtxtFiles { + file := fakeFile{reader: f} + file.reader.Seek(0, 0) + rtxts = append(rtxts, file) + } + + resC := getIds(rtxts) + receivedResources := make([]*resource, 0) + for res := range resC { + receivedResources = append(receivedResources, res) + } + sort.Slice(receivedResources, func(i, j int) bool { + return receivedResources[i].ID < receivedResources[j].ID + }) + + if diff := cmp.Diff(tc.expectedResources, receivedResources, cmp.AllowUnexported(resource{})); diff != "" { + t.Errorf("getIds(%v) returned diff (-want, +got):\n%v", rtxts, diff) + } + }) + } + +} + +func TestSortResByType(t *testing.T) { + tests := []struct { + name string + resources []*resource + expectedMap map[string][]*resource + }{ + { + name: "simple list of resources", + resources: []*resource{ + &resource{ID: "btn_checkbox", resType: "interpolator", varType: "int"}, + &resource{ID: "cancel_button_image_alpha", resType: "integer", varType: "int"}, + &resource{ID: "custom_dialog", resType: "id", varType: "int"}, + &resource{ID: "toolbar_logo", resType: "interpolator", varType: "int"}, + &resource{ID: "view_tree", resType: "id", varType: "int"}, + &resource{ID: "widget_appcompat_dark", resType: "layout", varType: "int[]"}, + }, + expectedMap: map[string][]*resource{ + "interpolator": []*resource{ + &resource{ID: "btn_checkbox", resType: "interpolator", varType: "int"}, + &resource{ID: "toolbar_logo", resType: "interpolator", varType: "int"}, + }, + "integer": []*resource{ + &resource{ID: "cancel_button_image_alpha", resType: "integer", varType: "int"}, + }, + "id": []*resource{ + &resource{ID: "custom_dialog", resType: "id", varType: "int"}, + &resource{ID: "view_tree", resType: "id", varType: "int"}, + }, + "layout": []*resource{ + &resource{ID: "widget_appcompat_dark", resType: "layout", varType: "int[]"}, + }, + }, + }, + { + name: "list of resources with duplicates", + resources: []*resource{ + &resource{ID: "btn_checkbox", resType: "interpolator", varType: "int"}, + &resource{ID: "btn_checkbox", resType: "interpolator", varType: "int"}, + &resource{ID: "cancel_button_image_alpha", resType: "integer", varType: "int"}, + &resource{ID: "custom_dialog", resType: "id", varType: "int"}, + &resource{ID: "toolbar_logo", resType: "interpolator", varType: "int"}, + &resource{ID: "toolbar_logo", resType: "attr", varType: "int"}, + &resource{ID: "view_tree", resType: "id", varType: "int"}, + &resource{ID: "cancel_button_image_alpha", resType: "integer", varType: "int"}, + &resource{ID: "widget_appcompat_dark", resType: "layout", varType: "int[]"}, + }, + expectedMap: map[string][]*resource{ + "attr": []*resource{ + &resource{ID: "toolbar_logo", resType: "attr", varType: "int"}, + }, + "interpolator": []*resource{ + &resource{ID: "btn_checkbox", resType: "interpolator", varType: "int"}, + &resource{ID: "toolbar_logo", resType: "interpolator", varType: "int"}, + }, + "integer": []*resource{ + &resource{ID: "cancel_button_image_alpha", resType: "integer", varType: "int"}, + }, + "id": []*resource{ + &resource{ID: "custom_dialog", resType: "id", varType: "int"}, + &resource{ID: "view_tree", resType: "id", varType: "int"}, + }, + "layout": []*resource{ + &resource{ID: "widget_appcompat_dark", resType: "layout", varType: "int[]"}, + }, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + resC := make(chan *resource) + go func() { + for _, res := range tc.resources { + resC <- res + } + close(resC) + }() + resMap := groupResByType(resC) + + if diff := cmp.Diff(tc.expectedMap, resMap, cmp.AllowUnexported(resource{})); diff != "" { + t.Errorf("groupResByType(%v) returned diff (-want, +got):\n%v", tc.resources, diff) + } + }) + } + +} + +func TestWriteRJavas(t *testing.T) { + tests := []struct { + name string + resMap map[string][]*resource + pkg string + rootPackage string + expectedRJava string + expectedRootRJava string + }{ + { + name: "simple map of resources", + resMap: map[string][]*resource{ + "interpolator": []*resource{ + &resource{ID: "btn_checkbox", resType: "interpolator", varType: "int"}, + &resource{ID: "toolbar_logo", resType: "interpolator", varType: "int"}, + }, + "integer": []*resource{ + &resource{ID: "cancel_button_image_alpha", resType: "integer", varType: "int"}, + }, + "id": []*resource{ + &resource{ID: "view_tree", resType: "id", varType: "int"}, + &resource{ID: "custom_dialog", resType: "id", varType: "int"}, + }, + "layout": []*resource{ + &resource{ID: "widget_appcompat_dark", resType: "layout", varType: "int[]"}, + }, + }, + pkg: "com.google.android.apps.sample", + rootPackage: "mi.rjava", + expectedRJava: `package com.google.android.apps.sample; +public class R { + public static class id { + public static final int custom_dialog=mi.rjava.R.id.custom_dialog; + public static final int view_tree=mi.rjava.R.id.view_tree; + } + public static class integer { + public static final int cancel_button_image_alpha=mi.rjava.R.integer.cancel_button_image_alpha; + } + public static class interpolator { + public static final int btn_checkbox=mi.rjava.R.interpolator.btn_checkbox; + public static final int toolbar_logo=mi.rjava.R.interpolator.toolbar_logo; + } + public static class layout { + public static final int[] widget_appcompat_dark=mi.rjava.R.layout.widget_appcompat_dark; + } +} +`, + expectedRootRJava: `package mi.rjava; +public class R { + public static class id { + public static int custom_dialog=0; + public static int view_tree=0; + } + public static class integer { + public static int cancel_button_image_alpha=0; + } + public static class interpolator { + public static int btn_checkbox=0; + public static int toolbar_logo=0; + } + public static class layout { + public static int[] widget_appcompat_dark=null; + } +} +`, + }, + { + name: "with empty class", + resMap: map[string][]*resource{ + "interpolator": []*resource{ + &resource{ID: "toolbar_logo", resType: "interpolator", varType: "int"}, + &resource{ID: "btn_checkbox", resType: "interpolator", varType: "int"}, + }, + "integer": []*resource{ + &resource{ID: "cancel_button_image_alpha", resType: "integer", varType: "int"}, + }, + "layout": []*resource{ + &resource{ID: "widget_appcompat_dark", resType: "layout", varType: "int[]"}, + }, + }, + pkg: "com.google.android.apps.empty", + rootPackage: "mi.rjava", + expectedRJava: `package com.google.android.apps.empty; +public class R { + public static class integer { + public static final int cancel_button_image_alpha=mi.rjava.R.integer.cancel_button_image_alpha; + } + public static class interpolator { + public static final int btn_checkbox=mi.rjava.R.interpolator.btn_checkbox; + public static final int toolbar_logo=mi.rjava.R.interpolator.toolbar_logo; + } + public static class layout { + public static final int[] widget_appcompat_dark=mi.rjava.R.layout.widget_appcompat_dark; + } +} +`, + expectedRootRJava: `package mi.rjava; +public class R { + public static class integer { + public static int cancel_button_image_alpha=0; + } + public static class interpolator { + public static int btn_checkbox=0; + public static int toolbar_logo=0; + } + public static class layout { + public static int[] widget_appcompat_dark=null; + } +} +`, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var rJavaBuffer bytes.Buffer + var rootRJavaBuffer bytes.Buffer + if err := writeRJavas(&rJavaBuffer, &rootRJavaBuffer, tc.resMap, tc.pkg, tc.rootPackage); err != nil { + t.Fatalf("writeRJavas(%v, %s, %s) unexpected error: %v", tc.resMap, tc.pkg, tc.rootPackage, err) + } + if diff := cmp.Diff(tc.expectedRJava, rJavaBuffer.String()); diff != "" { + t.Errorf("writeRJavas(%v, %s, %s) returned diff for R.java (-want, +got):\n%v", tc.resMap, tc.pkg, tc.rootPackage, diff) + } + if diff := cmp.Diff(tc.expectedRootRJava, rootRJavaBuffer.String()); diff != "" { + t.Errorf("writeRJavas(%v, %s, %s) returned diff for root R.java(-want, +got):\n%v", tc.resMap, tc.pkg, tc.rootPackage, diff) + } + }) + } + +} + +func TestHasReservedKeywords(t *testing.T) { + tests := []struct { + name string + pkg string + expected bool + }{ + { + name: "valid package", + pkg: "com.google.android.apps.sampleapp.lib", + expected: false, + }, + { + name: "valid package", + pkg: "com.google.android.static.sampleapp.lib", + expected: true, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + pkgParts := strings.Split(tc.pkg, ".") + invalid := hasJavaReservedWord(pkgParts) + if invalid != tc.expected { + t.Errorf("hasJavaReservedWord(%v) returned %v, want %v", pkgParts, invalid, tc.expected) + } + }) + } + +} diff --git a/src/tools/ak/generatemanifest/BUILD b/src/tools/ak/generatemanifest/BUILD new file mode 100644 index 0000000..2da8e4a --- /dev/null +++ b/src/tools/ak/generatemanifest/BUILD @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +# Package for manifest generation module +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +go_binary( + name = "generatemanifest_bin", + srcs = ["generatemanifest_bin.go"], + deps = [ + ":generatemanifest", + "//src/common/golang:flagfile", + ], +) + +go_library( + name = "generatemanifest", + srcs = ["generatemanifest.go"], + importpath = "src/tools/ak/generatemanifest/generatemanifest", + deps = [ + "//src/common/golang:flags", + "//src/tools/ak:types", + ], +) + +go_test( + name = "generatemanifest_test", + size = "small", + srcs = ["generatemanifest_test.go"], + embed = [":generatemanifest"], + deps = ["@com_github_google_go_cmp//cmp:go_default_library"], +) diff --git a/src/tools/ak/generatemanifest/generatemanifest.go b/src/tools/ak/generatemanifest/generatemanifest.go new file mode 100644 index 0000000..a42a227 --- /dev/null +++ b/src/tools/ak/generatemanifest/generatemanifest.go @@ -0,0 +1,188 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package generatemanifest is a command line tool to generate an empty AndroidManifest +package generatemanifest + +import ( + "bufio" + "encoding/xml" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "strconv" + "sync" + + "src/common/golang/flags" + "src/tools/ak/types" +) + +// Structs used for reading the manifest xml file +type manifestTag struct { + XMLName xml.Name `xml:"manifest"` + UsesSdk usesSdkTag `xml:"uses-sdk"` +} + +type usesSdkTag struct { + XMLName xml.Name `xml:"uses-sdk"` + MinSdk string `xml:"minSdkVersion,attr"` +} + +type result struct { + minSdk int + err error +} + +const manifestContent string = ` + + + + +` + +var ( + // Cmd defines the command to run + Cmd = types.Command{ + Init: Init, + Run: Run, + Desc: desc, + Flags: []string{ + "out", + "java_package", + "manifests", + "minsdk", + }, + } + + // Flag variables + out, javaPackage string + minSdk int + manifests flags.StringList + + initOnce sync.Once +) + +// Init initializes manifest flags +func Init() { + initOnce.Do(func() { + flag.StringVar(&out, "out", "", "Path to output manifest generated with the max min sdk value found from --manifests.") + flag.StringVar(&javaPackage, "java_package", "com.default", "(optional) Java package to use for the manifest.") + flag.IntVar(&minSdk, "minsdk", 14, "(optional) Default min sdk to support.") + flag.Var(&manifests, "manifests", "(optional) Manifests(s) to get min sdk from.") + }) +} + +func desc() string { + return "Generates an empty AndroidManifest.xml with a minSdk value. The min sdk is selected " + + "by taking the max value found between the manifests and the minsdk flag." +} + +// Run is the main entry point +func Run() { + if out == "" { + log.Fatal("Missing required flag. Must specify --out") + } + + var manifestFiles []io.ReadCloser + for _, manifest := range manifests { + manifestFile, err := os.Open(manifest) + if err != nil { + log.Fatalf("error opening manifest %s: %v", manifest, err) + } + manifestFiles = append(manifestFiles, manifestFile) + } + defer func(manifestFiles []io.ReadCloser) { + for _, manifestFile := range manifestFiles { + manifestFile.Close() + } + }(manifestFiles) + + extractedMinSdk, err := extractMinSdk(manifestFiles, minSdk) + if err != nil { + log.Fatalf("error extracting min sdk from manifests: %v", err) + } + + outFile, err := os.Create(out) + if err != nil { + log.Fatalf("error opening output manifest: %v", err) + } + defer outFile.Close() + if err := writeManifest(outFile, javaPackage, extractedMinSdk); err != nil { + log.Fatalf("error writing output manifest: %v", err) + } +} + +// The min sdk is selected by taking the max value found +// between the manifests and the minsdk flag +func extractMinSdk(manifests []io.ReadCloser, defaultSdk int) (int, error) { + // Extracting minSdk values in goroutines + results := make(chan result, len(manifests)) + var wg sync.WaitGroup + wg.Add(len(manifests)) + for _, manifestFile := range manifests { + go func(manifestFile io.Reader) { + res := extractMinSdkFromManifest(manifestFile) + results <- res + wg.Done() + }(manifestFile) + } + wg.Wait() + close(results) + + // Finding max value from channel + minSdk := defaultSdk + for result := range results { + if result.err != nil { + return 0, result.err + } + minSdk = max(minSdk, result.minSdk) + } + return minSdk, nil +} + +func extractMinSdkFromManifest(reader io.Reader) result { + manifestBytes, err := ioutil.ReadAll(reader) + if err != nil { + return result{minSdk: 0, err: err} + } + usesSdk := usesSdkTag{MinSdk: ""} + manifest := manifestTag{UsesSdk: usesSdk} + if err := xml.Unmarshal(manifestBytes, &manifest); err != nil { + return result{minSdk: 0, err: err} + } + + // MinSdk value could be a placeholder, we ignore it if that's the case + value, err := strconv.Atoi(manifest.UsesSdk.MinSdk) + if err != nil { + return result{minSdk: 0, err: nil} + } + return result{minSdk: value, err: nil} +} + +func writeManifest(outManifest io.Writer, javaPackage string, minSdk int) error { + manifestWriter := bufio.NewWriter(outManifest) + manifestWriter.WriteString(fmt.Sprintf(manifestContent, javaPackage, minSdk)) + return manifestWriter.Flush() +} + +func max(a, b int) int { + if a < b { + return b + } + return a +} diff --git a/src/tools/ak/generatemanifest/generatemanifest_bin.go b/src/tools/ak/generatemanifest/generatemanifest_bin.go new file mode 100644 index 0000000..668a42a --- /dev/null +++ b/src/tools/ak/generatemanifest/generatemanifest_bin.go @@ -0,0 +1,29 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// generatemanifest_bin is a command line tool to generate an empty AndroidManifest from dependencies +package main + +import ( + "flag" + + _ "src/common/golang/flagfile" + "src/tools/ak/generatemanifest/generatemanifest" +) + +func main() { + generatemanifest.Init() + flag.Parse() + generatemanifest.Run() +} diff --git a/src/tools/ak/generatemanifest/generatemanifest_test.go b/src/tools/ak/generatemanifest/generatemanifest_test.go new file mode 100644 index 0000000..ade3d4e --- /dev/null +++ b/src/tools/ak/generatemanifest/generatemanifest_test.go @@ -0,0 +1,220 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package generatemanifest is a command line tool to generate an empty AndroidManifest +package generatemanifest + +import ( + "io" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" +) + +type fakeFile struct { + reader *strings.Reader +} + +func (f fakeFile) Read(b []byte) (int, error) { + return f.reader.Read(b) +} + +func (f fakeFile) Close() error { + return nil +} + +func TestExtractMinSdk(t *testing.T) { + tests := []struct { + name string + manifests []*strings.Reader + defaultMinSdk int + expectedMinSdk int + }{ + { + name: "one manifest", + manifests: []*strings.Reader{ + strings.NewReader( + ` + + +`)}, + defaultMinSdk: 14, + expectedMinSdk: 20, + }, + { + name: "one manifest, lower then default min sdk", + manifests: []*strings.Reader{ + strings.NewReader( + ` + + +`)}, + defaultMinSdk: 30, + expectedMinSdk: 30, + }, + { + name: "multiple manifests", + manifests: []*strings.Reader{ + strings.NewReader( + ` + + +`), + strings.NewReader( + ` + + +`), + strings.NewReader( + ` + + +`), + }, + defaultMinSdk: 14, + expectedMinSdk: 30, + }, + { + name: "multiple manifests, all lower than default min sdk", + manifests: []*strings.Reader{ + strings.NewReader( + ` + + +`), + strings.NewReader( + ` + + +`), + strings.NewReader( + ` + + +`), + }, + defaultMinSdk: 4, + expectedMinSdk: 4, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + files := make([]io.ReadCloser, 0, len(tc.manifests)) + for _, f := range tc.manifests { + file := fakeFile{reader: f} + file.reader.Seek(0, 0) + files = append(files, file) + } + minSdk, err := extractMinSdk(files, tc.defaultMinSdk) + if err != nil { + t.Fatalf("extractMinSdk(%v, %d) failed with err: %v", files, tc.defaultMinSdk, err) + } + if diff := cmp.Diff(tc.expectedMinSdk, minSdk); diff != "" { + t.Errorf("extractMinSdkFromManifest(%v) returned diff (-want, +got):\n%v", files, diff) + } + }) + } + +} + +func TestExtractMinSdkFromManifest(t *testing.T) { + tests := []struct { + name string + manifest *strings.Reader + expectedMinSdk int + }{ + { + name: "minimal manifest", + manifest: strings.NewReader( + ` + + + +`), + expectedMinSdk: 1, + }, + { + name: "manifest with placeholder", + manifest: strings.NewReader( + ` + + + +`), + expectedMinSdk: 0, + }, + { + name: "empty manifest", + manifest: strings.NewReader( + ` + +`), + expectedMinSdk: 0, + }, + { + name: "manifest with various elements", + manifest: strings.NewReader( + ` + + + + + + + + + + +`), + expectedMinSdk: 25, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + file := fakeFile{reader: tc.manifest} + file.reader.Seek(0, 0) + results := make(chan result) + go func(results chan result) { + res := extractMinSdkFromManifest(file) + results <- res + }(results) + result := <-results + if result.err != nil { + t.Fatalf("extractMinSdkFromManifest(%v) failed with err: %v", file, result.err) + } + if diff := cmp.Diff(tc.expectedMinSdk, result.minSdk); diff != "" { + t.Errorf("extractMinSdkFromManifest(%v) returned diff (-want, +got):\n%v", file, diff) + } + }) + } + +} diff --git a/src/tools/ak/rjar/BUILD b/src/tools/ak/rjar/BUILD new file mode 100644 index 0000000..3928909 --- /dev/null +++ b/src/tools/ak/rjar/BUILD @@ -0,0 +1,44 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +# Description: +# Package for R.jar module +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +go_library( + name = "rjar", + srcs = ["rjar.go"], + importpath = "src/tools/ak/rjar/rjar", + deps = [ + "//src/common/golang:ziputils", + "//src/tools/ak:types", + ], +) + +go_binary( + name = "rjar_bin", + srcs = ["rjar_bin.go"], + deps = [ + ":rjar", + "//src/common/golang:flagfile", + ], +) + +go_test( + name = "rjar_test", + size = "small", + srcs = ["rjar_test.go"], + data = [ + "//src/tools/ak/rjar/testdata:R", + "//src/tools/ak/rjar/testdata:pkgs", + "@local_jdk//:bin/java", + "@remote_java_tools_for_rules_android//:java_tools/JavaBuilder_deploy.jar", + ], + embed = [":rjar"], + tags = [ + "manual", + "nozapfhahn", + ], +) diff --git a/src/tools/ak/rjar/rjar.go b/src/tools/ak/rjar/rjar.go new file mode 100644 index 0000000..5031c54 --- /dev/null +++ b/src/tools/ak/rjar/rjar.go @@ -0,0 +1,295 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package rjar generated R.jar. +package rjar + +import ( + "bufio" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + "sync" + + "src/common/golang/ziputils" + "src/tools/ak/types" +) + +var ( + // Cmd defines the command. + Cmd = types.Command{ + Init: Init, + Run: Run, + Desc: desc, + Flags: []string{"rjava", "pkgs", "rjar", "jdk", "jartool", "target_label"}, + } + + // Variables to hold flag values. + rjava string + pkgs string + rjar string + jdk string + jartool string + targetLabel string + + initOnce sync.Once + + javaReserved = map[string]bool{ + "abstract": true, + "assert": true, + "boolean": true, + "break": true, + "byte": true, + "case": true, + "catch": true, + "char": true, + "class": true, + "const": true, + "continue": true, + "default": true, + "do": true, + "double": true, + "else": true, + "enum": true, + "extends": true, + "false": true, + "final": true, + "finally": true, + "float": true, + "for": true, + "goto": true, + "if": true, + "implements": true, + "import": true, + "instanceof": true, + "int": true, + "interface": true, + "long": true, + "native": true, + "new": true, + "null": true, + "package": true, + "private": true, + "protected": true, + "public": true, + "return": true, + "short": true, + "static": true, + "strictfp": true, + "super": true, + "switch": true, + "synchronized": true, + "this": true, + "throw": true, + "throws": true, + "transient": true, + "true": true, + "try": true, + "void": true, + "volatile": true, + "while": true} +) + +// Init initiailizes rjar action. Must be called before google.Init. +func Init() { + initOnce.Do(func() { + flag.StringVar(&rjava, "rjava", "", "Input R.java path") + flag.StringVar(&pkgs, "pkgs", "", "Packages file path") + flag.StringVar(&rjar, "rjar", "", "Output R.jar path") + flag.StringVar(&jdk, "jdk", "", "Jdk path") + flag.StringVar(&jartool, "jartool", "", "Jartool path") + flag.StringVar(&targetLabel, "target_label", "", "The target label") + }) +} + +func desc() string { + return "rjar creates the R.jar" +} + +// Run is the entry point for rjar. Will exit on error. +func Run() { + if err := doWork(rjava, pkgs, rjar, jdk, jartool, targetLabel); err != nil { + log.Fatalf("Error creating R.jar: %v", err) + } +} + +func doWork(rjava, pkgs, rjar, jdk, jartool string, targetLabel string) error { + f, err := os.Stat(rjava) + if os.IsNotExist(err) || (err == nil && f.Size() == 0) { + // If we don't have an input r_java or have an empty r_java just write + // an empty jar apps might not define resources and in some cases (aar + // files) its not possible to know during analysis phase, so this action + // gets executed regardless. + return ziputils.EmptyZip(rjar) + } + if err != nil { + return fmt.Errorf("os.Stat(%s) failed: %v", rjava, err) + } + + srcDir, err := ioutil.TempDir("", "rjar") + if err != nil { + return err + } + defer os.RemoveAll(srcDir) + + var parentPkg, subclassTmpl string + var srcs []string + + filteredPkgs, err := getPkgs(pkgs) + if err != nil { + return err + } + for _, pkg := range filteredPkgs { + pkgParts := strings.Split(pkg, ".") + if hasInvalid(pkgParts) { + continue + } + pkgDir := filepath.Join(append([]string{srcDir}, pkgParts...)...) + err = os.MkdirAll(pkgDir, 0777) + if err != nil { + return err + } + outRJava := filepath.Join(pkgDir, "R.java") + srcs = append(srcs, outRJava) + if parentPkg == "" { + parentPkg = pkg + var classes []string + out, err := os.Create(outRJava) + if err != nil { + return err + } + defer out.Close() + in, err := os.Open(rjava) + if err != nil { + return err + } + defer in.Close() + if _, err := fmt.Fprintf(out, "package %s;", pkg); err != nil { + return err + } + if _, err := io.Copy(out, in); err != nil { + return err + } + if _, err := in.Seek(0, 0); err != nil { + return err + } + scanner := bufio.NewScanner(in) + for scanner.Scan() { + line := scanner.Text() + if strings.Contains(line, "public static class ") { + classes = append(classes, strings.Split(strings.Split(line, "public static class ")[1], " ")[0]) + } + } + subclassPts := []string{"package %s;", fmt.Sprintf("public class R extends %s.R {", pkg)} + for _, t := range classes { + subclassPts = append(subclassPts, fmt.Sprintf(" public static class %s extends %s.R.%s {}", t, pkg, t)) + } + subclassPts = append(subclassPts, "}") + subclassTmpl = strings.Join(subclassPts, "\n") + } else { + out, err := os.Create(outRJava) + if err != nil { + return err + } + defer out.Close() + fmt.Fprintf(out, subclassTmpl, pkg) + } + } + if _, err := os.Lstat(rjar); err == nil { + if err := os.Remove(rjar); err != nil { + return err + } + } + if err = os.MkdirAll(filepath.Dir(rjar), 0777); err != nil { + return err + } + return compileRJar(srcs, rjar, jdk, jartool, targetLabel) +} + +func compileRJar(srcs []string, rjar, jdk, jartool string, targetLabel string) error { + control, err := ioutil.TempFile("", "control") + if err != nil { + return err + } + defer os.Remove(control.Name()) + + args := []string{"--javacopts", + "-source", "7", + "-target", "7", + "-nowarn", "--", "--sources"} + args = append(args, srcs...) + args = append(args, []string{ + "--strict_java_deps", "ERROR", + "--output", rjar, + }...) + if len(targetLabel) > 0 { + args = append(args, []string{ + "--target_label", targetLabel, + }...) + } + if _, err := fmt.Fprint(control, strings.Join(args, "\n")); err != nil { + return err + } + if err := control.Sync(); err != nil { + return err + } + c, err := exec.Command(jdk, "-jar", jartool, fmt.Sprintf("@%s", control.Name())).CombinedOutput() + if err != nil { + return fmt.Errorf("%v:\n%s", err, c) + } + return nil +} + +func getPkgs(pkgs string) ([]string, error) { + var filteredPkgs []string + seenPkgs := map[string]bool{} + + f, err := os.Open(pkgs) + if err != nil { + return nil, err + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + pkg := strings.TrimSpace(scanner.Text()) + if strings.ContainsAny(pkg, "-$/") || pkg == "" { + continue + } + if seenPkgs[pkg] { + continue + } + filteredPkgs = append(filteredPkgs, pkg) + seenPkgs[pkg] = true + } + if err := scanner.Err(); err != nil { + return nil, err + } + return filteredPkgs, nil +} + +func hasInvalid(parts []string) bool { + for _, p := range parts { + if javaReserved[p] { + return true + } + } + return false +} diff --git a/src/tools/ak/rjar/rjar_bin.go b/src/tools/ak/rjar/rjar_bin.go new file mode 100644 index 0000000..11557f4 --- /dev/null +++ b/src/tools/ak/rjar/rjar_bin.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// rjar_bin is the command line tool to create an R.jar +package main + +import ( + "flag" + + _ "src/common/golang/flagfile" + "src/tools/ak/rjar/rjar" +) + +func main() { + rjar.Init() + flag.Parse() + rjar.Run() +} diff --git a/src/tools/ak/rjar/rjar_test.go b/src/tools/ak/rjar/rjar_test.go new file mode 100644 index 0000000..8c29650 --- /dev/null +++ b/src/tools/ak/rjar/rjar_test.go @@ -0,0 +1,82 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rjar + +import ( + "archive/zip" + "io/ioutil" + "os" + "path" + "path/filepath" + "testing" +) + +var ( + expectedClasses = []string{"R.class", "R$attr.class", "R$id.class", "R$layout.class", "R$string.class"} +) + +const ( + java = "local_jdk/bin/java" + testDataBase = "build_bazel_rules_android/src/tools/ak/rjar/testdata" +) + +func TestCreateRJar(t *testing.T) { + tmpDir, err := ioutil.TempDir("", "rjartest") + if err != nil { + t.Fatalf("Error creating temp directory: %v", err) + } + defer os.RemoveAll(tmpDir) + + out := filepath.Join(tmpDir, "R.jar") + jarDexer := path.Join(os.Getenv("TEST_SRCDIR"), "remote_java_tools_for_rules_android/java_tools/JavaBuilder_deploy.jar") + inJava := dataPath("R.java") + pkgs := dataPath("pkgs.txt") + targetLabel := "//test:test" + + if err := doWork(inJava, pkgs, out, path.Join(os.Getenv("TEST_SRCDIR"), java), jarDexer, targetLabel); err != nil { + t.Fatalf("Error creating R.jar: %v", err) + } + + z, err := zip.OpenReader(out) + if err != nil { + t.Fatalf("Error opening output jar: %v", err) + } + defer z.Close() + + for _, class := range expectedClasses { + if !zipContains(z, filepath.Join("android/support/v7", class)) { + t.Errorf("R.jar does not contain %s", filepath.Join("android/support/v7", class)) + } + if !zipContains(z, filepath.Join("com/google/android/samples/skeletonapp", class)) { + t.Errorf("R.jar does not contain %s", filepath.Join("com/google/android/samples/skeletonapp", class)) + } + if zipContains(z, filepath.Join("com/google/android/package/test", class)) { + t.Errorf("R.jar contains %s", filepath.Join("com/google/android/package/test", class)) + } + } +} + +func dataPath(fn string) string { + return filepath.Join(os.Getenv("TEST_SRCDIR"), testDataBase, fn) +} + +func zipContains(z *zip.ReadCloser, fn string) bool { + for _, f := range z.File { + if fn == f.Name { + return true + } + } + return false +} diff --git a/src/tools/ak/rjar/testdata/BUILD b/src/tools/ak/rjar/testdata/BUILD new file mode 100644 index 0000000..f4831ba --- /dev/null +++ b/src/tools/ak/rjar/testdata/BUILD @@ -0,0 +1,26 @@ +# Creates test data for testing the rjar action. + +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +package(default_visibility = ["//src/tools/ak/rjar:__subpackages__"]) + +licenses(["notice"]) + +genrule( + name = "pkgs", + outs = ["pkgs.txt"], + cmd = "printf 'android.support.v7\ncom.google.android.samples.skeletonapp\ncom.google.android.package.test' > '$@'", +) + +genrule( + name = "R", + srcs = ["//src/java/com/example/sampleapp:_migrated/lib.srcjar"], + outs = ["R.java"], + cmd = """ + unzip -p $(location //src/java/com/example/sampleapp:_migrated/lib.srcjar) com/example/sampleapp/R.java > '$@' + sed -i -- '/public final class R/,$$!d' '$@' + sed -i -- 's/@Deprecated//g' '$@' + sed -i -- 's/ final / /g' '$@' +""", +) -- cgit v1.2.3 From 56876d9f34b30b3cfebb446fc257904661254210 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Tue, 18 Oct 2022 10:58:50 -0700 Subject: Add .bazelrc and .gitignore to rules_android PiperOrigin-RevId: 481966556 Change-Id: Ie17b11959ee27080531c78bf66ff79b455a91eb9 --- .bazelrc | 2 ++ .gitignore | 1 + 2 files changed, 3 insertions(+) create mode 100644 .bazelrc create mode 100644 .gitignore diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 0000000..a7a9f55 --- /dev/null +++ b/.bazelrc @@ -0,0 +1,2 @@ +common --experimental_google_legacy_api +common --experimental_enable_android_migration_apis diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ac51a05 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +bazel-* -- cgit v1.2.3 From 12133fe1d0d9d58586cec9beea5722db20789e72 Mon Sep 17 00:00:00 2001 From: A Googler Date: Tue, 18 Oct 2022 15:33:11 -0700 Subject: Internal change PiperOrigin-RevId: 482038865 Change-Id: I4832faaf92792b66c586a139ae816e0379248e26 --- rules/android_binary_internal/attrs.bzl | 1 + rules/resources.bzl | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/rules/android_binary_internal/attrs.bzl b/rules/android_binary_internal/attrs.bzl index b9658d4..d0d2197 100644 --- a/rules/android_binary_internal/attrs.bzl +++ b/rules/android_binary_internal/attrs.bzl @@ -50,6 +50,7 @@ ATTRS = _attrs.replace( allow_files = False, allow_rules = ["android_binary", "android_test"], ), + proguard_specs = attr.label_list(allow_empty = True, allow_files = True), resource_configuration_filters = attr.string_list(), densities = attr.string_list(), nocompress_extensions = attr.string_list(), diff --git a/rules/resources.bzl b/rules/resources.bzl index 014e52f..8a894c4 100644 --- a/rules/resources.bzl +++ b/rules/resources.bzl @@ -393,8 +393,11 @@ def _is_resource_shrinking_enabled( def _should_shrink_resource_cycles( use_android_resource_cycle_shrinking, - resource_shrinking_enabled): - return use_android_resource_cycle_shrinking and resource_shrinking_enabled + resource_shrinking_enabled, + has_local_proguard_specs): + return (use_android_resource_cycle_shrinking and + resource_shrinking_enabled and + has_local_proguard_specs) def _filter_multi_cpu_configuration_targets( targets): @@ -445,6 +448,7 @@ def _package( enable_manifest_merging = True, should_compile_java_srcs = True, aapt = None, + has_local_proguard_specs = False, android_jar = None, legacy_merger = None, xsltproc = None, @@ -624,6 +628,7 @@ def _package( shrink_resource_cycles = _should_shrink_resource_cycles( use_android_resource_cycle_shrinking, resource_shrinking_enabled, + has_local_proguard_specs, ) resource_apk = ctx.actions.declare_file(ctx.label.name + "_migrated/.ap_") -- cgit v1.2.3 From 8ca46e1dbe125550fa265355862ba913ce7b3fa1 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Wed, 26 Oct 2022 15:55:11 -0700 Subject: Use `-source 8 -target 8` instead of `-source 7 -target 7`, which is no longer supported PiperOrigin-RevId: 484094266 Change-Id: Ia9035afc2fc8a89483789ba38d2f9c114361df3b --- src/tools/ak/finalrjar/finalrjar.go | 4 ++-- src/tools/ak/rjar/rjar.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/ak/finalrjar/finalrjar.go b/src/tools/ak/finalrjar/finalrjar.go index 4addc3a..89d73a4 100644 --- a/src/tools/ak/finalrjar/finalrjar.go +++ b/src/tools/ak/finalrjar/finalrjar.go @@ -418,8 +418,8 @@ func compileRJar(srcs []string, rjar, jdk, jartool string, targetLabel string) e defer os.Remove(control.Name()) args := []string{"--javacopts", - "-source", "7", - "-target", "7", + "-source", "8", + "-target", "8", "-nowarn", "--", "--sources"} args = append(args, srcs...) args = append(args, diff --git a/src/tools/ak/rjar/rjar.go b/src/tools/ak/rjar/rjar.go index 5031c54..c95dea8 100644 --- a/src/tools/ak/rjar/rjar.go +++ b/src/tools/ak/rjar/rjar.go @@ -231,8 +231,8 @@ func compileRJar(srcs []string, rjar, jdk, jartool string, targetLabel string) e defer os.Remove(control.Name()) args := []string{"--javacopts", - "-source", "7", - "-target", "7", + "-source", "8", + "-target", "8", "-nowarn", "--", "--sources"} args = append(args, srcs...) args = append(args, []string{ -- cgit v1.2.3 From dc651c98ab1891c2971f21c26f7219bb4b60d917 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Fri, 28 Oct 2022 08:49:54 -0700 Subject: Update source file license headers #12 PiperOrigin-RevId: 484541771 Change-Id: Iacd95585a039aceec8a5d876eea4915ed19995b7 --- src/common/golang/pprint.go | 14 ++++++++++++++ src/java/com/example/sampleapp/native.c | 15 +++++++++++++++ src/tools/ak/finalrjar/finalrjar_test.go | 15 +++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/src/common/golang/pprint.go b/src/common/golang/pprint.go index e0d7e75..221ff0f 100644 --- a/src/common/golang/pprint.go +++ b/src/common/golang/pprint.go @@ -1,3 +1,17 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + // Package pprint provides colored "pretty print" output helper methods package pprint diff --git a/src/java/com/example/sampleapp/native.c b/src/java/com/example/sampleapp/native.c index d221826..021bfa5 100644 --- a/src/java/com/example/sampleapp/native.c +++ b/src/java/com/example/sampleapp/native.c @@ -1,3 +1,18 @@ +/* + * Copyright 2022 The Bazel Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include #include diff --git a/src/tools/ak/finalrjar/finalrjar_test.go b/src/tools/ak/finalrjar/finalrjar_test.go index 2cfcf94..fffd1b9 100644 --- a/src/tools/ak/finalrjar/finalrjar_test.go +++ b/src/tools/ak/finalrjar/finalrjar_test.go @@ -1,3 +1,18 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package finalrjar generates a valid final R.jar. package finalrjar import ( -- cgit v1.2.3 From 76f88949f62f973034035de1e21e17b67b1c1ec2 Mon Sep 17 00:00:00 2001 From: Tim Peut Date: Tue, 1 Nov 2022 11:06:17 -0700 Subject: Provide acl-based mechanism for android_archive to skip the duplicate class check. We expect that there will be a very small number of targets that need to skip this, and we will be unable to provide strong runtime guarantees if they choose to do so. PiperOrigin-RevId: 485365902 Change-Id: Ib4521a8226908c2cc73f59e27b4cd80fe093bd88 --- rules/acls.bzl | 5 +++++ .../android_archive_duplicate_class_allowlist.bzl | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 rules/acls/android_archive_duplicate_class_allowlist.bzl diff --git a/rules/acls.bzl b/rules/acls.bzl index 5c3a6b8..b038c29 100644 --- a/rules/acls.bzl +++ b/rules/acls.bzl @@ -34,6 +34,7 @@ load("//rules/acls:aar_propagate_resources.bzl", "AAR_PROPAGATE_RESOURCES_FALLBA load("//rules/acls:ait_install_snapshots.bzl", "APP_INSTALLATION_SNAPSHOT", "APP_INSTALLATION_SNAPSHOT_FALLBACK") load("//rules/acls:allow_resource_conflicts.bzl", "ALLOW_RESOURCE_CONFLICTS") load("//rules/acls:android_archive_dogfood.bzl", "ANDROID_ARCHIVE_DOGFOOD") +load("//rules/acls:android_archive_duplicate_class_allowlist.bzl", "ANDROID_ARCHIVE_DUPLICATE_CLASS_ALLOWLIST") load("//rules/acls:android_archive_excluded_deps_denylist.bzl", "ANDROID_ARCHIVE_EXCLUDED_DEPS_DENYLIST") load("//rules/acls:android_archive_exposed_package_allowlist.bzl", "ANDROID_ARCHIVE_EXPOSED_PACKAGE_ALLOWLIST") load("//rules/acls:android_test_lockdown.bzl", "ANDROID_TEST_LOCKDOWN_GENERATOR_FUNCTIONS", "ANDROID_TEST_LOCKDOWN_TARGETS") @@ -201,6 +202,9 @@ def _in_enforce_min_sdk_floor_rollout(fqn): def _in_android_apk_to_bundle_features(fqn): return matches(fqn, ANDROID_APK_TO_BUNDLE_FEATURES_DICT) +def _get_android_archive_duplicate_class_allowlist(fqn): + return ANDROID_ARCHIVE_DUPLICATE_CLASS_ALLOWLIST.get(fqn, []) + def make_dict(lst): """Do not use this method outside of acls directory.""" return {t: True for t in lst} @@ -309,6 +313,7 @@ def matches(fqn, dct): return False acls = struct( + get_android_archive_duplicate_class_allowlist = _get_android_archive_duplicate_class_allowlist, get_android_archive_exposed_package_allowlist = _get_android_archive_exposed_package_allowlist, in_aar_import_deps_checker = _in_aar_import_deps_checker, in_aar_import_explicit_exports_manifest = _in_aar_import_explicit_exports_manifest, diff --git a/rules/acls/android_archive_duplicate_class_allowlist.bzl b/rules/acls/android_archive_duplicate_class_allowlist.bzl new file mode 100644 index 0000000..a170d83 --- /dev/null +++ b/rules/acls/android_archive_duplicate_class_allowlist.bzl @@ -0,0 +1,21 @@ +# Copyright 2022 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allowlist for android_archive targets to skip duplicate class validation.""" + +# Map of {"//some:target": ["list.class", "of.class", "classes.class"]} which will be excluded from +# duplicate class validation. +# keep sorted +ANDROID_ARCHIVE_DUPLICATE_CLASS_ALLOWLIST = { +} -- cgit v1.2.3 From 58ab639bd928915fc96ea017be5a078ccd28c010 Mon Sep 17 00:00:00 2001 From: A Googler Date: Tue, 1 Nov 2022 12:24:19 -0700 Subject: Internal PiperOrigin-RevId: 485386414 Change-Id: If3fefd7f722c2925fe4c4f3e58740c3fa61477b3 --- rules/flags/flag_defs.bzl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rules/flags/flag_defs.bzl b/rules/flags/flag_defs.bzl index 0a81805..61f2cf7 100644 --- a/rules/flags/flag_defs.bzl +++ b/rules/flags/flag_defs.bzl @@ -91,3 +91,9 @@ def define_flags(): name = "stamp", description = "Accesses the native --stamp CLI flag", ) + + flags.DEFINE_bool( + name = "use_studio_deployer", + default = True, + description = "Use Studio Deployer to install apks", + ) -- cgit v1.2.3 From a90edc486eb48e831655b369535d8fdef7e50aee Mon Sep 17 00:00:00 2001 From: Joseph Park Date: Tue, 1 Nov 2022 14:06:10 -0700 Subject: Define Starlark android_binary rule suffix (_RESOURCES_DO_NOT_USE) in common.bzl and delete other definitions. PiperOrigin-RevId: 485413178 Change-Id: I2cdede738a03e296b494a64891c8e468d7c11567 --- rules/android_binary.bzl | 3 ++- rules/common.bzl | 4 ++++ rules/resources.bzl | 7 +++---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/rules/android_binary.bzl b/rules/android_binary.bzl index 3772c5e..16c7a40 100644 --- a/rules/android_binary.bzl +++ b/rules/android_binary.bzl @@ -14,6 +14,7 @@ """Bazel rule for building an APK.""" +load(":common.bzl", "common") load(":migration_tag_DONOTUSE.bzl", "add_migration_tag") load("//rules/android_binary_internal:rule.bzl", "android_binary_internal_macro") @@ -25,7 +26,7 @@ def android_binary(**attrs): Args: **attrs: Rule attributes """ - android_binary_internal_name = ":%s_RESOURCES_DO_NOT_USE" % attrs["name"] + android_binary_internal_name = ":" + attrs["name"] + common.PACKAGED_RESOURCES_SUFFIX android_binary_internal_macro( **dict( attrs, diff --git a/rules/common.bzl b/rules/common.bzl index 1cd512e..508413f 100644 --- a/rules/common.bzl +++ b/rules/common.bzl @@ -17,6 +17,9 @@ load(":utils.bzl", "get_android_toolchain", _log = "log") load("//rules/android_common:reexport_android_common.bzl", _native_android_common = "native_android_common") +# Suffix attached to the Starlark portion of android_binary target +_PACKAGED_RESOURCES_SUFFIX = "_RESOURCES_DO_NOT_USE" + # Validates that the packages listed under "deps" all have the given constraint. If a package # does not have this attribute, an error is generated. def _validate_constraints(targets, constraint): @@ -68,6 +71,7 @@ def _create_signer_properties(ctx, oldest_key): return properties common = struct( + PACKAGED_RESOURCES_SUFFIX = _PACKAGED_RESOURCES_SUFFIX, check_rule = _check_rule, create_signer_properties = _create_signer_properties, get_host_javabase = _get_host_javabase, diff --git a/rules/resources.bzl b/rules/resources.bzl index 8a894c4..86e82e0 100644 --- a/rules/resources.bzl +++ b/rules/resources.bzl @@ -690,11 +690,10 @@ def _package( packaged_resources_ctx[_PACKAGED_VALIDATION_RESULT] = resource_files_zip # Fix class jar name because some tests depend on {label_name}_resources.jar being the suffix of - # the path, with _RESOURCES_DO_NOT_USE removed from the label name. - _RESOURCES_SUFFIX = "_RESOURCES_DO_NOT_USE" + # the path, with _common.PACKAGED_RESOURCES_SUFFIX removed from the label name. class_jar_name = ctx.label.name + "_migrated/_resources.jar" - if ctx.label.name.endswith(_RESOURCES_SUFFIX): - label_name = ctx.label.name[:-len(_RESOURCES_SUFFIX)] + if ctx.label.name.endswith(_common.PACKAGED_RESOURCES_SUFFIX): + label_name = ctx.label.name.removesuffix(_common.PACKAGED_RESOURCES_SUFFIX) class_jar_name = ctx.label.name + "_migrated/" + label_name + "_resources.jar" class_jar = ctx.actions.declare_file(class_jar_name) -- cgit v1.2.3 From b098535f1dcaacd897f57f07160a7e3e830d28e5 Mon Sep 17 00:00:00 2001 From: A Googler Date: Wed, 2 Nov 2022 17:12:59 -0700 Subject: Internal change PiperOrigin-RevId: 485734683 Change-Id: I06c415736c8c30b68f41b0fe634d083f2e35feb1 --- rules/flags/flag_defs.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/flags/flag_defs.bzl b/rules/flags/flag_defs.bzl index 61f2cf7..c7240a2 100644 --- a/rules/flags/flag_defs.bzl +++ b/rules/flags/flag_defs.bzl @@ -94,6 +94,6 @@ def define_flags(): flags.DEFINE_bool( name = "use_studio_deployer", - default = True, + default = False, description = "Use Studio Deployer to install apks", ) -- cgit v1.2.3 From 67f68f42464711149819d959723a43a2785aa355 Mon Sep 17 00:00:00 2001 From: A Googler Date: Thu, 3 Nov 2022 14:14:13 -0700 Subject: Internal PiperOrigin-RevId: 485970313 Change-Id: I7b68a1c71747f69bc71d33042c13b50e5fd411f1 --- rules/flags/flag_defs.bzl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/flags/flag_defs.bzl b/rules/flags/flag_defs.bzl index c7240a2..61f2cf7 100644 --- a/rules/flags/flag_defs.bzl +++ b/rules/flags/flag_defs.bzl @@ -94,6 +94,6 @@ def define_flags(): flags.DEFINE_bool( name = "use_studio_deployer", - default = False, + default = True, description = "Use Studio Deployer to install apks", ) -- cgit v1.2.3 From a170ffcc67dc9eb9a12b4d2f1dd232a14d578c61 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Fri, 11 Nov 2022 08:36:42 -0800 Subject: OSS release of src/tools/ak/res PiperOrigin-RevId: 487831880 Change-Id: I28d2866ee3c8a0187e503816d467b67fd0b50ba1 --- WORKSPACE | 41 +++- kokoro/presubmit/kokoro_presubmit.sh | 2 +- src/tools/ak/liteparse/BUILD | 2 +- src/tools/ak/liteparse/liteparse.go | 2 +- src/tools/ak/res/BUILD | 40 +++ src/tools/ak/res/naming.go | 175 +++++++++++++ src/tools/ak/res/naming_test.go | 341 ++++++++++++++++++++++++++ src/tools/ak/res/path.go | 108 ++++++++ src/tools/ak/res/path_test.go | 249 +++++++++++++++++++ src/tools/ak/res/proto/BUILD | 32 +++ src/tools/ak/res/proto/res_data.proto | 53 ++++ src/tools/ak/res/proto/res_meta.proto | 14 ++ src/tools/ak/res/respipe/BUILD | 42 ++++ src/tools/ak/res/respipe/errors.go | 43 ++++ src/tools/ak/res/respipe/errors_test.go | 56 +++++ src/tools/ak/res/respipe/path_emitter.go | 94 +++++++ src/tools/ak/res/respipe/path_emitter_test.go | 92 +++++++ src/tools/ak/res/respipe/res_io.go | 109 ++++++++ src/tools/ak/res/respipe/res_io_test.go | 85 +++++++ src/tools/ak/res/respipe/streams.go | 119 +++++++++ src/tools/ak/res/respipe/streams_test.go | 85 +++++++ src/tools/ak/res/resxml/BUILD | 24 ++ src/tools/ak/res/resxml/xml_parser.go | 133 ++++++++++ src/tools/ak/res/resxml/xml_parser_test.go | 226 +++++++++++++++++ src/tools/ak/res/struct.go | 328 +++++++++++++++++++++++++ src/tools/ak/res/struct_test.go | 99 ++++++++ src/tools/ak/res/xml.go | 87 +++++++ 27 files changed, 2668 insertions(+), 13 deletions(-) create mode 100644 src/tools/ak/res/BUILD create mode 100644 src/tools/ak/res/naming.go create mode 100644 src/tools/ak/res/naming_test.go create mode 100644 src/tools/ak/res/path.go create mode 100644 src/tools/ak/res/path_test.go create mode 100644 src/tools/ak/res/proto/BUILD create mode 100644 src/tools/ak/res/proto/res_data.proto create mode 100644 src/tools/ak/res/proto/res_meta.proto create mode 100644 src/tools/ak/res/respipe/BUILD create mode 100644 src/tools/ak/res/respipe/errors.go create mode 100644 src/tools/ak/res/respipe/errors_test.go create mode 100644 src/tools/ak/res/respipe/path_emitter.go create mode 100644 src/tools/ak/res/respipe/path_emitter_test.go create mode 100644 src/tools/ak/res/respipe/res_io.go create mode 100644 src/tools/ak/res/respipe/res_io_test.go create mode 100644 src/tools/ak/res/respipe/streams.go create mode 100644 src/tools/ak/res/respipe/streams_test.go create mode 100644 src/tools/ak/res/resxml/BUILD create mode 100644 src/tools/ak/res/resxml/xml_parser.go create mode 100644 src/tools/ak/res/resxml/xml_parser_test.go create mode 100644 src/tools/ak/res/struct.go create mode 100644 src/tools/ak/res/struct_test.go create mode 100644 src/tools/ak/res/xml.go diff --git a/WORKSPACE b/WORKSPACE index a588f59..c691e57 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -3,6 +3,27 @@ workspace(name = "build_bazel_rules_android") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") +maybe( + http_archive, + name = "remote_java_tools_for_rules_android", + sha256 = "8fb4d3138bd92a9d3324dae29c9f70d91ca2db18cd0bf1997446eed4657d19b3", + urls = [ + "https://mirror.bazel.build/bazel_java_tools/releases/java/v11.8/java_tools-v11.8.zip", + "https://github.com/bazelbuild/java_tools/releases/download/java_v11.8/java_tools-v11.8.zip", + ], +) + +maybe( + http_archive, + name = "com_google_protobuf", + sha256 = "87407cd28e7a9c95d9f61a098a53cf031109d451a7763e7dd1253abf8b4df422", + strip_prefix = "protobuf-3.19.1", + urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.19.1.tar.gz"], +) + +load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") +protobuf_deps() + maybe( http_archive, name = "rules_jvm_external", @@ -58,12 +79,18 @@ maybe( load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") +go_rules_dependencies() + +go_register_toolchains(version = "1.18.3") + +gazelle_dependencies() +# gazelle:repository go_repository name=org_golang_x_xerrors importpath=golang.org/x/xerrors go_repository( - name = "com_github_golang_protobuf", - importpath = "github.com/golang/protobuf", - sum = "h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=", - version = "v1.5.2", + name = "org_golang_google_protobuf", + importpath = "google.golang.org/protobuf", + sum = "h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=", + version = "v1.28.1", ) go_repository( @@ -80,11 +107,5 @@ go_repository( version = "v0.0.0-20210220032951-036812b2e83c", ) -go_rules_dependencies() - -go_register_toolchains(version = "1.18.3") - -gazelle_dependencies() -# gazelle:repository go_repository name=org_golang_x_xerrors importpath=golang.org/x/xerrors rules_android_workspace() diff --git a/kokoro/presubmit/kokoro_presubmit.sh b/kokoro/presubmit/kokoro_presubmit.sh index 50ec5b5..ed6eb5f 100644 --- a/kokoro/presubmit/kokoro_presubmit.sh +++ b/kokoro/presubmit/kokoro_presubmit.sh @@ -66,7 +66,7 @@ COMMON_ARGS=( # Go to rules_android workspace and run relevant tests. cd "${KOKORO_ARTIFACTS_DIR}/git/rules_android" "$bazel" test "${COMMON_ARGS[@]}" //src/common/golang/... \ - //src/tools/ak/{compile,dex,finalrjar,generatemanifest,link,manifest,mindex,rjar}/... + //src/tools/ak/{compile,dex,finalrjar,generatemanifest,link,manifest,mindex,res,rjar}/... # Go to basic app workspace in the source tree cd "${KOKORO_ARTIFACTS_DIR}/git/rules_android/examples/basicapp" diff --git a/src/tools/ak/liteparse/BUILD b/src/tools/ak/liteparse/BUILD index 0cb8fea..619353d 100644 --- a/src/tools/ak/liteparse/BUILD +++ b/src/tools/ak/liteparse/BUILD @@ -24,7 +24,7 @@ go_library( "//src/tools/ak/res/proto:res_meta_go_proto", "//src/tools/ak/res/respipe", "//src/tools/ak/res/resxml", - "@com_github_golang_protobuf//proto", + "@org_golang_google_protobuf//proto", ], ) diff --git a/src/tools/ak/liteparse/liteparse.go b/src/tools/ak/liteparse/liteparse.go index 55f4d9a..9ab50d8 100644 --- a/src/tools/ak/liteparse/liteparse.go +++ b/src/tools/ak/liteparse/liteparse.go @@ -37,7 +37,7 @@ import ( "src/tools/ak/res/respipe/respipe" "src/tools/ak/res/resxml/resxml" "src/tools/ak/types" - "github.com/golang/protobuf/proto" + "google.golang.org/protobuf/proto" ) var ( diff --git a/src/tools/ak/res/BUILD b/src/tools/ak/res/BUILD new file mode 100644 index 0000000..6cdb6e5 --- /dev/null +++ b/src/tools/ak/res/BUILD @@ -0,0 +1,40 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +# Description: +# Package for res module +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +go_library( + name = "res", + srcs = [ + "naming.go", + "path.go", + "struct.go", + "xml.go", + ], + importpath = "src/tools/ak/res/res", + visibility = [ + "//src/tools/ak:__subpackages__", + "//src/tools/resource_extractor:__subpackages__", + "//tools/android/incremental:__subpackages__", + ], + deps = [ + "//src/tools/ak/res/proto:res_data_go_proto", + "//src/tools/ak/res/proto:res_meta_go_proto", + "@org_golang_google_protobuf//proto", + ], +) + +go_test( + name = "res_test", + size = "small", + srcs = [ + "naming_test.go", + "path_test.go", + "struct_test.go", + ], + embed = [":res"], +) diff --git a/src/tools/ak/res/naming.go b/src/tools/ak/res/naming.go new file mode 100644 index 0000000..178f740 --- /dev/null +++ b/src/tools/ak/res/naming.go @@ -0,0 +1,175 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package res + +import ( + "fmt" + "strings" + + rdpb "src/tools/ak/res/proto/res_data_go_proto" + rmpb "src/tools/ak/res/proto/res_meta_go_proto" + "google.golang.org/protobuf/proto" +) + +// FullyQualifiedName represents the components of a name. +type FullyQualifiedName struct { + Package string + Type Type + Name string +} + +// ValuesResource represents a resource element. +type ValuesResource struct { + Src *PathInfo + N FullyQualifiedName + Payload []byte +} + +// SetResource sets all the name related fields on the top level resource proto. +func (f FullyQualifiedName) SetResource(r *rdpb.Resource) error { + rt, err := f.Type.Enum() + if err != nil { + return err + } + r.ResourceType = rt + r.Name = protoNameSanitizer.Replace(f.Name) + return nil +} + +// SetMetaData sets all name related fields for this style on a StyleableMetaData proto +func (f FullyQualifiedName) SetMetaData(md *rmpb.StyleableMetaData) error { + if f.Type != Styleable { + return ErrWrongType + } + md.Name = proto.String(protoNameSanitizer.Replace(f.Name)) + return nil +} + +var ( + protoNameSanitizer = strings.NewReplacer(".", "_") + javaNameSanitizer = strings.NewReplacer(":", "_", ".", "_") +) + +// JavaName returns a version of the FullyQualifiedName that should be used for resource identifier fields. +func (f FullyQualifiedName) JavaName() (string, error) { + if !f.Type.IsReal() { + return "", ErrWrongType + } + return javaNameSanitizer.Replace(f.Name), nil +} + +// StyleableAttrName creates the java identifier for referencing this attribute in the given +// style. +func StyleableAttrName(styleable, attr FullyQualifiedName) (string, error) { + if styleable.Type != Styleable || attr.Type != Attr { + return "", ErrWrongType + } + js, err := styleable.JavaName() + if err != nil { + return "", err + } + ja, err := attr.JavaName() + if err != nil { + return "", err + } + + if attr.Package == "android" { + return fmt.Sprintf("%s_android_%s", js, ja), nil + } + return fmt.Sprintf("%s_%s", js, ja), nil +} + +// ParseName is given a name string and optional context about the name (what type the name may be) +// and attempts to extract the local name, Type, and package from the unparsed input. The format of +// unparsed names is flexible and not well specified. +// A FullyQualifiedName's String method will emit pkg:type/name which every tool understands, but +// ParseName will encounter input like ?type:pkg/name - an undocumented, but legal way to specify a +// reference to a style. If unparsed is so mangled that a legal name cannot possibly be determined, +// it will return an error. +func ParseName(unparsed string, resType Type) (FullyQualifiedName, error) { + fqn := removeRef(unparsed) + fqn.Type = resType + pkgIdx := strings.Index(fqn.Name, ":") + typeIdx := strings.Index(fqn.Name, "/") + if pkgIdx == 0 || typeIdx == 0 { + return FullyQualifiedName{}, fmt.Errorf("malformed name %q - can not start with ':' or '/'", unparsed) + } + + if typeIdx != -1 { + if pkgIdx != -1 { + if pkgIdx < typeIdx { + // Package, type and name (pkg:type/name) + t, err := ParseType(fqn.Name[pkgIdx+1 : typeIdx]) + if err != nil { + // the name has illegal type in it that we'll never be able to scrub out. + return FullyQualifiedName{}, err + } + fqn.Type = t + fqn.Package = fqn.Name[:pkgIdx] + fqn.Name = fqn.Name[typeIdx+1:] + + } else { + // Package, type and name, type and package swapped (type:pkg/name) + t, err := ParseType(fqn.Name[:typeIdx]) + if err != nil { + // the name has illegal type in it that we'll never be able to scrub out. + return FullyQualifiedName{}, err + } + fqn.Type = t + fqn.Package = fqn.Name[typeIdx+1 : pkgIdx] + fqn.Name = fqn.Name[pkgIdx+1:] + } + } else { + // Only type and name (type/name) + t, err := ParseType(fqn.Name[:typeIdx]) + if err != nil { + // the name has illegal type in it that we'll never be able to scrub out. + return FullyQualifiedName{}, err + } + fqn.Type = t + fqn.Name = fqn.Name[typeIdx+1:] + } + } else { + // Only package and name (pkg:name) + if pkgIdx != -1 { + fqn.Package = fqn.Name[:pkgIdx] + fqn.Name = fqn.Name[pkgIdx+1:] + } + } + + if fqn.Package == "" { + fqn.Package = "res-auto" + } + + if fqn.Type == UnknownType { + return FullyQualifiedName{}, fmt.Errorf("cannot determine type from %q and %v - not a valid name", unparsed, resType) + } + if fqn.Name == "" { + return FullyQualifiedName{}, fmt.Errorf("cannot determine name from %q and %v - not a valid name", unparsed, resType) + } + return fqn, nil +} + +func removeRef(unparsed string) (fqn FullyQualifiedName) { + fqn.Name = unparsed + if len(fqn.Name) > 2 && (strings.HasPrefix(fqn.Name, "@") || strings.HasPrefix(fqn.Name, "?")) { + fqn.Name = fqn.Name[1:] + } + return +} + +func (f FullyQualifiedName) String() string { + return fmt.Sprintf("%s:%s/%s", f.Package, f.Type, f.Name) +} diff --git a/src/tools/ak/res/naming_test.go b/src/tools/ak/res/naming_test.go new file mode 100644 index 0000000..550251c --- /dev/null +++ b/src/tools/ak/res/naming_test.go @@ -0,0 +1,341 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package res + +import ( + "reflect" + "strings" + "testing" +) + +func TestNaming(t *testing.T) { + tests := []struct { + unparsed string + resType Type + want FullyQualifiedName + wantErrPrefix string + }{ + { + "style/InlineProjectStyle", + ValueType, + FullyQualifiedName{ + Name: "InlineProjectStyle", + Type: Style, + Package: "res-auto", + }, + "", + }, + { + "android:style/InlineProjectStyle", + ValueType, + FullyQualifiedName{ + Name: "InlineProjectStyle", + Type: Style, + Package: "android", + }, + "", + }, + { + "@style/InlineProjectStyle", + ValueType, + FullyQualifiedName{ + Name: "InlineProjectStyle", + Type: Style, + Package: "res-auto", + }, + "", + }, + { + "@style/android:InlineProjectStyle", + ValueType, + FullyQualifiedName{ + Name: "InlineProjectStyle", + Type: Style, + Package: "android", + }, + "", + }, + { + "?style/InlineProjectStyle", + ValueType, + FullyQualifiedName{ + Name: "InlineProjectStyle", + Type: Style, + Package: "res-auto", + }, + "", + }, + { + "?style/android:InlineProjectStyle", + ValueType, + FullyQualifiedName{ + Name: "InlineProjectStyle", + Type: Style, + Package: "android", + }, + "", + }, + { + "android:style/Widget.TextView", + ValueType, + FullyQualifiedName{ + Name: "Widget.TextView", + Type: Style, + Package: "android", + }, + "", + }, + { + "@android:style/Widget.TextView", + ValueType, + FullyQualifiedName{ + Name: "Widget.TextView", + Type: Style, + Package: "android", + }, + "", + }, + { + "?android:style/Widget.TextView", + ValueType, + FullyQualifiedName{ + Name: "Widget.TextView", + Type: Style, + Package: "android", + }, + "", + }, + { + "?attr/styleReference", + ValueType, + FullyQualifiedName{ + Name: "styleReference", + Type: Attr, + Package: "res-auto", + }, + "", + }, + { + "?android:attr/textAppearance", + ValueType, + FullyQualifiedName{ + Name: "textAppearance", + Type: Attr, + Package: "android", + }, + "", + }, + { + "?attr/android:textAppearance", + ValueType, + FullyQualifiedName{ + Name: "textAppearance", + Type: Attr, + Package: "android", + }, + "", + }, + { + "@dimen/viewer:progress_bar_height", + ValueType, + FullyQualifiedName{ + Name: "progress_bar_height", + Type: Dimen, + Package: "viewer", + }, + "", + }, + { + "drawable/simple", + Drawable, + FullyQualifiedName{ + Name: "simple", + Type: Drawable, + Package: "res-auto", + }, + "", + }, + { + "android:fraction/name", + ValueType, + FullyQualifiedName{ + Name: "name", + Type: Fraction, + Package: "android", + }, + "", + }, + { + "android:style/foo:with_colon", + ValueType, + FullyQualifiedName{ + Name: "foo:with_colon", + Type: Style, + Package: "android", + }, + "", + }, + { + "color/red", + ValueType, + FullyQualifiedName{ + Name: "red", + Type: Color, + Package: "res-auto", + }, + "", + }, + { + "style/bright:with_colon", + ValueType, + FullyQualifiedName{ + Name: "with_colon", + Type: Style, + Package: "bright", + }, + "", + }, + { + "com.google.android.apps.gmoney:array/available_locales", + ValueType, + FullyQualifiedName{ + Name: "available_locales", + Type: Array, + Package: "com.google.android.apps.gmoney", + }, + "", + }, + { + "@android:string/ok", + ValueType, + FullyQualifiedName{ + Name: "ok", + Type: String, + Package: "android", + }, + "", + }, + { + "@string/android:ok", + ValueType, + FullyQualifiedName{ + Name: "ok", + Type: String, + Package: "android", + }, + "", + }, + { + "name", + String, + FullyQualifiedName{ + Package: "res-auto", + Type: String, + Name: "name", + }, + "", + }, + { + "string/name", + String, + FullyQualifiedName{ + Package: "res-auto", + Type: String, + Name: "name", + }, + "", + }, + { + "android:Theme.Material.Light", + Style, + FullyQualifiedName{ + Package: "android", + Type: Style, + Name: "Theme.Material.Light", + }, + "", + }, + { + "@android:attr/borderlessButtonStyle", + Style, + FullyQualifiedName{ + Package: "android", + Type: Attr, + Name: "borderlessButtonStyle", + }, + "", + }, + { + "@id/:packagelessId", + Style, + FullyQualifiedName{ + Package: "res-auto", + Type: ID, + Name: "packagelessId", + }, + "", + }, + { + "InlineProjectStyle", + UnknownType, + FullyQualifiedName{}, + "cannot determine type", + }, + { + "android:InlineProjectStyle", + UnknownType, + FullyQualifiedName{}, + "cannot determine type", + }, + { + "res-auto:InlineProjectStyle", + UnknownType, + FullyQualifiedName{}, + "cannot determine type", + }, + { + "style/", + ValueType, + FullyQualifiedName{}, + "cannot determine name", + }, + { + ":style/InlineProjectStyle", + ValueType, + FullyQualifiedName{}, + "malformed name", + }, + { + "/InlineProjectStyle", + ValueType, + FullyQualifiedName{}, + "malformed name", + }, + } + + for _, tc := range tests { + got, gotErr := ParseName(tc.unparsed, tc.resType) + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("ParseName(%s, %+v): got: %#v want: %#v", tc.unparsed, tc.resType, got, tc.want) + } + + if gotErr != nil && ("" == tc.wantErrPrefix || !strings.HasPrefix(gotErr.Error(), tc.wantErrPrefix)) { + t.Errorf("ParseName(%s, %+v): %v want prefix: %s", tc.unparsed, tc.resType, gotErr, tc.wantErrPrefix) + } + if gotErr == nil && "" != tc.wantErrPrefix { + t.Errorf("ParseName(%s, %+v): got no err want err prefix: %s", tc.unparsed, tc.resType, tc.wantErrPrefix) + } + } +} diff --git a/src/tools/ak/res/path.go b/src/tools/ak/res/path.go new file mode 100644 index 0000000..502dedd --- /dev/null +++ b/src/tools/ak/res/path.go @@ -0,0 +1,108 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package res + +import ( + "errors" + "fmt" + "path" + "strings" +) + +// ErrNotResPath the provided path does not seem to point to a resource file +var ErrNotResPath = errors.New("Not a resource path") + +// ErrSkipResPath the provided path does needs to be skipped. +var ErrSkipResPath = errors.New("resource path that does needs to be skipped") + +// PathInfo contains all information about a resource that can be derived from its location on the filesystem. +type PathInfo struct { + Path string + ResDir string + TypeDir string + Type Type + Qualifier string + Density Density +} + +// ParsePath converts a path string into a PathInfo object if the string points to a resource file. +func ParsePath(p string) (PathInfo, error) { + parent := path.Dir(p) + resDir := path.Dir(parent) + typeDir := path.Base(parent) + + if strings.HasPrefix(path.Base(p), ".") { + return PathInfo{}, ErrSkipResPath + } + + resType, err := ParseValueOrType(strings.Split(typeDir, "-")[0]) + qualifier := extractQualifier(typeDir) + if err != nil { + return PathInfo{}, ErrNotResPath + } + var density Density + for _, q := range strings.Split(qualifier, "-") { + var err error + density, err = ParseDensity(q) + if err != nil { + return PathInfo{}, err + } + if density != UnspecifiedDensity { + break + } + } + return PathInfo{ + Path: p, + ResDir: resDir, + TypeDir: typeDir, + Type: resType, + Qualifier: qualifier, + Density: density, + }, nil +} + +// MakePathInfo converts a path string into a PathInfo object. +func MakePathInfo(p string) (*PathInfo, error) { + pi, err := ParsePath(p) + if err != nil { + return nil, fmt.Errorf("ParsePath failed to parse %q: %v", p, err) + } + return &pi, nil +} + +// MakePathInfos converts a list of path strings into a list of PathInfo objects. +func MakePathInfos(paths []string) ([]*PathInfo, error) { + pis := make([]*PathInfo, 0, len(paths)) + for _, p := range paths { + if strings.HasPrefix(path.Base(p), ".") { + continue + } + pi, err := MakePathInfo(p) + if err != nil { + return nil, err + } + pis = append(pis, pi) + } + return pis, nil +} + +func extractQualifier(s string) string { + base := path.Base(s) + parts := strings.SplitN(base, "-", 2) + if len(parts) > 1 { + return parts[1] + } + return "" +} diff --git a/src/tools/ak/res/path_test.go b/src/tools/ak/res/path_test.go new file mode 100644 index 0000000..24871d7 --- /dev/null +++ b/src/tools/ak/res/path_test.go @@ -0,0 +1,249 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package res + +import ( + "reflect" + "testing" +) + +func TestParsePath(t *testing.T) { + tests := []struct { + arg string + want PathInfo + }{ + { + "/tmp/foobar/values/strings.xml", + PathInfo{ + Path: "/tmp/foobar/values/strings.xml", + Type: ValueType, + TypeDir: "values", + ResDir: "/tmp/foobar", + }, + }, + { + "/tmp/foobar/values-v19/strings.xml", + PathInfo{ + Path: "/tmp/foobar/values-v19/strings.xml", + Type: ValueType, + TypeDir: "values-v19", + ResDir: "/tmp/foobar", + Qualifier: "v19", + }, + }, + { + "/tmp/baz/foobar/layout-es-419/main_activity.xml", + PathInfo{ + Path: "/tmp/baz/foobar/layout-es-419/main_activity.xml", + Type: Layout, + TypeDir: "layout-es-419", + ResDir: "/tmp/baz/foobar", + Qualifier: "es-419", + }, + }, + { + "/tmp/baz/foobar/menu/menu_data.xml", + PathInfo{ + Path: "/tmp/baz/foobar/menu/menu_data.xml", + Type: Menu, + TypeDir: "menu", + ResDir: "/tmp/baz/foobar", + }, + }, + { + "tmp/baz/foobar/drawable-en-ldpi/mercury.png", + PathInfo{ + Path: "tmp/baz/foobar/drawable-en-ldpi/mercury.png", + Type: Drawable, + TypeDir: "drawable-en-ldpi", + ResDir: "tmp/baz/foobar", + Qualifier: "en-ldpi", + Density: 120, + }, + }, + { + "tmp/baz/foobar/drawable-fr-mdpi-nokeys/mars.xml", + PathInfo{ + Path: "tmp/baz/foobar/drawable-fr-mdpi-nokeys/mars.xml", + Type: Drawable, + TypeDir: "drawable-fr-mdpi-nokeys", + ResDir: "tmp/baz/foobar", + Qualifier: "fr-mdpi-nokeys", + Density: 160, + }, + }, + + { + "tmp/baz/foobar/drawable-mcc310-en-rUS-tvdpi/venus.jpg", + PathInfo{ + Path: "tmp/baz/foobar/drawable-mcc310-en-rUS-tvdpi/venus.jpg", + Type: Drawable, + TypeDir: "drawable-mcc310-en-rUS-tvdpi", + ResDir: "tmp/baz/foobar", + Qualifier: "mcc310-en-rUS-tvdpi", + Density: 213, + }, + }, + { + "tmp/baz/foobar/drawable-mcc208-mnc00-fr-rCA-hdpi-12key-dpad/earth.gif", + PathInfo{ + Path: "tmp/baz/foobar/drawable-mcc208-mnc00-fr-rCA-hdpi-12key-dpad/earth.gif", + Type: Drawable, + TypeDir: "drawable-mcc208-mnc00-fr-rCA-hdpi-12key-dpad", + ResDir: "tmp/baz/foobar", + Qualifier: "mcc208-mnc00-fr-rCA-hdpi-12key-dpad", + Density: 240, + }, + }, + { + "tmp/baz/foobar/drawable-xhdpi/neptune.jpg", + PathInfo{ + Path: "tmp/baz/foobar/drawable-xhdpi/neptune.jpg", + Type: Drawable, + TypeDir: "drawable-xhdpi", + ResDir: "tmp/baz/foobar", + Qualifier: "xhdpi", + Density: 320, + }, + }, + { + "tmp/baz/foobar/drawable-xxhdpi/uranus.png", + PathInfo{ + Path: "tmp/baz/foobar/drawable-xxhdpi/uranus.png", + Type: Drawable, + TypeDir: "drawable-xxhdpi", + ResDir: "tmp/baz/foobar", + Qualifier: "xxhdpi", + Density: 480, + }, + }, + { + "tmp/baz/foobar/drawable-xxxhdpi/saturn.xml", + PathInfo{ + Path: "tmp/baz/foobar/drawable-xxxhdpi/saturn.xml", + Type: Drawable, + TypeDir: "drawable-xxxhdpi", + ResDir: "tmp/baz/foobar", + Qualifier: "xxxhdpi", + Density: 640, + }, + }, + { + "tmp/baz/foobar/drawable-anydpi/jupiter.png", + PathInfo{ + Path: "tmp/baz/foobar/drawable-anydpi/jupiter.png", + Type: Drawable, + TypeDir: "drawable-anydpi", + ResDir: "tmp/baz/foobar", + Qualifier: "anydpi", + Density: AnyDPI, + }, + }, + { + "tmp/baz/foobar/drawable-nodpi/sun.gif", + PathInfo{ + Path: "tmp/baz/foobar/drawable-nodpi/sun.gif", + Type: Drawable, + TypeDir: "drawable-nodpi", + ResDir: "tmp/baz/foobar", + Qualifier: "nodpi", + Density: NoDPI, + }, + }, + { + "tmp/baz/foobar/drawable-120dpi/moon.xml", + PathInfo{ + Path: "tmp/baz/foobar/drawable-120dpi/moon.xml", + Type: Drawable, + TypeDir: "drawable-120dpi", + ResDir: "tmp/baz/foobar", + Qualifier: "120dpi", + Density: 120, + }, + }, + } + for _, tc := range tests { + got, err := ParsePath(tc.arg) + if err != nil { + t.Errorf("ParsePath(%s): got err: %s", tc.arg, err) + continue + } + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("ParsePath(%s): got %+v want: %+v", tc.arg, got, tc.want) + } + } +} + +func TestParsePath_NegativeCases(t *testing.T) { + tests := []struct { + arg string + err error + }{ + {"/foo/bar/baz/strings.xml", ErrNotResPath}, + {"strings.xml", ErrNotResPath}, + } + for _, tc := range tests { + got, err := ParsePath(tc.arg) + if err == nil { + t.Errorf("ParsePath(%s): got: %+v and nil err, want err: %v", tc.arg, got, tc.err) + } + if err != tc.err { + t.Errorf("ParsePath(%s): got err: %v want err: %v", tc.arg, err, tc.err) + } + } +} + +func TestMakePathInfo(t *testing.T) { + paths := []string{ + "/tmp/foobar/values/strings.xml", + "/tmp/foobar/values-v19/strings.xml", + "/tmp/foobar/values-v19/.skip_me.xml", + "/tmp/baz/foobar/menu/menu_data.xml", + "tmp/baz/foobar/drawable-en-ldpi/mercury.png", + "/tmp/foobar/values-v19/.skip_me_as_well.xml", + } + want := []*PathInfo{ + &PathInfo{ + Path: "/tmp/foobar/values/strings.xml", + Type: ValueType, + TypeDir: "values", + ResDir: "/tmp/foobar"}, + &PathInfo{ + Path: "/tmp/foobar/values-v19/strings.xml", + Type: ValueType, + TypeDir: "values-v19", + ResDir: "/tmp/foobar", + Qualifier: "v19"}, + &PathInfo{ + Path: "/tmp/baz/foobar/menu/menu_data.xml", + Type: Menu, + TypeDir: "menu", + ResDir: "/tmp/baz/foobar"}, + &PathInfo{ + Path: "tmp/baz/foobar/drawable-en-ldpi/mercury.png", + Type: Drawable, + TypeDir: "drawable-en-ldpi", + ResDir: "tmp/baz/foobar", + Qualifier: "en-ldpi", + Density: 120}, + } + pInfos, err := MakePathInfos(paths) + if err != nil { + t.Fatalf("MakePathInfos unexpected error: %v", err) + } + if !reflect.DeepEqual(pInfos, want) { + t.Errorf("MakePathInfos: got %+v want: %+v", pInfos, want) + } +} diff --git a/src/tools/ak/res/proto/BUILD b/src/tools/ak/res/proto/BUILD new file mode 100644 index 0000000..adbdf33 --- /dev/null +++ b/src/tools/ak/res/proto/BUILD @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +# Description +# Android resources proto +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +proto_library( + name = "res_meta_proto", + srcs = ["res_meta.proto"], +) + +go_proto_library( + name = "res_meta_go_proto", + importpath = "src/tools/ak/res/proto/res_meta_go_proto", + protos = [":res_meta_proto"], +) + +proto_library( + name = "res_data_proto", + srcs = ["res_data.proto"], + deps = [":res_meta_proto"], +) + +go_proto_library( + name = "res_data_go_proto", + importpath = "src/tools/ak/res/proto/res_data_go_proto", + protos = [":res_data_proto"], + deps = [":res_meta_go_proto"], +) diff --git a/src/tools/ak/res/proto/res_data.proto b/src/tools/ak/res/proto/res_data.proto new file mode 100644 index 0000000..406983a --- /dev/null +++ b/src/tools/ak/res/proto/res_data.proto @@ -0,0 +1,53 @@ +syntax = "proto3"; + +package tools.android.ak.res.proto; + +import "src/tools/ak/res/proto/res_meta.proto"; + +// A Resource file including its values. +// Next ID: 4 +// From frameworks/base/tools/aapt2/Resource.h, +message Resource { + // Next ID: 26 + enum Type { + ANIM = 0; + ANIMATOR = 1; + ARRAY = 2; + ATTR = 3; + ATTR_PRIVATE = 4; + BOOL = 5; + COLOR = 6; + CONFIG_VARYING = 7; + DIMEN = 8; + DRAWABLE = 9; + FONT = 10; + FRACTION = 11; + ID = 12; + INTEGER = 13; + INTERPOLATOR = 14; + LAYOUT = 15; + MENU = 16; + MIPMAP = 17; + NAVIGATION = 18; + PLURALS = 19; + RAW = 20; + STRING = 21; + STYLE = 22; + STYLEABLE = 23; + TRANSITION = 24; + XML = 25; + } + + // name of the resource, e.g.: + // for strings its the "name" attribute of the entry + // for layouts its the layout file name. + string name = 1; + Type resource_type = 2; + StyleableMetaData styleable_value = 3; // set if resource_type = STYLEABLE +} + +// Ideally we could just use a recordio file for this. But not opensource. +message Resources { + string pkg = 1; + repeated Resource resource = 2; +} diff --git a/src/tools/ak/res/proto/res_meta.proto b/src/tools/ak/res/proto/res_meta.proto new file mode 100644 index 0000000..38d2f57 --- /dev/null +++ b/src/tools/ak/res/proto/res_meta.proto @@ -0,0 +1,14 @@ +syntax = "proto2"; + +package tools.android.ak.res.proto; + +// Describes a stylable view. +// Corresponds to R.styleable +// Next ID: 5 +message StyleableMetaData { + // The name of the style - eg AbsListView or PieChart + optional string name = 1; + + // pkg:attr_name form. + repeated string fqn_attributes = 2; +} diff --git a/src/tools/ak/res/respipe/BUILD b/src/tools/ak/res/respipe/BUILD new file mode 100644 index 0000000..ddc3053 --- /dev/null +++ b/src/tools/ak/res/respipe/BUILD @@ -0,0 +1,42 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +licenses(["notice"]) + +go_library( + name = "respipe", + srcs = [ + "errors.go", + "path_emitter.go", + "res_io.go", + "streams.go", + ], + importpath = "src/tools/ak/res/respipe/respipe", + visibility = [ + "//src/tools/ak:__subpackages__", + "//src/tools/resource_extractor:__subpackages__", + "//tools/android/incremental:__subpackages__", + ], + deps = [ + "//src/tools/ak/res", + "//src/tools/ak/res/proto:res_data_go_proto", + "@org_golang_google_protobuf//proto", + ], +) + +go_test( + name = "respipe_test", + size = "small", + srcs = [ + "errors_test.go", + "path_emitter_test.go", + "res_io_test.go", + "streams_test.go", + ], + embed = [":respipe"], + deps = [ + "//src/tools/ak/res", + "//src/tools/ak/res/proto:res_data_go_proto", + "@org_golang_google_protobuf//proto", + ], +) diff --git a/src/tools/ak/res/respipe/errors.go b/src/tools/ak/res/respipe/errors.go new file mode 100644 index 0000000..60cbb02 --- /dev/null +++ b/src/tools/ak/res/respipe/errors.go @@ -0,0 +1,43 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package respipe + +import ( + "fmt" + "strings" + + "context" +) + +const ( + ctxErrPrefix = "err-prefix" +) + +// Errorf returns a formatted error with any context sensitive information prefixed to the error +func Errorf(ctx context.Context, fmts string, a ...interface{}) error { + if s, ok := ctx.Value(ctxErrPrefix).(string); ok { + return fmt.Errorf(strings.Join([]string{s, fmts}, ""), a...) + } + return fmt.Errorf(fmts, a...) +} + +// PrefixErr returns a context which adds a prefix to error messages. +func PrefixErr(ctx context.Context, add string) context.Context { + if s, ok := ctx.Value(ctxErrPrefix).(string); ok { + return context.WithValue(ctx, ctxErrPrefix, strings.Join([]string{s, add}, "")) + } + return context.WithValue(ctx, ctxErrPrefix, add) + +} diff --git a/src/tools/ak/res/respipe/errors_test.go b/src/tools/ak/res/respipe/errors_test.go new file mode 100644 index 0000000..6b7b5b1 --- /dev/null +++ b/src/tools/ak/res/respipe/errors_test.go @@ -0,0 +1,56 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package respipe + +import ( + "errors" + "reflect" + "testing" + + "context" +) + +func TestPrefixErr(t *testing.T) { + tests := []struct { + ctx context.Context + fmts string + args []interface{} + want error + }{ + { + ctx: context.Background(), + fmts: "Hello world", + want: errors.New("Hello world"), + }, + { + ctx: PrefixErr(context.Background(), "file: foo: "), + fmts: "Hello world: %d", + args: []interface{}{1}, + want: errors.New("file: foo: Hello world: 1"), + }, + { + ctx: PrefixErr(PrefixErr(context.Background(), "file: foo: "), "tag: : "), + fmts: "Hello world: %d", + args: []interface{}{1}, + want: errors.New("file: foo: tag: : Hello world: 1"), + }, + } + for _, tc := range tests { + got := Errorf(tc.ctx, tc.fmts, tc.args...) + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("Errorf(%v, %v, %v): %v wanted %v", tc.ctx, tc.fmts, tc.args, got, tc.want) + } + } +} diff --git a/src/tools/ak/res/respipe/path_emitter.go b/src/tools/ak/res/respipe/path_emitter.go new file mode 100644 index 0000000..f903718 --- /dev/null +++ b/src/tools/ak/res/respipe/path_emitter.go @@ -0,0 +1,94 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package respipe + +import ( + "context" + "fmt" + "os" + "path/filepath" + + "src/tools/ak/res/res" +) + +// EmitPathInfos takes the list of provided PathInfos and emits them via its returned channel. +func EmitPathInfos(ctx context.Context, pis []*res.PathInfo) <-chan *res.PathInfo { + // produce PathInfos from res files + piC := make(chan *res.PathInfo) + go func() { + defer close(piC) + for _, pi := range pis { + select { + case piC <- pi: + case <-ctx.Done(): + return + } + } + }() + return piC +} + +// EmitPathInfosDir descends a provided directory and emits PathInfo objects via its returned +// channel. It also emits any errors encountered during the walk to its error channel. +func EmitPathInfosDir(ctx context.Context, base string) (<-chan *res.PathInfo, <-chan error) { + piC := make(chan *res.PathInfo) + errC := make(chan error) + go func() { + defer close(piC) + defer close(errC) + emit := func(path string, info os.FileInfo, err error) error { + if err != nil { + return fmt.Errorf("%s: walk failed: %v", path, err) + } + if info.IsDir() { + // we do not care about dirs. + return nil + } + pi, err := res.ParsePath(path) + if err == res.ErrNotResPath || err == res.ErrSkipResPath { + return nil + } + if err != nil { + if !SendErr(ctx, errC, Errorf(ctx, "%s: unexpected PathInfo failure: %v", path, err)) { + return filepath.SkipDir + } + return nil + } + select { + case <-ctx.Done(): + return filepath.SkipDir + case piC <- &pi: + } + return nil + } + if err := filepath.Walk(base, emit); err != nil { + SendErr(ctx, errC, Errorf(ctx, "%s: walk encountered err: %v", base, err)) + } + }() + return piC, errC +} + +// EmitPathInfosDirs descends a provided directories and emits PathsInfo objects via its returned +// channel. It also emits any errors encountered during the walk to its error channel. +func EmitPathInfosDirs(ctx context.Context, dirs []string) (<-chan *res.PathInfo, <-chan error) { + piCs := make([]<-chan *res.PathInfo, 0, len(dirs)) + errCs := make([]<-chan error, 0, len(dirs)) + for _, rd := range dirs { + piC, piErr := EmitPathInfosDir(ctx, rd) + piCs = append(piCs, piC) + errCs = append(errCs, piErr) + } + return MergePathInfoStreams(ctx, piCs), MergeErrStreams(ctx, errCs) +} diff --git a/src/tools/ak/res/respipe/path_emitter_test.go b/src/tools/ak/res/respipe/path_emitter_test.go new file mode 100644 index 0000000..84f28cc --- /dev/null +++ b/src/tools/ak/res/respipe/path_emitter_test.go @@ -0,0 +1,92 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package respipe + +import ( + "io/ioutil" + "os" + "path" + "reflect" + "sort" + "testing" + + "context" +) + +func TestEmitPathInfosDir(t *testing.T) { + tmpDir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatalf("%s: make failed: %v", tmpDir, err) + } + defer func() { + if err := os.RemoveAll(tmpDir); err != nil { + t.Errorf("%s: could not remove: %v", tmpDir, err) + } + }() + + touch := func(p string) string { + if err := os.MkdirAll(path.Dir(path.Join(tmpDir, p)), 0744); err != nil { + t.Fatalf("%s: mkdir failed: %v", p, err) + } + f, err := os.OpenFile(path.Join(tmpDir, p), os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + t.Fatalf("%s: touch failed: %v", p, err) + } + defer f.Close() + return f.Name() + } + wantPaths := []string{ + "values/strings.xml", + "values/styles.xml", + "layout-land/hello.xml", + "layout/hello.xml", + "values-v19/styles.xml", + "drawable-ldpi/foo.png", + "raw/data.xml", + "xml/perf.xml", + } + for i, p := range wantPaths { + wantPaths[i] = touch(p) + } + touch("values/.placeholder") + touch("something_random/data.txt") + + ctx, cxlFn := context.WithCancel(context.Background()) + defer cxlFn() + piC, errC := EmitPathInfosDir(ctx, tmpDir) + var gotPaths []string +Loop: + for { + select { + case p, ok := <-piC: + if !ok { + break Loop + } + gotPaths = append(gotPaths, p.Path) + case e, ok := <-errC: + if !ok { + break Loop + } + t.Fatalf("Unexpected failure: %v", e) + + } + } + sort.Strings(gotPaths) + sort.Strings(wantPaths) + if !reflect.DeepEqual(gotPaths, wantPaths) { + t.Errorf("EmitPathInfosDir(): %v wanted: %v", gotPaths, wantPaths) + } + +} diff --git a/src/tools/ak/res/respipe/res_io.go b/src/tools/ak/res/respipe/res_io.go new file mode 100644 index 0000000..d1e608f --- /dev/null +++ b/src/tools/ak/res/respipe/res_io.go @@ -0,0 +1,109 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +package respipe + +import ( + "bufio" + "encoding/binary" + "io" + + "context" + rdpb "src/tools/ak/res/proto/res_data_go_proto" + "google.golang.org/protobuf/proto" +) + +// ResInput sends all protos in the provided reader into the pipeline. +type ResInput struct { + In io.Reader +} + +// Produce returns a channel of resource protos encountered in the input along with a chan of errors encountered while decoding them. +func (ri ResInput) Produce(ctx context.Context) (<-chan *rdpb.Resource, <-chan error) { + resC := make(chan *rdpb.Resource) + errC := make(chan error) + go func() { + defer close(resC) + defer close(errC) + r := bufio.NewReaderSize(ri.In, 2<<16) + var b [4]byte + for { + if _, err := io.ReadFull(r, b[:]); err != nil { + if err != io.EOF { + SendErr(ctx, errC, Errorf(ctx, "read len failed: %v", err)) + } + return + + } + dlen := binary.LittleEndian.Uint32(b[:]) + d := make([]byte, dlen) + if _, err := io.ReadFull(r, d); err != nil { + SendErr(ctx, errC, Errorf(ctx, "read proto failed: %v", err)) + return + } + r := &rdpb.Resource{} + if err := proto.Unmarshal(d, r); err != nil { + SendErr(ctx, errC, Errorf(ctx, "unmarshal proto failed: %v", err)) + return + } + if !SendRes(ctx, resC, r) { + return + } + + } + + }() + return resC, errC +} + +// ResOutput is a sink to a resource pipeline that writes all resource protos it encounters to the given writer. +type ResOutput struct { + Out io.Writer +} + +// Consume takes all resource protos from the provided channel and writes them to ResOutput's writer. +func (ro ResOutput) Consume(ctx context.Context, resChan <-chan *rdpb.Resource) <-chan error { + + errC := make(chan error) + go func() { + defer close(errC) + + w := bufio.NewWriterSize(ro.Out, 2<<16) + defer func() { + if err := w.Flush(); err != nil { + SendErr(ctx, errC, Errorf(ctx, "flush end of data failed: %v", err)) + } + }() + var b [4]byte + for r := range resChan { + d, err := proto.Marshal(r) + if err != nil { + SendErr(ctx, errC, Errorf(ctx, "%#v encoding failed: %v", r, err)) + return + } + binary.LittleEndian.PutUint32(b[:], uint32(len(d))) + if _, err := w.Write(b[:]); err != nil { + SendErr(ctx, errC, Errorf(ctx, "write failed: %v", err)) + return + } + if _, err := w.Write(d); err != nil { + SendErr(ctx, errC, Errorf(ctx, "write failed: %v", err)) + return + } + } + }() + + return errC +} diff --git a/src/tools/ak/res/respipe/res_io_test.go b/src/tools/ak/res/respipe/res_io_test.go new file mode 100644 index 0000000..70ac90a --- /dev/null +++ b/src/tools/ak/res/respipe/res_io_test.go @@ -0,0 +1,85 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package respipe + +import ( + "bytes" + "context" + "testing" + + rdpb "src/tools/ak/res/proto/res_data_go_proto" + "google.golang.org/protobuf/proto" +) + +func TestProduceConsume(t *testing.T) { + var b bytes.Buffer + + ro := ResOutput{Out: &b} + resC := make(chan *rdpb.Resource) + ctx, cxlFn := context.WithCancel(context.Background()) + defer cxlFn() + + errC := ro.Consume(ctx, resC) + ress := []*rdpb.Resource{ + { + Name: "hi", + }, + { + Name: "bye", + }, + { + Name: "foo", + }, + } + for _, r := range ress { + select { + case err := <-errC: + t.Fatalf("Unexpected err: %v", err) + case resC <- r: + } + } + close(resC) + if err := <-errC; err != nil { + t.Fatalf("unexpected err: %v", err) + } + ri := ResInput{In: &b} + + resInC, errC := ri.Produce(ctx) + var got []*rdpb.Resource + for resInC != nil || errC != nil { + select { + case r, ok := <-resInC: + if !ok { + resInC = nil + continue + } + got = append(got, r) + case err, ok := <-errC: + if !ok { + errC = nil + continue + } + t.Fatalf("Unexpected err: %v", err) + } + } + if len(got) != len(ress) { + t.Fatalf("Got %d elements, expected %d", len(got), len(ress)) + } + for i := range ress { + if !proto.Equal(got[i], ress[i]) { + t.Errorf("Got: %+v wanted: %+v", got[i], ress[i]) + } + } +} diff --git a/src/tools/ak/res/respipe/streams.go b/src/tools/ak/res/respipe/streams.go new file mode 100644 index 0000000..33c41ea --- /dev/null +++ b/src/tools/ak/res/respipe/streams.go @@ -0,0 +1,119 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package respipe contains utilities for running pipelines on android resources. +package respipe + +import ( + "context" + "sync" + + rdpb "src/tools/ak/res/proto/res_data_go_proto" + "src/tools/ak/res/res" +) + +// MergePathInfoStreams fans in multiple PathInfo streams into a single stream. +func MergePathInfoStreams(ctx context.Context, piCs []<-chan *res.PathInfo) <-chan *res.PathInfo { + piC := make(chan *res.PathInfo) + var wg sync.WaitGroup + wg.Add(len(piCs)) + output := func(c <-chan *res.PathInfo) { + defer wg.Done() + for r := range c { + select { + case piC <- r: + case <-ctx.Done(): + return + } + } + } + for _, rc := range piCs { + go output(rc) + } + go func() { + wg.Wait() + close(piC) + }() + return piC +} + +// MergeResStreams fans in multiple Resource streams into a single stream. +func MergeResStreams(ctx context.Context, resCs []<-chan *rdpb.Resource) <-chan *rdpb.Resource { + resC := make(chan *rdpb.Resource) + var wg sync.WaitGroup + wg.Add(len(resCs)) + output := func(c <-chan *rdpb.Resource) { + defer wg.Done() + for r := range c { + select { + case resC <- r: + case <-ctx.Done(): + return + } + } + } + for _, rc := range resCs { + go output(rc) + } + go func() { + wg.Wait() + close(resC) + }() + return resC +} + +// MergeErrStreams fans in multiple error streams into a single stream. +func MergeErrStreams(ctx context.Context, errCs []<-chan error) <-chan error { + errC := make(chan error) + var wg sync.WaitGroup + wg.Add(len(errCs)) + output := func(c <-chan error) { + defer wg.Done() + for e := range c { + select { + case errC <- e: + case <-ctx.Done(): + return + } + } + } + for _, rc := range errCs { + go output(rc) + } + go func() { + wg.Wait() + close(errC) + }() + return errC +} + +// SendErr attempts to send the provided error to the provided chan, however is the context is canceled, it will return false. +func SendErr(ctx context.Context, errC chan<- error, err error) bool { + select { + case <-ctx.Done(): + return false + case errC <- err: + return true + } +} + +// SendRes attempts to send the provided resource to the provided chan, however is the context is canceled, it will return false. +func SendRes(ctx context.Context, resC chan<- *rdpb.Resource, r *rdpb.Resource) bool { + select { + case <-ctx.Done(): + return false + case resC <- r: + return true + } +} diff --git a/src/tools/ak/res/respipe/streams_test.go b/src/tools/ak/res/respipe/streams_test.go new file mode 100644 index 0000000..81dd7fc --- /dev/null +++ b/src/tools/ak/res/respipe/streams_test.go @@ -0,0 +1,85 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package respipe + +import ( + "context" + "errors" + "testing" + + rdpb "src/tools/ak/res/proto/res_data_go_proto" + "src/tools/ak/res/res" +) + +func TestMergePathInfoStreams(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + sendClose := func(p *res.PathInfo, c chan<- *res.PathInfo) { + defer close(c) + c <- p + } + in1 := make(chan *res.PathInfo) + in2 := make(chan *res.PathInfo) + go sendClose(&res.PathInfo{}, in1) + go sendClose(&res.PathInfo{}, in2) + mergedC := MergePathInfoStreams(ctx, []<-chan *res.PathInfo{in1, in2}) + var rcv []*res.PathInfo + for p := range mergedC { + rcv = append(rcv, p) + } + if len(rcv) != 2 { + t.Errorf("got: %v on merged stream, wanted only 2 elements", rcv) + } +} + +func TestMergeResStreams(t *testing.T) { + ctx := context.Background() + sendClose := func(r *rdpb.Resource, c chan<- *rdpb.Resource) { + defer close(c) + c <- r + } + in1 := make(chan *rdpb.Resource) + in2 := make(chan *rdpb.Resource) + go sendClose(&rdpb.Resource{}, in1) + go sendClose(&rdpb.Resource{}, in2) + merged := MergeResStreams(ctx, []<-chan *rdpb.Resource{in1, in2}) + var rcv []*rdpb.Resource + for r := range merged { + rcv = append(rcv, r) + } + if len(rcv) != 2 { + t.Errorf("got: %v on merged stream, wanted only 2 elements", rcv) + } +} + +func TestMergeErrStreams(t *testing.T) { + ctx := context.Background() + sendClose := func(e error, eC chan<- error) { + defer close(eC) + eC <- e + } + in1 := make(chan error) + in2 := make(chan error) + go sendClose(errors.New("hi"), in1) + go sendClose(errors.New("hello"), in2) + merged := MergeErrStreams(ctx, []<-chan error{in1, in2}) + var rcv []error + for r := range merged { + rcv = append(rcv, r) + } + if len(rcv) != 2 { + t.Errorf("got: %v on merged stream, wanted only 2 elements", rcv) + } +} diff --git a/src/tools/ak/res/resxml/BUILD b/src/tools/ak/res/resxml/BUILD new file mode 100644 index 0000000..c74aa68 --- /dev/null +++ b/src/tools/ak/res/resxml/BUILD @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +licenses(["notice"]) + +go_library( + name = "resxml", + srcs = ["xml_parser.go"], + importpath = "src/tools/ak/res/resxml/resxml", + visibility = ["//src/tools/ak/liteparse:__subpackages__"], + deps = [ + "//src/tools/ak/res/respipe", + ], +) + +go_test( + name = "resxml_test", + size = "small", + srcs = ["xml_parser_test.go"], + embed = [":resxml"], + deps = [ + "//src/tools/ak/res/respipe", + ], +) diff --git a/src/tools/ak/res/resxml/xml_parser.go b/src/tools/ak/res/resxml/xml_parser.go new file mode 100644 index 0000000..ed765fd --- /dev/null +++ b/src/tools/ak/res/resxml/xml_parser.go @@ -0,0 +1,133 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package resxml contains common functions to extract information from xml files and feed that information into the resource processing pipeline. +package resxml + +import ( + "context" + "encoding/xml" + "io" + + "src/tools/ak/res/respipe/respipe" +) + +// XMLEvent wraps an XMLToken and the Offset at which it was encountered. +type XMLEvent struct { + Token xml.Token + Offset int64 +} + +// ConsumeUntil takes xmlEvents from the provided chan and discards them until it finds a StartEvent which matches the provided name. If the channel is exhausted, false is returned. +func ConsumeUntil(name xml.Name, xmlC <-chan XMLEvent) (XMLEvent, bool) { + for xe := range xmlC { + if se, ok := xe.Token.(xml.StartElement); ok { + if SloppyMatches(name, se.Name) { + return xe, true + } + } + } + return XMLEvent{}, false +} + +// ForwardChildren takes the provided StartElement and a channel of XMLEvents and forwards that all events onto the returned XMLEvent channel until the matching EndElement to start is encountered. +func ForwardChildren(ctx context.Context, start XMLEvent, xmlC <-chan XMLEvent) <-chan XMLEvent { + eventC := make(chan XMLEvent, 1) + se := start.Token.(xml.StartElement) + go func() { + defer close(eventC) + count := 1 + for xe := range xmlC { + if e, ok := xe.Token.(xml.StartElement); ok { + if StrictMatches(e.Name, se.Name) { + count++ + } + } + if e, ok := xe.Token.(xml.EndElement); ok { + if StrictMatches(e.Name, se.Name) { + count-- + } + if count == 0 { + return + } + } + if !SendXML(ctx, eventC, xe) { + return + } + } + }() + return eventC + +} + +// StrictMatches considers xml.Names equal if both their space and name matches. +func StrictMatches(n1, n2 xml.Name) bool { + return n1.Local == n2.Local && n1.Space == n2.Space +} + +// SloppyMatches ignores xml.Name Space attributes unless both names specify Space. Otherwise +// only the Local attribute is used for matching. +func SloppyMatches(n1, n2 xml.Name) bool { + if n1.Space != "" && n2.Space != "" { + return StrictMatches(n1, n2) + } + return n1.Local == n2.Local +} + +// StreamDoc parses the provided doc and forwards all xml tokens to the returned XMLEvent chan. +func StreamDoc(ctx context.Context, doc io.Reader) (<-chan XMLEvent, <-chan error) { + eventC := make(chan XMLEvent) + errC := make(chan error) + go func() { + defer close(eventC) + defer close(errC) + decoder := xml.NewDecoder(doc) + // Turns off unknown entities check. Would otherwise fail on resources + // using non-standard XML entities. + decoder.Strict = false + for { + tok, err := decoder.Token() + if err == io.EOF { + return + } + if err != nil { + respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "offset: %d xml error: %v", decoder.InputOffset(), err)) + return + } + tok = xml.CopyToken(tok) + if !SendXML(ctx, eventC, XMLEvent{tok, decoder.InputOffset()}) { + return + } + } + }() + return eventC, errC +} + +// SendXML sends an XMLEvent to the provided channel and returns true, otherwise if the context is done, it returns false. +func SendXML(ctx context.Context, xmlC chan<- XMLEvent, xml XMLEvent) bool { + select { + case <-ctx.Done(): + return false + case xmlC <- xml: + return true + } +} + +// Attrs returns all []xml.Attrs encounted on an XMLEvent. +func Attrs(xe XMLEvent) []xml.Attr { + if se, ok := xe.Token.(xml.StartElement); ok { + return se.Attr + } + return nil +} diff --git a/src/tools/ak/res/resxml/xml_parser_test.go b/src/tools/ak/res/resxml/xml_parser_test.go new file mode 100644 index 0000000..8c39e29 --- /dev/null +++ b/src/tools/ak/res/resxml/xml_parser_test.go @@ -0,0 +1,226 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package resxml + +import ( + "bytes" + "context" + "encoding/xml" + "io" + "reflect" + "testing" + + "src/tools/ak/res/respipe/respipe" +) + +const ( + doc = ` + + Grace R. Emlin + Example Inc. + + gre@example.com + + Hanga Rao1234 Main St.RandomText + + gre@work.com + + + Friends + Squash + + Easter Island + + ` +) + +func TestForwardChildren(t *testing.T) { + ctx, cancel := context.WithCancel(respipe.PrefixErr(context.Background(), "test doc: ")) + defer cancel() + xmlC, errC := StreamDoc(ctx, bytes.NewBufferString(doc)) + xe, ok := ConsumeUntil(xml.Name{Local: "City"}, xmlC) + if !ok { + t.Fatalf("Expected to find: %s in %s", xml.Name{Local: "City"}, doc) + } + childC := ForwardChildren(ctx, xe, xmlC) + wantEvents := []XMLEvent{ + { + Token: xml.CharData("Hanga Rao"), + }, + { + Token: xml.StartElement{Name: xml.Name{Local: "Street"}, Attr: []xml.Attr{}}, + }, + { + Token: xml.CharData("1234 Main St."), + }, + { + Token: xml.EndElement{Name: xml.Name{Local: "Street"}}, + }, + { + Token: xml.CharData("RandomText"), + }, + } + var gotEvents []XMLEvent + for childC != nil || errC != nil { + select { + case xe, ok := <-childC: + if !ok { + childC = nil + cancel() + continue + } + xe.Offset = 0 + gotEvents = append(gotEvents, xe) + case e, ok := <-errC: + if !ok { + errC = nil + continue + } + t.Errorf("unexpected error: %v", e) + } + } + + if !reflect.DeepEqual(wantEvents, gotEvents) { + t.Errorf("Got children: %#v wanted: %#v", gotEvents, wantEvents) + } + +} + +func TestAttrs(t *testing.T) { + tests := []struct { + arg XMLEvent + want []xml.Attr + }{ + { + XMLEvent{ + Token: xml.StartElement{ + Attr: []xml.Attr{ + { + Name: xml.Name{Local: "dog"}, + Value: "shepard", + }, + { + Name: xml.Name{Local: "cat"}, + Value: "cheshire", + }, + }, + }, + }, + []xml.Attr{ + { + Name: xml.Name{Local: "dog"}, + Value: "shepard", + }, + { + Name: xml.Name{Local: "cat"}, + Value: "cheshire", + }, + }, + }, + { + XMLEvent{Token: xml.StartElement{}}, + []xml.Attr(nil), + }, + { + XMLEvent{Token: xml.CharData("foo")}, + []xml.Attr(nil), + }, + } + + for _, tc := range tests { + got := Attrs(tc.arg) + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("Attrs(%#v): %#v wanted %#v", tc.arg, got, tc.want) + } + } +} + +func TestConsumeUntil(t *testing.T) { + ctx, cancel := context.WithCancel(respipe.PrefixErr(context.Background(), "test doc: ")) + defer cancel() + xmlC, errC := StreamDoc(ctx, bytes.NewBufferString(doc)) + + xe, ok := ConsumeUntil(xml.Name{Local: "Email"}, xmlC) + if !ok { + t.Fatalf("Expected to find: %s in %s", xml.Name{Local: "Email"}, doc) + } + if se, ok := xe.Token.(xml.StartElement); ok { + want := []xml.Attr{{xml.Name{Local: "where"}, "home"}} + if !reflect.DeepEqual(want, se.Attr) { + t.Errorf("Got attr: %v wanted: %v", se.Attr, want) + } + } else { + t.Fatalf("Got: %v Expected to stop on a start element", xe) + } + xe, ok = ConsumeUntil(xml.Name{Local: "Email"}, xmlC) + if !ok { + t.Fatalf("Expected to find: %s in %s", xml.Name{Local: "Email"}, doc) + } + if se, ok := xe.Token.(xml.StartElement); ok { + want := []xml.Attr{{xml.Name{Local: "where"}, "work"}} + if !reflect.DeepEqual(want, se.Attr) { + t.Errorf("Got attr: %v wanted: %v", se.Attr, want) + } + } else { + t.Fatalf("Got: %v Expected to stop on a start element", xe) + } + xe, ok = ConsumeUntil(xml.Name{Local: "Email"}, xmlC) + if ok { + t.Fatalf("Expected no more nodes with: %v got: %v in doc: %s", xml.Name{Local: "Email"}, xe, doc) + } + e, ok := <-errC + if ok { + t.Fatalf("Expected no errors during parse: %v", e) + } +} + +func TestStreamDoc(t *testing.T) { + dec := xml.NewDecoder(bytes.NewBufferString(doc)) + var events []XMLEvent + for { + tok, err := dec.Token() + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("Unexpected xml parse failure: %v", err) + } + events = append(events, XMLEvent{xml.CopyToken(tok), dec.InputOffset()}) + } + ctx, cancel := context.WithCancel(respipe.PrefixErr(context.Background(), "test doc: ")) + defer cancel() + xmlC, errC := StreamDoc(ctx, bytes.NewBufferString(doc)) + var got []XMLEvent + for xmlC != nil || errC != nil { + select { + case e, ok := <-errC: + if !ok { + errC = nil + continue + } + t.Errorf("Unexpected error: %v", e) + case xe, ok := <-xmlC: + if !ok { + xmlC = nil + continue + } + got = append(got, xe) + } + } + if !reflect.DeepEqual(events, got) { + t.Errorf("StreamDoc() got: %v wanted: %v", got, events) + } + +} diff --git a/src/tools/ak/res/struct.go b/src/tools/ak/res/struct.go new file mode 100644 index 0000000..b58e854 --- /dev/null +++ b/src/tools/ak/res/struct.go @@ -0,0 +1,328 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package res handles understanding and representing information about Android resources. +package res + +import ( + "errors" + "fmt" + "strconv" + "strings" + + rdpb "src/tools/ak/res/proto/res_data_go_proto" +) + +var ( + // ErrWrongType occurs when a type is used in an operation that it does not support. + ErrWrongType = errors.New("this type cannot be used in this operation") +) + +// Type of resource (eg: string, layout, drawable) +type Type rdpb.Resource_Type + +// Enum converts a Type into a enum proto value +func (t Type) Enum() (rdpb.Resource_Type, error) { + if !t.IsSerializable() { + return rdpb.Resource_Type(ValueType), ErrWrongType + } + return rdpb.Resource_Type(t), nil +} + +// IsSerializable indicates that the Type can be converted to a proto (some types are only for in memory operations). +func (t Type) IsSerializable() bool { + for _, a := range nonProtoTypes { + if t == a { + return false + } + } + return true +} + +// NestedClassName is the R.java nested class name for this type (if the type is understood by android). +func (t Type) NestedClassName() (string, error) { + if !t.IsReal() { + return "", ErrWrongType + } + return typeToString[t], nil +} + +// IsReal indicates that the type is known to the android framework. +func (t Type) IsReal() bool { + for _, a := range nonProtoTypes { + if a == t { + return false + } + } + return true +} + +// From frameworks/base/tools/aapt2/Resource.h, except UnknownType and ValueType +// TODO(mauriciogg): use proto definitions and remove ValueType and UnknownType. +const ( + // UnknownType needs to be zero value + UnknownType Type = -2 + ValueType = -1 + + // Anim represents Android Anim resource types. + Anim = Type(rdpb.Resource_ANIM) + // Animator represents Android Animator resource types. + Animator = Type(rdpb.Resource_ANIMATOR) + // Array represents Android Array resource types. + Array = Type(rdpb.Resource_ARRAY) + // Attr represents Android Attr resource types. + Attr = Type(rdpb.Resource_ATTR) + // AttrPrivate represents Android AttrPrivate resource types. + AttrPrivate = Type(rdpb.Resource_ATTR_PRIVATE) + // Bool represents Android Bool resource types. + Bool = Type(rdpb.Resource_BOOL) + // Color represents Android Color resource types. + Color = Type(rdpb.Resource_COLOR) + // ConfigVarying represents Android ConfigVarying resource types, not really a type, but it shows up in some CTS tests + ConfigVarying = Type(rdpb.Resource_CONFIG_VARYING) + // Dimen represents Android Dimen resource types. + Dimen = Type(rdpb.Resource_DIMEN) + // Drawable represents Android Drawable resource types. + Drawable = Type(rdpb.Resource_DRAWABLE) + // Font represents Android Font resource types. + Font = Type(rdpb.Resource_FONT) + // Fraction represents Android Fraction resource types. + Fraction = Type(rdpb.Resource_FRACTION) + // ID represents Android Id resource types. + ID = Type(rdpb.Resource_ID) + // Integer represents Android Integer resource types. + Integer = Type(rdpb.Resource_INTEGER) + // Interpolator represents Android Interpolator resource types. + Interpolator = Type(rdpb.Resource_INTERPOLATOR) + // Layout represents Android Layout resource types. + Layout = Type(rdpb.Resource_LAYOUT) + // Menu represents Android Menu resource types. + Menu = Type(rdpb.Resource_MENU) + // Mipmap represents Android Mipmap resource types. + Mipmap = Type(rdpb.Resource_MIPMAP) + // Navigation represents Android Navigation resource types. + Navigation = Type(rdpb.Resource_NAVIGATION) + // Plurals represents Android Plurals resource types. + Plurals = Type(rdpb.Resource_PLURALS) + // Raw represents Android Raw resource types. + Raw = Type(rdpb.Resource_RAW) + // String represents Android String resource types. + String = Type(rdpb.Resource_STRING) + // Style represents Android Style resource types. + Style = Type(rdpb.Resource_STYLE) + // Styleable represents Android Styleable resource types. + Styleable = Type(rdpb.Resource_STYLEABLE) + // Transition represents Android Transition resource types. + Transition = Type(rdpb.Resource_TRANSITION) + // XML represents Android Xml resource types. + XML = Type(rdpb.Resource_XML) +) + +var ( + // A fixed mapping between the string representation of a type and its Type. + typeToString = map[Type]string{ + Anim: "anim", + Animator: "animator", + Array: "array", + Attr: "attr", + AttrPrivate: "^attr-private", + Bool: "bool", + Color: "color", + ConfigVarying: "configVarying", + Dimen: "dimen", + Drawable: "drawable", + Fraction: "fraction", + Font: "font", + ID: "id", + Integer: "integer", + Interpolator: "interpolator", + Layout: "layout", + Menu: "menu", + Mipmap: "mipmap", + Navigation: "navigation", + Plurals: "plurals", + Raw: "raw", + String: "string", + Style: "style", + Styleable: "styleable", + Transition: "transition", + XML: "xml", + } + stringToType = make(map[string]Type) + // AllTypes is a list of all known resource types. + AllTypes = make([]Type, 0, len(typeToString)) + + // These types are not allowed to be serialized into proto format. + nonProtoTypes = []Type{ValueType, UnknownType} +) + +// Kind indicates what type of resource file emits this resource. A resource can be found in +// res/values folder (and therefore is a Value - which can be represented as a ResourceValue in +// Android) or in folders outside of res/values (such as res/layout) and thus are not ResourceValues +// but rather some external resource (such as an image or parsed xml file). +type Kind uint8 + +const ( + // Unknown should not be encountered. + Unknown Kind = iota + // Value can only be encountered in res/values folders. + Value + // NonValue can not be encountered in res/values folders. + NonValue + // Both is a Kind of Type which may be inside a res/values folder or in another res/ folder. + Both +) + +var ( + kindToString = map[Kind]string{ + Unknown: "Unknown", + Value: "Value", + NonValue: "NonValue", + Both: "Both", + } + + // A fixed mapping between Type and Kind. + TypesToKind = map[Type]Kind{ + Anim: NonValue, + Animator: NonValue, + Array: Value, + Attr: Value, + AttrPrivate: Value, + Bool: Value, + Color: Both, + ConfigVarying: Value, + Dimen: Value, + Drawable: NonValue, + Font: NonValue, + Fraction: Value, + ID: Value, + Integer: Value, + Interpolator: NonValue, + Layout: NonValue, + Menu: NonValue, + Mipmap: NonValue, + Navigation: NonValue, + Plurals: Value, + Raw: NonValue, + String: Value, + Style: Value, + Styleable: Value, + Transition: NonValue, + XML: NonValue, + } +) + +// Density represents the dpi value of a resource. +type Density uint16 + +// From frameworks/base/core/java/Android/content/res/Configuration.java +const ( + // UnspecifiedDensity is a default value indicating no dpi has been specified + UnspecifiedDensity Density = 0 + + // LDPI has a dpi of 120 + LDPI Density = 120 + // MDPI has a dpi of 160 + MDPI Density = 160 + // TVDPI has a dpi of 213 + TVDPI Density = 213 + // HDPI has a dpi of 240 + HDPI Density = 240 + // XhDPI has a dpi of 320 + XhDPI Density = 320 + // XxhDPI has a dpi of 480 + XxhDPI Density = 480 + // XxxhDPI has a dpi of 640 + XxxhDPI Density = 640 + // AnyDPI indicates a resource which can be any dpi. + AnyDPI Density = 0xfffe + // NoDPI indicates the resources have no dpi constraints + NoDPI Density = 0xffff + dpiSuffix = "dpi" +) + +var ( + densityToStr = map[Density]string{ + LDPI: "ldpi", + MDPI: "mdpi", + TVDPI: "tvdpi", + HDPI: "hdpi", + XhDPI: "xhdpi", + XxhDPI: "xxhdpi", + XxxhDPI: "xxxhdpi", + AnyDPI: "anydpi", + NoDPI: "nodpi", + } + strToDensity = make(map[string]Density) +) + +// ParseValueOrType converts a string into a value type or well known type +func ParseValueOrType(s string) (Type, error) { + if s == "values" { + return ValueType, nil + } + return ParseType(s) +} + +// ParseType converts a string into a well known type +func ParseType(s string) (Type, error) { + if t, ok := stringToType[s]; ok { + return t, nil + } + return UnknownType, fmt.Errorf("%s: unknown type", s) +} + +// String for Type structs corresponds to the string format known to Android. +func (t Type) String() string { + if s, ok := typeToString[t]; ok { + return s + } + return fmt.Sprintf("Type(%d)", t) +} + +// Kind indicates the resource kind of this type. +func (t Type) Kind() Kind { + if t == ValueType { + return Value + } + if t, ok := TypesToKind[t]; ok { + return t + } + return Unknown +} + +// ParseDensity converts a string representation of a density into a Density. +func ParseDensity(s string) (Density, error) { + if d, ok := strToDensity[s]; ok { + return d, nil + } + if strings.HasSuffix(s, dpiSuffix) { + parsed, err := strconv.ParseUint(s[0:len(s)-len(dpiSuffix)], 10, 16) + if err != nil { + return 0, fmt.Errorf("%s: unparsable: %v", s, err) + } + return Density(parsed), nil + } + return UnspecifiedDensity, nil +} + +func init() { + for k, v := range typeToString { + AllTypes = append(AllTypes, k) + stringToType[v] = k + } + for k, v := range densityToStr { + strToDensity[v] = k + } +} diff --git a/src/tools/ak/res/struct_test.go b/src/tools/ak/res/struct_test.go new file mode 100644 index 0000000..0a3d4d5 --- /dev/null +++ b/src/tools/ak/res/struct_test.go @@ -0,0 +1,99 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package res + +import ( + "fmt" + "strings" + "testing" +) + +func TestKinds(t *testing.T) { + tests := []struct { + t Type + k Kind + }{ + {t: String, k: Value}, + {t: XML, k: NonValue}, + {t: Drawable, k: NonValue}, + {t: Color, k: Both}, + {t: Menu, k: NonValue}, + {t: Dimen, k: Value}, + {t: UnknownType, k: Unknown}, + } + for _, tc := range tests { + if tc.t.Kind() != tc.k { + t.Errorf("%v.Kind() = %v want: %v", tc.t, tc.t.Kind(), tc.k) + } + } + for _, at := range AllTypes { + if at == UnknownType { + continue + } + if at.Kind() == Unknown { + t.Errorf("%v.Kind() = %v - wanting anything else but that", at, Unknown) + } + } +} + +func TestTypes(t *testing.T) { + tests := []struct { + t Type + s string + }{ + {t: String, s: "string"}, + {t: XML, s: "xml"}, + {t: Drawable, s: "drawable"}, + {t: Color, s: "color"}, + {t: Menu, s: "menu"}, + {t: Dimen, s: "dimen"}, + } + + for _, tc := range tests { + pt, err := ParseType(tc.s) + if tc.t != pt || err != nil { + t.Errorf("ParseType(%s): %v, %v want: %v", tc.s, pt, err, tc.t) + } + } +} + +func TestDensities(t *testing.T) { + tests := []struct { + arg string + want Density + err error + }{ + {arg: "tvdpi", want: TVDPI}, + {arg: "hdpi", want: HDPI}, + {arg: "320dpi", want: 320}, + {arg: "nodpi", want: NoDPI}, + {arg: "en-US", want: UnspecifiedDensity}, + {arg: "12000000dpi", err: fmt.Errorf("%ddpi: unparsable", 12000000)}, + } + + for _, tc := range tests { + got, err := ParseDensity(tc.arg) + if tc.err == nil && err != nil { + t.Errorf("ParseDensity(%s): got err: %s", tc.arg, err) + } + if tc.err != nil && err != nil && !strings.HasPrefix(err.Error(), tc.err.Error()) { + t.Errorf("ParseDensity(%s): got err: %v want err: %v", tc.arg, err, tc.err) + } + + if got != tc.want { + t.Errorf("ParseDensity(%s): Got: %v want: %v", tc.arg, got, tc.want) + } + } +} diff --git a/src/tools/ak/res/xml.go b/src/tools/ak/res/xml.go new file mode 100644 index 0000000..bdf3f4a --- /dev/null +++ b/src/tools/ak/res/xml.go @@ -0,0 +1,87 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package res + +import ( + "encoding/xml" +) + +var ( + // IDAttrName is the android:id attribute xml name. + // It appears anywhere an xml document wishes to associate a tag to a given android id. + IDAttrName = xml.Name{Space: "http://schemas.android.com/apk/res/android", Local: "id"} + + // ResourcesTagName tag wraps all xml documents in res/values directory. These + // documents are reasonably well structured, and its children _normally_ end up becoming + // ResourceValues in Android. The exception being and which + // define how to interpret and store attributes in xml files outside of res/values. + ResourcesTagName = xml.Name{Local: "resources"} + + // ItemTagName is used in various ways in a tag. If it is a direct child, it can + // only denote an id resource. Otherwise, it can be a child of array/*-array and denotes the + // type and wraps the value of the item of the array. + ItemTagName = xml.Name{Local: "item"} + + // NameAttrName is an attribute that is expected to be encountered on every tag that is a + // direct child of . The value of this tag is the name of the resource that is + // being generated. + NameAttrName = xml.Name{Local: "name"} + + // TypeAttrName is the type attribute xml name. + // It appears in the tag when the item wants to specify its type. + TypeAttrName = xml.Name{Local: "type"} + + // EnumTagName appears beneath tags to define valid enum values for an attribute. + EnumTagName = xml.Name{Local: "enum"} + + // FlagTagName appears beneath tags to define valid flag values for an attribute. + FlagTagName = xml.Name{Local: "flag"} + + // ResourcesTagToType maps the child tag name of resources to the resource type it will generate. + ResourcesTagToType = map[string]Type{ + "array": Array, + "integer-array": Array, + "string-array": Array, + "attr": Attr, + "^attr-private": AttrPrivate, + "bool": Bool, + "color": Color, + "configVarying": ConfigVarying, + "dimen": Dimen, + "drawable": Drawable, + "fraction": Fraction, + "id": ID, + "integer": Integer, + "layout": Layout, + "plurals": Plurals, + "string": String, + "style": Style, + "declare-styleable": Styleable, + } + + // ResourcesChildToSkip a map containing child tags that can be skipped while parsing resources. + ResourcesChildToSkip = map[xml.Name]bool{ + {Local: "skip"}: true, + {Local: "eat-comment"}: true, + {Local: "public"}: true, + } +) + +const ( + // GeneratedIDPrefix prefixes an attribute value whose name is IDAttrName, it indicates that + // this id likely does not exist outside of the current document and a new Id Resource for + // this value. + GeneratedIDPrefix = "@+id" +) -- cgit v1.2.3 From 6d7846ba7dc177debaf733a9d0dccd0e475e41bf Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 14 Nov 2022 17:57:05 -0800 Subject: Migrate from `cfg = "host"` to `cfg = "exec"` PiperOrigin-RevId: 488516992 Change-Id: I72e335bf3f7a592fb0349caed9cb85823cd65820 --- rules/aar_import/attrs.bzl | 2 +- rules/android_application/attrs.bzl | 8 +++--- rules/android_library/attrs.bzl | 2 +- rules/attrs.bzl | 48 ++++++++++++++++----------------- rules/toolchains/emulator/toolchain.bzl | 8 +++--- toolchains/emulator/toolchain.bzl | 8 +++--- 6 files changed, 38 insertions(+), 38 deletions(-) diff --git a/rules/aar_import/attrs.bzl b/rules/aar_import/attrs.bzl index a76cdea..6fefdb0 100644 --- a/rules/aar_import/attrs.bzl +++ b/rules/aar_import/attrs.bzl @@ -64,7 +64,7 @@ ATTRS = _attrs.add( default = Label("//tools/jdk:toolchain_android_only"), ), _host_javabase = attr.label( - cfg = "host", + cfg = "exec", default = Label("//tools/jdk:current_java_runtime"), ), ), diff --git a/rules/android_application/attrs.bzl b/rules/android_application/attrs.bzl index 8084388..131fb46 100644 --- a/rules/android_application/attrs.bzl +++ b/rules/android_application/attrs.bzl @@ -52,7 +52,7 @@ ANDROID_APPLICATION_ATTRS = _attrs.add( ), _feature_manifest_script = attr.label( allow_single_file = True, - cfg = "host", + cfg = "exec", executable = True, default = ":gen_android_feature_manifest.sh", ), @@ -67,12 +67,12 @@ ANDROID_APPLICATION_ATTRS = _attrs.add( ), _priority_feature_manifest_script = attr.label( allow_single_file = True, - cfg = "host", + cfg = "exec", executable = True, default = ":gen_priority_android_feature_manifest.sh", ), _host_javabase = attr.label( - cfg = "host", + cfg = "exec", default = Label("//tools/jdk:current_java_runtime"), ), ), @@ -92,7 +92,7 @@ ANDROID_FEATURE_MODULE_ATTRS = dict( title_lib = attr.string(), _feature_module_validation_script = attr.label( allow_single_file = True, - cfg = "host", + cfg = "exec", executable = True, default = ":feature_module_validation.sh", ), diff --git a/rules/android_library/attrs.bzl b/rules/android_library/attrs.bzl index 9fc3214..8924e70 100644 --- a/rules/android_library/attrs.bzl +++ b/rules/android_library/attrs.bzl @@ -54,7 +54,7 @@ ATTRS = _attrs.add( providers = [ [JavaPluginInfo], ], - cfg = "host", + cfg = "exec", doc = ( "The list of [java_plugin](https://docs.bazel.build/versions/main/be/java.html#java_plugin)s " + "(e.g. annotation processors) to export to libraries that directly depend on this library. " + diff --git a/rules/attrs.bzl b/rules/attrs.bzl index 43408ab..fcab354 100644 --- a/rules/attrs.bzl +++ b/rules/attrs.bzl @@ -78,7 +78,7 @@ _tristate = struct( _JAVA_RUNTIME = dict( _host_javabase = attr.label( - cfg = "host", + cfg = "exec", default = Label("//tools/jdk:current_java_runtime"), ), ) @@ -152,7 +152,7 @@ _COMPILATION = _add( ), plugins = attr.label_list( providers = [JavaPluginInfo], - cfg = "host", + cfg = "exec", doc = ( "Java compiler plugins to run at compile-time. " + "Every `java_plugin` specified in the plugins attribute will be run whenever this rule is built. " + @@ -188,23 +188,23 @@ _DATA_CONTEXT = _add( dict( # Additional attrs needed for AndroidDataContext _add_g3itr_xslt = attr.label( - cfg = "host", + cfg = "exec", default = Label("//tools/android/xslt:add_g3itr.xslt"), allow_single_file = True, ), _android_manifest_merge_tool = attr.label( - cfg = "host", + cfg = "exec", default = Label("//tools/android:merge_manifests"), executable = True, ), # TODO(b/145617058) Switching back to head RPBB until the Android rules release process is improved _android_resources_busybox = attr.label( - cfg = "host", + cfg = "exec", default = Label("//rules:ResourceProcessorBusyBox"), executable = True, ), _xsltproc_tool = attr.label( - cfg = "host", + cfg = "exec", default = Label("//tools/android/xslt:xslt"), allow_files = True, ), @@ -221,18 +221,18 @@ _DATA_CONTEXT = _add( ANDROID_SDK_ATTRS = dict( aapt = attr.label( allow_single_file = True, - cfg = "host", + cfg = "exec", executable = True, mandatory = True, ), aapt2 = attr.label( allow_single_file = True, - cfg = "host", + cfg = "exec", executable = True, ), aidl = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", executable = True, mandatory = True, ), @@ -241,80 +241,80 @@ ANDROID_SDK_ATTRS = dict( ), android_jar = attr.label( allow_single_file = [".jar"], - cfg = "host", + cfg = "exec", mandatory = True, ), annotations_jar = attr.label( allow_single_file = [".jar"], - cfg = "host", + cfg = "exec", ), apkbuilder = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", executable = True, ), apksigner = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", executable = True, mandatory = True, ), adb = attr.label( allow_single_file = True, - cfg = "host", + cfg = "exec", executable = True, mandatory = True, ), build_tools_version = attr.string(), dx = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", executable = True, mandatory = True, ), framework_aidl = attr.label( allow_single_file = True, - cfg = "host", + cfg = "exec", mandatory = True, ), legacy_main_dex_list_generator = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", executable = True, ), main_dex_classes = attr.label( allow_single_file = True, - cfg = "host", + cfg = "exec", mandatory = True, ), main_dex_list_creator = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", executable = True, mandatory = True, ), proguard = attr.label( allow_files = True, - cfg = "host", + cfg = "exec", executable = True, mandatory = True, ), shrinked_android_jar = attr.label( allow_single_file = True, - cfg = "host", + cfg = "exec", ), source_properties = attr.label( allow_single_file = True, - cfg = "host", + cfg = "exec", ), zipalign = attr.label( allow_single_file = True, - cfg = "host", + cfg = "exec", executable = True, mandatory = True, ), _proguard = attr.label( - cfg = "host", + cfg = "exec", default = configuration_field( fragment = "java", name = "proguard_top", diff --git a/rules/toolchains/emulator/toolchain.bzl b/rules/toolchains/emulator/toolchain.bzl index 61bfb77..6bf2e0f 100644 --- a/rules/toolchains/emulator/toolchain.bzl +++ b/rules/toolchains/emulator/toolchain.bzl @@ -38,20 +38,20 @@ emulator_toolchain = rule( attrs = { "emulator": attr.label( allow_files = True, - cfg = "host", + cfg = "exec", mandatory = True, ), "emulator_deps": attr.label_list( allow_files = True, - cfg = "host", + cfg = "exec", ), "emulator_head": attr.label( allow_files = True, - cfg = "host", + cfg = "exec", ), "emulator_head_deps": attr.label_list( allow_files = True, - cfg = "host", + cfg = "exec", ), "emulator_suffix": attr.string(default = ""), "emulator_head_suffix": attr.string(default = ""), diff --git a/toolchains/emulator/toolchain.bzl b/toolchains/emulator/toolchain.bzl index 61bfb77..6bf2e0f 100644 --- a/toolchains/emulator/toolchain.bzl +++ b/toolchains/emulator/toolchain.bzl @@ -38,20 +38,20 @@ emulator_toolchain = rule( attrs = { "emulator": attr.label( allow_files = True, - cfg = "host", + cfg = "exec", mandatory = True, ), "emulator_deps": attr.label_list( allow_files = True, - cfg = "host", + cfg = "exec", ), "emulator_head": attr.label( allow_files = True, - cfg = "host", + cfg = "exec", ), "emulator_head_deps": attr.label_list( allow_files = True, - cfg = "host", + cfg = "exec", ), "emulator_suffix": attr.string(default = ""), "emulator_head_suffix": attr.string(default = ""), -- cgit v1.2.3 From d21a76197b32b6f8be6139f354cbddc51b9f0f9b Mon Sep 17 00:00:00 2001 From: A Googler Date: Mon, 21 Nov 2022 18:53:34 -0800 Subject: The option (whose default value is false) is used to switch the AIDL compiler from the custom-build one one to the upstream AOSP AIDL compiler, which supports more new language features that the former doesn't provide. PiperOrigin-RevId: 490124198 Change-Id: I665d1b65a1ed203c0d7b789c7c80a1fd66685bf2 --- rules/acls.bzl | 6 ++++++ .../acls/android_library_use_aosp_aidl_compiler.bzl | 18 ++++++++++++++++++ rules/android_library/attrs.bzl | 15 +++++++++++++++ rules/android_library/impl.bzl | 10 ++++++++++ rules/idl.bzl | 21 ++++++++++++++++++--- 5 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 rules/acls/android_library_use_aosp_aidl_compiler.bzl diff --git a/rules/acls.bzl b/rules/acls.bzl index b038c29..30314db 100644 --- a/rules/acls.bzl +++ b/rules/acls.bzl @@ -44,6 +44,7 @@ load("//rules/acls:android_binary_starlark_javac.bzl", "ANDROID_BINARY_STARLARK_ load("//rules/acls:android_feature_splits_dogfood.bzl", "ANDROID_FEATURE_SPLITS_DOGFOOD") load("//rules/acls:android_library_resources_without_srcs.bzl", "ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS", "ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS") load("//rules/acls:android_library_starlark_resource_outputs.bzl", "ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK", "ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT") +load("//rules/acls:android_library_use_aosp_aidl_compiler.bzl", "ANDROID_LIBRARY_USE_AOSP_AIDL_COMPILER_ALLOWLIST") load("//rules/acls:android_lint_checks_rollout.bzl", "ANDROID_LINT_CHECKS_FALLBACK", "ANDROID_LINT_CHECKS_ROLLOUT") load("//rules/acls:android_lint_rollout.bzl", "ANDROID_LINT_FALLBACK", "ANDROID_LINT_ROLLOUT") load("//rules/acls:lint_registry_rollout.bzl", "LINT_REGISTRY_FALLBACK", "LINT_REGISTRY_ROLLOUT") @@ -136,6 +137,9 @@ def _in_android_library_resources_without_srcs_generator_functions(gfn): def _in_android_library_starlark_resource_outputs_rollout(fqn): return not matches(fqn, ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK_DICT) and matches(fqn, ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT_DICT) +def _in_android_library_use_aosp_aidl_compiler_allowlist(fqn): + return matches(fqn, ANDROID_LIBRARY_USE_AOSP_AIDL_COMPILER_ALLOWLIST_DICT) + def _in_app_installation_snapshot(fqn): return not matches(fqn, APP_INSTALLATION_SNAPSHOT_FALLBACK_DICT) and matches(fqn, APP_INSTALLATION_SNAPSHOT_DICT) @@ -276,6 +280,7 @@ BASELINE_PROFILES_ROLLOUT_DICT = make_dict(BASELINE_PROFILES_ROLLOUT) ENFORCE_MIN_SDK_FLOOR_ROLLOUT_DICT = make_dict(ENFORCE_MIN_SDK_FLOOR_ROLLOUT) ENFORCE_MIN_SDK_FLOOR_FALLBACK_DICT = make_dict(ENFORCE_MIN_SDK_FLOOR_FALLBACK) ANDROID_APK_TO_BUNDLE_FEATURES_DICT = make_dict(ANDROID_APK_TO_BUNDLE_FEATURES) +ANDROID_LIBRARY_USE_AOSP_AIDL_COMPILER_ALLOWLIST_DICT = make_dict(ANDROID_LIBRARY_USE_AOSP_AIDL_COMPILER_ALLOWLIST) def matches(fqn, dct): # Labels with workspace names ("@workspace//pkg:target") are not supported. @@ -330,6 +335,7 @@ acls = struct( in_android_library_starlark_resource_outputs_rollout = _in_android_library_starlark_resource_outputs_rollout, in_android_library_resources_without_srcs = _in_android_library_resources_without_srcs, in_android_library_resources_without_srcs_generator_functions = _in_android_library_resources_without_srcs_generator_functions, + in_android_library_use_aosp_aidl_compiler_allowlist = _in_android_library_use_aosp_aidl_compiler_allowlist, in_android_lint_checks_rollout = _in_android_lint_checks_rollout, in_android_lint_rollout = _in_android_lint_rollout, in_lint_registry_rollout = _in_lint_registry_rollout, diff --git a/rules/acls/android_library_use_aosp_aidl_compiler.bzl b/rules/acls/android_library_use_aosp_aidl_compiler.bzl new file mode 100644 index 0000000..3825458 --- /dev/null +++ b/rules/acls/android_library_use_aosp_aidl_compiler.bzl @@ -0,0 +1,18 @@ +# Copyright 2022 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 3.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow list for the `idl_use_aosp_compiler` attribute in the `android_library` rule.""" + +ANDROID_LIBRARY_USE_AOSP_AIDL_COMPILER_ALLOWLIST = [ +] diff --git a/rules/android_library/attrs.bzl b/rules/android_library/attrs.bzl index 8924e70..468ac9d 100644 --- a/rules/android_library/attrs.bzl +++ b/rules/android_library/attrs.bzl @@ -137,6 +137,21 @@ ATTRS = _attrs.add( "for information about what this means." ), ), + idl_uses_aosp_compiler = attr.bool( + default = False, + doc = ( + "Use the upstream AOSP compiler to generate Java files out of `idl_srcs`." + + "The upstream AOSP compiler provides several new language features that the " + + "Google3-only compiler doesn't provide. For example: structured parcelables, " + + "unions, enums, nested type declarations, constant expressions, annotations, " + + "and more. " + + "See [AIDL Doc](https://source.android.com/docs/core/architecture/aidl/overview) " + + "for more details. " + + "Note: the use of the AOSP compiler in google3 is restricted due to performance " + + "considerations. This should not be broadly used unless these features are " + + "strictly required." + ), + ), neverlink = attr.bool( default = False, doc = ( diff --git a/rules/android_library/impl.bzl b/rules/android_library/impl.bzl index f508c1b..8faa6fa 100644 --- a/rules/android_library/impl.bzl +++ b/rules/android_library/impl.bzl @@ -52,6 +52,10 @@ _IDL_SRC_FROM_DIFFERENT_PACKAGE_ERROR = ( "package or depend on an appropriate rule there." ) +_IDL_USES_AOSP_COMPILER_ERROR = ( + "Use of `idl_uses_aosp_compiler` is not allowed for %s." +) + # Android library AAR context attributes. _PROVIDERS = "providers" _VALIDATION_OUTPUTS = "validation_outputs" @@ -99,6 +103,11 @@ def _validate_rule_context(ctx): if ctx.label.package != idl_src.label.package: log.error(_IDL_SRC_FROM_DIFFERENT_PACKAGE_ERROR % idl_src.label) + # Ensure that the AOSP AIDL compiler is used only in allowlisted packages + if (ctx.attr.idl_uses_aosp_compiler and + not acls.in_android_library_use_aosp_aidl_compiler_allowlist(str(ctx.label))): + log.error(_IDL_USES_AOSP_COMPILER_ERROR % ctx.label) + return struct( enable_deps_without_srcs = _check_deps_without_java_srcs(ctx), ) @@ -201,6 +210,7 @@ def _process_idl(ctx, **unused_sub_ctxs): aidl = get_android_sdk(ctx).aidl, aidl_lib = get_android_sdk(ctx).aidl_lib, aidl_framework = get_android_sdk(ctx).framework_aidl, + uses_aosp_compiler = ctx.attr.idl_uses_aosp_compiler, ), ) diff --git a/rules/idl.bzl b/rules/idl.bzl index 8dc52d3..ee0ed21 100644 --- a/rules/idl.bzl +++ b/rules/idl.bzl @@ -47,9 +47,16 @@ def _gen_java_from_idl( transitive_idl_preprocessed = [], aidl = None, aidl_lib = None, - aidl_framework = None): + aidl_framework = None, + uses_aosp_compiler = False): args = ctx.actions.args() - args.add("-b") + + # Note: at the moment (2022/11/07), the flags that the AOSP compiler accepts is a superset of + # the Google3 compiler, but that might not be true in the future. + if uses_aosp_compiler: + args.add("--use-aosp-compiler") + + args.add("-b") # fail on parcelable args.add_all(transitive_idl_import_roots, format_each = "-I%s") args.add(aidl_framework, format = "-p%s") args.add_all(transitive_idl_preprocessed, format_each = "-p%s") @@ -129,7 +136,8 @@ def _process( exports = [], aidl = None, aidl_lib = None, - aidl_framework = None): + aidl_framework = None, + uses_aosp_compiler = False): """Processes Android IDL. Args: @@ -179,6 +187,12 @@ def _process( Optional, unless idl_srcs are supplied. aidl_framework: Target. A target pointing to the aidl framework. Optional, unless idl_srcs are supplied. + uses_aosp_compiler: boolean. If True, the upstream AOSP AIDL compiler is + used instead of the Google3-only AIDL compiler. This allows wider range + of AIDL language features including the structured parcelable, enum, + union, and many more. On the other hand, using this may cause noticeable + regression in terms of code size and performance as the compiler doesn't + implement several optimization techniques that the Google3 compiler has. Returns: A IDLContextInfo provider. @@ -224,6 +238,7 @@ def _process( aidl = aidl, aidl_lib = aidl_lib, aidl_framework = aidl_framework, + uses_aosp_compiler = uses_aosp_compiler, ) return IDLContextInfo( -- cgit v1.2.3 From ba13b29687bf3588175ae9f4f284e13e51e3728c Mon Sep 17 00:00:00 2001 From: A Googler Date: Fri, 25 Nov 2022 04:16:14 -0800 Subject: Avoid using named arguments for Starlark built-in function `dict.setdefault()`, `dict.get()` and `dict.pop()`. PiperOrigin-RevId: 490881960 Change-Id: Ie0cc002f846bfc8cca1f53f634e172e67898a4f3 --- rules/android_application/android_application_rule.bzl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rules/android_application/android_application_rule.bzl b/rules/android_application/android_application_rule.bzl index d539620..5def705 100644 --- a/rules/android_application/android_application_rule.bzl +++ b/rules/android_application/android_application_rule.bzl @@ -57,7 +57,7 @@ def _verify_attrs(attrs, fqn): if hasattr(attrs, attr): _log.error("Unsupported attr: %s in android_application" % attr) - if not attrs.get("manifest_values", default = {}).get("applicationId"): + if not attrs.get("manifest_values", {}).get("applicationId"): _log.error("%s missing required applicationId in manifest_values" % fqn) for attr in ["deps"]: @@ -351,8 +351,8 @@ def android_application_macro(_android_binary, **attrs): fqn = "//%s:%s" % (native.package_name(), attrs["name"]) # Must pop these because android_binary does not have these attributes. - app_integrity_config = attrs.pop("app_integrity_config", default = None) - rotation_config = attrs.pop("rotation_config", default = None) + app_integrity_config = attrs.pop("app_integrity_config", None) + rotation_config = attrs.pop("rotation_config", None) # Simply fall back to android_binary if no feature splits or bundle_config if not attrs.get("feature_modules", None) and not (attrs.get("bundle_config", None) or attrs.get("bundle_config_file", None)): @@ -366,9 +366,9 @@ def android_application_macro(_android_binary, **attrs): base_split_name = "%s_base" % name # default to [] if feature_modules = None is passed - feature_modules = attrs.pop("feature_modules", default = []) or [] - bundle_config = attrs.pop("bundle_config", default = None) - bundle_config_file = attrs.pop("bundle_config_file", default = None) + feature_modules = attrs.pop("feature_modules", []) or [] + bundle_config = attrs.pop("bundle_config", None) + bundle_config_file = attrs.pop("bundle_config_file", None) # bundle_config is deprecated in favor of bundle_config_file # In the future bundle_config will accept a build rule rather than a raw file. -- cgit v1.2.3 From 5ce3ffec01eeca190c5ed1840e00e368222d799b Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Thu, 1 Dec 2022 14:57:32 -0500 Subject: Clean up rules_android WORKSPACE * Separate http_archives into prereqs.bzl * Move all dep macro calls and external package manager calls (go_repository, maven_install) into defs.bzl Significantly reduces the bloat in WORKSPACE, and makes integration by users much easier (only need to call rules_android_prereqs() and rules_android_workspace()). NOTE: This will eventually be deprecated once we make bzlmod the default dependency manager for rules_android by mandating --[experimental_]enable_bzlmod in user bazelrc files. --- WORKSPACE | 99 +++++-------------------------------------------------------- defs.bzl | 36 ++++++++++++++++++++++ prereqs.bzl | 79 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 92 deletions(-) create mode 100644 prereqs.bzl diff --git a/WORKSPACE b/WORKSPACE index c691e57..d816b52 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -3,37 +3,6 @@ workspace(name = "build_bazel_rules_android") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") -maybe( - http_archive, - name = "remote_java_tools_for_rules_android", - sha256 = "8fb4d3138bd92a9d3324dae29c9f70d91ca2db18cd0bf1997446eed4657d19b3", - urls = [ - "https://mirror.bazel.build/bazel_java_tools/releases/java/v11.8/java_tools-v11.8.zip", - "https://github.com/bazelbuild/java_tools/releases/download/java_v11.8/java_tools-v11.8.zip", - ], -) - -maybe( - http_archive, - name = "com_google_protobuf", - sha256 = "87407cd28e7a9c95d9f61a098a53cf031109d451a7763e7dd1253abf8b4df422", - strip_prefix = "protobuf-3.19.1", - urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.19.1.tar.gz"], -) - -load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") -protobuf_deps() - -maybe( - http_archive, - name = "rules_jvm_external", - strip_prefix = "rules_jvm_external-fa73b1a8e4846cee88240d0019b8f80d39feb1c3", - sha256 = "7e13e48b50f9505e8a99cc5a16c557cbe826e9b68d733050cd1e318d69f94bb5", - url = "https://github.com/bazelbuild/rules_jvm_external/archive/fa73b1a8e4846cee88240d0019b8f80d39feb1c3.zip", -) - -load("defs.bzl", "rules_android_workspace") - maybe( android_sdk_repository, name = "androidsdk", @@ -44,68 +13,14 @@ maybe( name = "androidndk", ) -maybe( - http_archive, - name = "bazel_skylib", - sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c", - urls = [ - "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz", - "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz", - ], -) -register_toolchains("//toolchains/android:all") -register_toolchains("//toolchains/android_sdk:all") -register_toolchains("//toolchains/emulator:all") - -maybe( - http_archive, - name = "io_bazel_rules_go", - sha256 = "16e9fca53ed6bd4ff4ad76facc9b7b651a89db1689a2877d6fd7b82aa824e366", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.34.0/rules_go-v0.34.0.zip", - "https://github.com/bazelbuild/rules_go/releases/download/v0.34.0/rules_go-v0.34.0.zip", - ], -) - -maybe( - http_archive, - name = "bazel_gazelle", - sha256 = "5982e5463f171da99e3bdaeff8c0f48283a7a5f396ec5282910b9e8a49c0dd7e", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.25.0/bazel-gazelle-v0.25.0.tar.gz", - "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.25.0/bazel-gazelle-v0.25.0.tar.gz", - ], -) - -load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") -load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") -go_rules_dependencies() - -go_register_toolchains(version = "1.18.3") - -gazelle_dependencies() -# gazelle:repository go_repository name=org_golang_x_xerrors importpath=golang.org/x/xerrors - -go_repository( - name = "org_golang_google_protobuf", - importpath = "google.golang.org/protobuf", - sum = "h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=", - version = "v1.28.1", -) - -go_repository( - name = "com_github_google_go_cmp", - importpath = "github.com/google/go-cmp", - sum = "h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=", - version = "v0.5.9", -) +load("prereqs.bzl", "rules_android_prereqs") +rules_android_prereqs() -go_repository( - name = "org_golang_x_sync", - importpath = "golang.org/x/sync", - sum = "h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=", - version = "v0.0.0-20210220032951-036812b2e83c", -) +load("defs.bzl", "rules_android_workspace") rules_android_workspace() +register_toolchains("//toolchains/android:all") +register_toolchains("//toolchains/android_sdk:all") +register_toolchains("//toolchains/emulator:all") + diff --git a/defs.bzl b/defs.bzl index 4d08708..72d5339 100644 --- a/defs.bzl +++ b/defs.bzl @@ -14,10 +14,16 @@ """Workspace setup macro for rules_android.""" +load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") +load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") +load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") load("@rules_jvm_external//:defs.bzl", "maven_install") def rules_android_workspace(): """ Sets up workspace dependencies for rules_android.""" + protobuf_deps() maven_install( name = "rules_android_maven", @@ -29,3 +35,33 @@ def rules_android_workspace(): "https://repo1.maven.org/maven2", ], ) + + go_rules_dependencies() + + go_register_toolchains(version = "1.18.3") + + gazelle_dependencies() + # gazelle:repository go_repository name=org_golang_x_xerrors importpath=golang.org/x/xerrors + + go_repository( + name = "org_golang_google_protobuf", + importpath = "google.golang.org/protobuf", + sum = "h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=", + version = "v1.28.1", + ) + + go_repository( + name = "com_github_google_go_cmp", + importpath = "github.com/google/go-cmp", + sum = "h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=", + version = "v0.5.9", + ) + + go_repository( + name = "org_golang_x_sync", + importpath = "golang.org/x/sync", + sum = "h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=", + version = "v0.0.0-20210220032951-036812b2e83c", + ) + + diff --git a/prereqs.bzl b/prereqs.bzl new file mode 100644 index 0000000..09cfeec --- /dev/null +++ b/prereqs.bzl @@ -0,0 +1,79 @@ +# Copyright 2022 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Sets up prerequisites for rules_android.""" + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") + + +def rules_android_prereqs(): + """Downloads prerequisite repositories for rules_android.""" + maybe( + http_archive, + name = "rules_jvm_external", + strip_prefix = "rules_jvm_external-fa73b1a8e4846cee88240d0019b8f80d39feb1c3", + sha256 = "7e13e48b50f9505e8a99cc5a16c557cbe826e9b68d733050cd1e318d69f94bb5", + url = "https://github.com/bazelbuild/rules_jvm_external/archive/fa73b1a8e4846cee88240d0019b8f80d39feb1c3.zip", + ) + + maybe( + http_archive, + name = "com_google_protobuf", + sha256 = "87407cd28e7a9c95d9f61a098a53cf031109d451a7763e7dd1253abf8b4df422", + strip_prefix = "protobuf-3.19.1", + urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.19.1.tar.gz"], + ) + + maybe( + http_archive, + name = "remote_java_tools_for_rules_android", + sha256 = "8fb4d3138bd92a9d3324dae29c9f70d91ca2db18cd0bf1997446eed4657d19b3", + urls = [ + "https://mirror.bazel.build/bazel_java_tools/releases/java/v11.8/java_tools-v11.8.zip", + "https://github.com/bazelbuild/java_tools/releases/download/java_v11.8/java_tools-v11.8.zip", + ], + ) + + maybe( + http_archive, + name = "bazel_skylib", + sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c", + urls = [ + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz", + ], + ) + + maybe( + http_archive, + name = "io_bazel_rules_go", + sha256 = "16e9fca53ed6bd4ff4ad76facc9b7b651a89db1689a2877d6fd7b82aa824e366", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.34.0/rules_go-v0.34.0.zip", + "https://github.com/bazelbuild/rules_go/releases/download/v0.34.0/rules_go-v0.34.0.zip", + ], + ) + + maybe( + http_archive, + name = "bazel_gazelle", + sha256 = "5982e5463f171da99e3bdaeff8c0f48283a7a5f396ec5282910b9e8a49c0dd7e", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.25.0/bazel-gazelle-v0.25.0.tar.gz", + "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.25.0/bazel-gazelle-v0.25.0.tar.gz", + ], + ) + + -- cgit v1.2.3 From ef10f4b65e628c0b20fab2b0d2649c9ab9a05e88 Mon Sep 17 00:00:00 2001 From: nickreid Date: Fri, 2 Dec 2022 12:52:14 -0800 Subject: Check for common_srcs in _uses_deprecated_implicit_export and _uses_resources_and_deps_without_srcs PiperOrigin-RevId: 492537447 Change-Id: I5afc69a79efe607c4fab74506625b26b65c6d5b8 --- rules/android_library/impl.bzl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/rules/android_library/impl.bzl b/rules/android_library/impl.bzl index 8faa6fa..e2bfc2a 100644 --- a/rules/android_library/impl.bzl +++ b/rules/android_library/impl.bzl @@ -68,9 +68,11 @@ _AARContextInfo = provider( }, ) +def _has_srcs(ctx): + return ctx.files.srcs or ctx.files.idl_srcs or getattr(ctx.files, "common_srcs", False) + def _uses_deprecated_implicit_export(ctx): - return (ctx.attr.deps and not (ctx.files.srcs or - ctx.files.idl_srcs or + return (ctx.attr.deps and not (_has_srcs(ctx) or ctx.attr._defined_assets or ctx.files.resource_files or ctx.attr.manifest)) @@ -78,10 +80,10 @@ def _uses_deprecated_implicit_export(ctx): def _uses_resources_and_deps_without_srcs(ctx): return (ctx.attr.deps and (ctx.attr._defined_assets or ctx.files.resource_files or ctx.attr.manifest) and - not (ctx.files.srcs or ctx.files.idl_srcs)) + not _has_srcs(ctx)) def _check_deps_without_java_srcs(ctx): - if not ctx.attr.deps or ctx.files.srcs or ctx.files.idl_srcs: + if not ctx.attr.deps or _has_srcs(ctx): return False gfn = getattr(ctx.attr, "generator_function", "") if _uses_deprecated_implicit_export(ctx): -- cgit v1.2.3 From 1e029d670726bad33dad21ab01859466f4815301 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Wed, 14 Dec 2022 14:02:13 -0800 Subject: bzlmod-ify rules_android external dependencies * Relocates all WORKSPACE and macro dependency loading to MODULE.bazel * NOTE: Cannot fully switch over to blank WORKSPACE.bzlmod yet due to reliance on workspace suffixing and bind() in Android rules; to be fixed. Closes #54 PiperOrigin-RevId: 495410564 Change-Id: I77059752b81e1038f2143003554278608eeb3178 --- MODULE.bazel | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ WORKSPACE.bzlmod | 14 ++++++++++++++ go.mod | 9 +++++++++ go.sum | 10 ++++++++++ 4 files changed, 81 insertions(+) create mode 100644 MODULE.bazel create mode 100644 WORKSPACE.bzlmod create mode 100644 go.mod create mode 100644 go.sum diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 0000000..e5a32bd --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,48 @@ +module( + name = "bazel_build_rules_android", + version = "0.2.0", +) + +bazel_dep(name = "platforms", version = "0.0.5") +bazel_dep(name = "rules_java", version = "5.3.5") +rules_java_toolchains = use_extension("@rules_java//java:extensions.bzl", "toolchains") +use_repo(rules_java_toolchains, "remote_java_tools") + +bazel_dep(name = "protobuf", version = "3.19.0", repo_name = "com_google_protobuf") +bazel_dep(name = "rules_jvm_external", version = "4.5") +bazel_dep(name = "bazel_skylib", version = "1.0.3") + +register_toolchains("//toolchains/android:all") +register_toolchains("//toolchains/android_sdk:all") +register_toolchains("//toolchains/emulator:all") + +# go-related dependency setup +bazel_dep(name = "rules_go", version = "0.34.0", repo_name = "io_bazel_rules_go") +bazel_dep(name = "gazelle", version = "0.28.0") +go_sdk = use_extension("@io_bazel_rules_go//go:extensions.bzl", "go_sdk") +go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps") +go_deps.from_file(go_mod = "//:go.mod") +use_repo( + go_deps, + "org_golang_google_protobuf", + "com_github_google_go_cmp", + "org_golang_x_sync", +) +maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven") +maven.install( + name = "rules_android_maven", + artifacts = [ + "com.android.tools.build:bundletool:1.6.1", + ], + repositories = [ + "https://maven.google.com", + "https://repo1.maven.org/maven2", + ], +) +use_repo( + maven, + "rules_android_maven" +) + +remote_android_extensions = use_extension("@bazel_tools//tools/android:android_extensions.bzl", "remote_android_tools_extensions") +use_repo(remote_android_extensions, "android_tools", "android_gmaven_r8") diff --git a/WORKSPACE.bzlmod b/WORKSPACE.bzlmod new file mode 100644 index 0000000..0aca21e --- /dev/null +++ b/WORKSPACE.bzlmod @@ -0,0 +1,14 @@ +workspace(name = "build_bazel_rules_android") + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") + +maybe( + android_sdk_repository, + name = "androidsdk", +) + +maybe( + android_ndk_repository, + name = "androidndk", +) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..68f0c90 --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module github.com/bazelbuild/rules_android + +go 1.18 + +require ( + github.com/google/go-cmp v0.5.9 + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + google.golang.org/protobuf v1.28.1 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..23ba436 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -- cgit v1.2.3 From 0dec591a780ef7404feb81873d5e8c7e5a0f2e55 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Wed, 14 Dec 2022 15:04:03 -0800 Subject: OSS release of AK's bucketize and extractaar tools PiperOrigin-RevId: 495427373 Change-Id: Ib8c99985a26a5fd181b8ee5cd7c61cdce26f8c69 --- kokoro/presubmit/kokoro_presubmit.sh | 2 +- src/tools/ak/akhelper.go | 27 ++ src/tools/ak/bucketize/BUILD | 58 ++++ src/tools/ak/bucketize/bucketize.go | 451 +++++++++++++++++++++++++++ src/tools/ak/bucketize/bucketize_bin.go | 29 ++ src/tools/ak/bucketize/bucketize_test.go | 483 +++++++++++++++++++++++++++++ src/tools/ak/bucketize/partitioner.go | 319 +++++++++++++++++++ src/tools/ak/bucketize/partitioner_test.go | 349 +++++++++++++++++++++ src/tools/ak/bucketize/pipe.go | 154 +++++++++ src/tools/ak/bucketize/pipe_test.go | 75 +++++ src/tools/ak/extractaar/BUILD | 44 +++ src/tools/ak/extractaar/buildozer.go | 48 +++ src/tools/ak/extractaar/extractaar.go | 286 +++++++++++++++++ src/tools/ak/extractaar/extractaar_bin.go | 29 ++ src/tools/ak/extractaar/extractaar_test.go | 73 +++++ src/tools/ak/extractaar/validator.go | 77 +++++ src/tools/ak/extractaar/validator_test.go | 175 +++++++++++ 17 files changed, 2678 insertions(+), 1 deletion(-) create mode 100644 src/tools/ak/akhelper.go create mode 100644 src/tools/ak/bucketize/BUILD create mode 100644 src/tools/ak/bucketize/bucketize.go create mode 100644 src/tools/ak/bucketize/bucketize_bin.go create mode 100644 src/tools/ak/bucketize/bucketize_test.go create mode 100644 src/tools/ak/bucketize/partitioner.go create mode 100644 src/tools/ak/bucketize/partitioner_test.go create mode 100644 src/tools/ak/bucketize/pipe.go create mode 100644 src/tools/ak/bucketize/pipe_test.go create mode 100644 src/tools/ak/extractaar/BUILD create mode 100644 src/tools/ak/extractaar/buildozer.go create mode 100644 src/tools/ak/extractaar/extractaar.go create mode 100644 src/tools/ak/extractaar/extractaar_bin.go create mode 100644 src/tools/ak/extractaar/extractaar_test.go create mode 100644 src/tools/ak/extractaar/validator.go create mode 100644 src/tools/ak/extractaar/validator_test.go diff --git a/kokoro/presubmit/kokoro_presubmit.sh b/kokoro/presubmit/kokoro_presubmit.sh index ed6eb5f..cdd6642 100644 --- a/kokoro/presubmit/kokoro_presubmit.sh +++ b/kokoro/presubmit/kokoro_presubmit.sh @@ -66,7 +66,7 @@ COMMON_ARGS=( # Go to rules_android workspace and run relevant tests. cd "${KOKORO_ARTIFACTS_DIR}/git/rules_android" "$bazel" test "${COMMON_ARGS[@]}" //src/common/golang/... \ - //src/tools/ak/{compile,dex,finalrjar,generatemanifest,link,manifest,mindex,res,rjar}/... + //src/tools/ak/{bucketize,compile,dex,extractaar,finalrjar,generatemanifest,link,manifest,mindex,res,rjar}/... # Go to basic app workspace in the source tree cd "${KOKORO_ARTIFACTS_DIR}/git/rules_android/examples/basicapp" diff --git a/src/tools/ak/akhelper.go b/src/tools/ak/akhelper.go new file mode 100644 index 0000000..5a91098 --- /dev/null +++ b/src/tools/ak/akhelper.go @@ -0,0 +1,27 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package akhelper provides globally used functions. +package akhelper + +import "strings" + +const ( + lnBreak = "\n " +) + +// FormatDesc returns an indented string with line breaks for each element in given string array. +func FormatDesc(desc []string) string { + return strings.Join(desc, lnBreak) +} diff --git a/src/tools/ak/bucketize/BUILD b/src/tools/ak/bucketize/BUILD new file mode 100644 index 0000000..750400a --- /dev/null +++ b/src/tools/ak/bucketize/BUILD @@ -0,0 +1,58 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +# Description: +# Package for bucketize module +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +go_binary( + name = "bucketize_bin", + srcs = ["bucketize_bin.go"], + deps = [ + ":bucketize", + "//src/common/golang:flagfile", + ], +) + +go_library( + name = "bucketize", + srcs = [ + "bucketize.go", + "partitioner.go", + "pipe.go", + ], + importpath = "src/tools/ak/bucketize/bucketize", + deps = [ + "//src/common/golang:flags", + "//src/common/golang:shard", + "//src/common/golang:walk", + "//src/common/golang:xml2", + "//src/tools/ak:akhelper", + "//src/tools/ak:types", + "//src/tools/ak/res", + ], +) + +go_test( + name = "bucketize_test", + size = "small", + srcs = [ + "bucketize_test.go", + "partitioner_test.go", + ], + embed = [":bucketize"], + deps = [ + "//src/common/golang:shard", + "//src/common/golang:walk", + "//src/tools/ak/res", + ], +) + +go_test( + name = "pipe_test", + size = "small", + srcs = ["pipe_test.go"], + embed = [":bucketize"], +) diff --git a/src/tools/ak/bucketize/bucketize.go b/src/tools/ak/bucketize/bucketize.go new file mode 100644 index 0000000..4e72b0f --- /dev/null +++ b/src/tools/ak/bucketize/bucketize.go @@ -0,0 +1,451 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package bucketize provides functionality to bucketize Android resources. +package bucketize + +import ( + "bytes" + "context" + "encoding/xml" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path" + "strings" + "sync" + + "src/common/golang/flags" + "src/common/golang/shard" + "src/common/golang/walk" + "src/common/golang/xml2" + "src/tools/ak/akhelper" + "src/tools/ak/res/res" + "src/tools/ak/types" +) + +const ( + numParsers = 25 +) + +// Archiver process the provided resource files and directories stores the data +type Archiver struct { + ResFiles []*res.PathInfo + Partitioner Partitioner +} + +// ResourcesAttribute correlates the attribute of a resources xml tag and the file where it originates +type ResourcesAttribute struct { + Attribute xml.Attr + ResFile *res.PathInfo +} + +var ( + // Cmd defines the command to run repack + Cmd = types.Command{ + Init: Init, + Run: Run, + Desc: desc, + Flags: []string{ + "res_paths", + "typed_outputs", + }, + } + + resPaths flags.StringList + typedOutputs flags.StringList + + initOnce sync.Once +) + +// Init initializes repack. +func Init() { + initOnce.Do(func() { + flag.Var(&resPaths, "res_paths", "List of res paths (a file or directory).") + flag.Var(&typedOutputs, "typed_outputs", akhelper.FormatDesc([]string{ + "A list of output file paths, each path prefixed with the res type it supports.", + ": i.e. string:/foo/bar/res-string-0.zip,string:/foo/bar/res-string-1.zip,...", + "The number of files per res type will determine shards."})) + }) +} + +func desc() string { + return "Bucketize Android resources." +} + +// MakeArchiver creates an Archiver +func makeArchiver(resFiles []string, p Partitioner) (*Archiver, error) { + pis, err := res.MakePathInfos(resFiles) + if err != nil { + return nil, fmt.Errorf("converting res path failed: %v", err) + } + return &Archiver{ResFiles: pis, Partitioner: p}, nil +} + +// Archive process the res directories and files of the archiver +func (a *Archiver) Archive(ctx context.Context) error { + ctx, cancel := context.WithCancel(prefixErr(ctx, "archive: ")) + defer cancel() + vPIC, nvPIC := separatePathInfosByValues(ctx, a.ResFiles) + vrCs := make([]<-chan *res.ValuesResource, 0, numParsers) + raCs := make([]<-chan *ResourcesAttribute, 0, numParsers) + errCs := make([]<-chan error, 0, numParsers) + for i := 0; i < numParsers; i++ { + vrC, raC, vErrC := handleValuesPathInfos(ctx, vPIC) + vrCs = append(vrCs, vrC) + raCs = append(raCs, raC) + errCs = append(errCs, vErrC) + } + mVRC := mergeValuesResourceStreams(ctx, vrCs) + mRAC := mergeResourcesAttributeStreams(ctx, raCs) + mErrC := mergeErrStreams(ctx, errCs) + return a.archive(ctx, nvPIC, mVRC, mRAC, mErrC) +} + +// archive takes PathInfo, ValuesResource and error channels and process the values given +func (a *Archiver) archive(ctx context.Context, piC <-chan *res.PathInfo, vrC <-chan *res.ValuesResource, raC <-chan *ResourcesAttribute, errC <-chan error) error { + var errs []error +Loop: + for piC != nil || vrC != nil || errC != nil || raC != nil { + select { + case e, ok := <-errC: + if !ok { + errC = nil + continue + } + errs = append(errs, e) + break Loop + case ra, ok := <-raC: + if !ok { + raC = nil + continue + } + a.Partitioner.CollectResourcesAttribute(ra) + case pi, ok := <-piC: + if !ok { + piC = nil + continue + } + a.Partitioner.CollectPathResource(*pi) + case vr, ok := <-vrC: + if !ok { + vrC = nil + continue + } + if err := a.Partitioner.CollectValues(vr); err != nil { + return fmt.Errorf("got error collecting values: %v", err) + } + } + } + + if len(errs) != 0 { + return errorf(ctx, "errors encountered: %v", errs) + } + if err := a.Partitioner.Close(); err != nil { + return fmt.Errorf("got error closing partitioner: %v", err) + } + return nil +} + +func handleValuesPathInfos(ctx context.Context, piC <-chan *res.PathInfo) (<-chan *res.ValuesResource, <-chan *ResourcesAttribute, <-chan error) { + vrC := make(chan *res.ValuesResource) + raC := make(chan *ResourcesAttribute) + errC := make(chan error) + go func() { + defer close(vrC) + defer close(raC) + defer close(errC) + for pi := range piC { + if !syncParse(prefixErr(ctx, fmt.Sprintf("%s values-parse: ", pi.Path)), pi, vrC, raC, errC) { + return + } + } + }() + return vrC, raC, errC +} + +func syncParse(ctx context.Context, pi *res.PathInfo, vrC chan<- *res.ValuesResource, raC chan<- *ResourcesAttribute, errC chan<- error) bool { + f, err := os.Open(pi.Path) + if err != nil { + return sendErr(ctx, errC, errorf(ctx, "open failed: %v", err)) + } + defer f.Close() + return syncParseReader(ctx, pi, xml.NewDecoder(f), vrC, raC, errC) +} + +func syncParseReader(ctx context.Context, pi *res.PathInfo, dec *xml.Decoder, vrC chan<- *res.ValuesResource, raC chan<- *ResourcesAttribute, errC chan<- error) bool { + // Shadow Encoder is used to track xml state, such as namespaces. The state will be inherited by child encoders. + parentEnc := xml2.NewEncoder(ioutil.Discard) + for { + t, err := dec.Token() + if err == io.EOF { + return true + } + if err != nil { + return sendErr(ctx, errC, errorf(ctx, "token failed: %v", err)) + } + if err := parentEnc.EncodeToken(t); err != nil { + return sendErr(ctx, errC, errorf(ctx, "encoding token token %s failed: %v", t, err)) + } + if se, ok := t.(xml.StartElement); ok && se.Name == res.ResourcesTagName { + for _, xmlAttr := range se.Attr { + raC <- &ResourcesAttribute{ResFile: pi, Attribute: xmlAttr} + } + // AAPT2 does not support a multiple resources sections in a single file and silently ignores + // subsequent resources sections. The parser will only parse the first resources tag and exit. + return parseRes(ctx, parentEnc, pi, dec, vrC, errC) + } + } +} + +func skipTag(se xml.StartElement) bool { + _, ok := res.ResourcesChildToSkip[se.Name] + return ok +} + +func parseRes(ctx context.Context, parentEnc *xml2.Encoder, pi *res.PathInfo, dec *xml.Decoder, vrC chan<- *res.ValuesResource, errC chan<- error) bool { + for { + t, err := dec.Token() + if err != nil { + return sendErr(ctx, errC, errorf(ctx, "extract token failed: %v", err)) + } + // Encode all tokens to the shadow Encoder at the top-level loop to keep track of any required xml state. + if err := parentEnc.EncodeToken(t); err != nil { + return sendErr(ctx, errC, errorf(ctx, "encoding token token %s failed: %v", t, err)) + } + switch t.(type) { + case xml.StartElement: + se := t.(xml.StartElement) + if skipTag(se) { + dec.Skip() + break + } + + fqn, err := extractFQN(se) + if err != nil { + return sendErr(ctx, errC, errorf(ctx, "extract name and type failed: %v", err)) + } + + b, err := extractElement(parentEnc, dec, se) + if err != nil { + return sendErr(ctx, errC, errorf(ctx, "extracting element failed: %v", err)) + } + + if !sendVR(ctx, vrC, &res.ValuesResource{pi, fqn, b.Bytes()}) { + return false + } + + if fqn.Type == res.Styleable { + // with a declare-styleable tag, parse its childen and treat them as direct children of resources + dsDec := xml.NewDecoder(b) + dsDec.Token() // we've already processed the first token (the declare-styleable start element) + if !parseRes(ctx, parentEnc, pi, dsDec, vrC, errC) { + return false + } + } + case xml.EndElement: + return true + } + } +} + +func extractFQN(se xml.StartElement) (res.FullyQualifiedName, error) { + if matches(se.Name, res.ItemTagName) { + nameAttr, resType, err := extractNameAndType(se) + if err != nil { + return res.FullyQualifiedName{}, err + } + return res.ParseName(nameAttr, resType) + } + + nameAttr, err := extractName(se) + if err != nil { + return res.FullyQualifiedName{}, err + } + if resType, ok := res.ResourcesTagToType[se.Name.Local]; ok { + return res.ParseName(nameAttr, resType) + } + return res.FullyQualifiedName{}, fmt.Errorf("%s: is an unhandled tag", se.Name.Local) + +} + +func extractName(se xml.StartElement) (nameAttr string, err error) { + for _, a := range se.Attr { + if matches(res.NameAttrName, a.Name) { + nameAttr = a.Value + break + } + } + if nameAttr == "" { + err = fmt.Errorf("%s: tag is missing %q attribute or is empty", se.Name.Local, res.NameAttrName.Local) + } + return +} + +func extractNameAndType(se xml.StartElement) (nameAttr string, resType res.Type, err error) { + var typeAttr string + for _, a := range se.Attr { + if matches(res.NameAttrName, a.Name) { + nameAttr = a.Value + } + if matches(res.TypeAttrName, a.Name) { + typeAttr = a.Value + } + } + if nameAttr == "" { + err = fmt.Errorf("%s: tag is missing %q attribute or is empty", se.Name.Local, res.NameAttrName.Local) + return + } + if typeAttr == "" { + err = fmt.Errorf("%s: tag is missing %q attribute or is empty", se.Name.Local, res.TypeAttrName.Local) + return + } + resType, err = res.ParseType(typeAttr) + return +} + +func matches(n1, n2 xml.Name) bool { + // Ignores xml.Name Space attributes unless both names specify Space. + if n1.Space == "" || n2.Space == "" { + return n1.Local == n2.Local + } + return n1.Local == n2.Local && n1.Space == n2.Space +} + +func extractElement(parentEnc *xml2.Encoder, dec *xml.Decoder, se xml.Token) (*bytes.Buffer, error) { + // copy tag contents to a buffer + b := &bytes.Buffer{} + enc := xml2.ChildEncoder(b, parentEnc) + if err := enc.EncodeToken(se); err != nil { + return nil, fmt.Errorf("encoding start element failed: %v", err) + } + if err := copyTag(enc, dec); err != nil { + return nil, fmt.Errorf("copyTag failed: %s", err) + } + enc.Flush() + return b, nil +} + +func copyTag(enc *xml2.Encoder, dec *xml.Decoder) error { + for { + t, err := dec.Token() + if err != nil { + return fmt.Errorf("extract token failed: %v", err) + } + if err := enc.EncodeToken(t); err != nil { + return fmt.Errorf("encoding token %v failed: %v", t, err) + } + switch t.(type) { + case xml.StartElement: + if err := copyTag(enc, dec); err != nil { + return err + } + case xml.EndElement: + return nil + } + } +} + +func sendVR(ctx context.Context, vrC chan<- *res.ValuesResource, vr *res.ValuesResource) bool { + select { + case vrC <- vr: + case <-ctx.Done(): + return false + } + return true +} + +func hasChildType(dec *xml.Decoder, lookup map[xml.Name]res.Type, want res.Type) (bool, error) { + for { + t, err := dec.Token() + if err != nil { + return false, fmt.Errorf("extract token failed: %v", err) + } + switch t.(type) { + case xml.StartElement: + if rt, ok := lookup[t.(xml.StartElement).Name]; ok { + if rt == want { + return true, nil + } + } + // when tag is not in the lookup or the type is unknown or "wanted", skip it. + dec.Skip() + case xml.EndElement: + return false, nil + } + } +} + +func createPartitions(typedOutputs []string) (map[res.Type][]io.Writer, error) { + partitions := make(map[res.Type][]io.Writer) + for _, tAndOP := range typedOutputs { + tOP := strings.SplitN(tAndOP, ":", 2) + // no shard count override specified + if len(tOP) == 1 { + return nil, fmt.Errorf("got malformed typed output path %q wanted the following format \":\"", tAndOP) + } + t, err := res.ParseType(tOP[0]) + if err != nil { + return nil, fmt.Errorf("got err while trying to parse %s to a res type: %v", tOP[0], err) + } + op := tOP[1] + if err := os.MkdirAll(path.Dir(op), 0744); err != nil { + return nil, fmt.Errorf("%s: mkdir failed: %v", op, err) + } + f, err := os.OpenFile(op, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644) + if err != nil { + return nil, fmt.Errorf("open/create failed: %v", err) + } + partitions[t] = append(partitions[t], f) + } + return partitions, nil +} + +// Run is the entry point for bucketize. +func Run() { + if resPaths == nil || typedOutputs == nil { + log.Fatal("Flags -res_paths and -typed_outputs must be specified.") + } + + resFiles, err := walk.Files(resPaths) + if err != nil { + log.Fatalf("Got error getting the resource paths: %v", err) + } + resFileIdxs := make(map[string]int) + for i, resFile := range resFiles { + resFileIdxs[resFile] = i + } + + p, err := createPartitions(typedOutputs) + if err != nil { + log.Fatalf("Got error creating partitions: %v", err) + } + + ps, err := makePartitionSession(p, shard.FNV, resFileIdxs) + if err != nil { + log.Fatalf("Got error making partition session: %v", err) + } + + m, err := makeArchiver(resFiles, ps) + if err != nil { + log.Fatalf("Got error making archiver: %v", err) + } + + if err := m.Archive(context.Background()); err != nil { + log.Fatalf("Got error archiving: %v", err) + } +} diff --git a/src/tools/ak/bucketize/bucketize_bin.go b/src/tools/ak/bucketize/bucketize_bin.go new file mode 100644 index 0000000..f9ea517 --- /dev/null +++ b/src/tools/ak/bucketize/bucketize_bin.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// The bucketize_bin is a command line tool to bucketize Android resources. +package main + +import ( + "flag" + + _ "src/common/golang/flagfile" + "src/tools/ak/bucketize/bucketize" +) + +func main() { + bucketize.Init() + flag.Parse() + bucketize.Run() +} diff --git a/src/tools/ak/bucketize/bucketize_test.go b/src/tools/ak/bucketize/bucketize_test.go new file mode 100644 index 0000000..3864164 --- /dev/null +++ b/src/tools/ak/bucketize/bucketize_test.go @@ -0,0 +1,483 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bucketize + +import ( + "bytes" + "context" + "encoding/xml" + "fmt" + "io" + "io/ioutil" + "os" + "path" + "reflect" + "strings" + "testing" + + "src/common/golang/shard" + "src/common/golang/walk" + "src/tools/ak/res/res" +) + +func TestNormalizeResPaths(t *testing.T) { + // Create a temporary directory to house the fake workspace. + tmp, err := ioutil.TempDir("", "") + if err != nil { + t.Fatalf("Can't make temp directory: %v", err) + } + defer os.RemoveAll(tmp) + + var resPaths []string + fp1 := path.Join(tmp, "foo") + _, err = os.Create(fp1) + if err != nil { + t.Fatalf("Got error while trying to create %s: %v", fp1, err) + } + resPaths = append(resPaths, fp1) + + dp1 := path.Join(tmp, "bar", "baz", "qux") + if err != os.MkdirAll(dp1, 0777) { + t.Fatalf("Got error while trying to create %s: %v", dp1, err) + } + resPaths = append(resPaths, dp1) + + // Create a file nested in the directory that is passed in as a resPath. This file will get + // injected between fp1 and fp3 because the directory is defined in the middle. Hence, + // files added to the directory will appear between fp1 and fp3. This behavior is intended. + fInDP1 := path.Join(dp1, "quux") + _, err = os.Create(fInDP1) + if err != nil { + t.Fatalf("Got error while trying to create %s: %v", fInDP1, err) + } + + fp3 := path.Join(tmp, "bar", "corge") + _, err = os.Create(fp3) + if err != nil { + t.Fatalf("Got error while trying to create %s: %v", fp3, err) + } + resPaths = append(resPaths, fp3) + + gotFiles, err := walk.Files(resPaths) + if err != nil { + t.Fatalf("Got error getting the resource paths: %v", err) + } + gotFileIdxs := make(map[string]int) + for i, gotFile := range gotFiles { + gotFileIdxs[gotFile] = i + } + + wantFiles := []string{fp1, fInDP1, fp3} + if !reflect.DeepEqual(gotFiles, wantFiles) { + t.Errorf("DeepEqual(\n%#v\n,\n%#v\n): returned false", gotFiles, wantFiles) + } + + wantFileIdxs := map[string]int{fp1: 0, fInDP1: 1, fp3: 2} + if !reflect.DeepEqual(gotFileIdxs, wantFileIdxs) { + t.Errorf("DeepEqual(\n%#v\n,\n%#v\n): returned false", gotFileIdxs, wantFileIdxs) + } +} + +func TestArchiverWithPartitionSession(t *testing.T) { + order := make(map[string]int) + ps, err := makePartitionSession(map[res.Type][]io.Writer{}, shard.FNV, order) + if err != nil { + t.Fatalf("MakePartitionSesion got err: %v", err) + } + if _, err := makeArchiver([]string{}, ps); err != nil { + t.Errorf("MakeArchiver got err: %v", err) + } +} + +func TestArchiveNoValues(t *testing.T) { + ctx, cxlFn := context.WithCancel(context.Background()) + defer cxlFn() + a, err := makeArchiver([]string{}, &mockPartitioner{}) + if err != nil { + t.Fatalf("MakeArchiver got error: %v", err) + } + a.Archive(ctx) +} + +func TestInternalArchive(t *testing.T) { + tcs := []struct { + name string + p Partitioner + pis []*res.PathInfo + vrs []*res.ValuesResource + ras []ResourcesAttribute + errs []error + wantErr bool + }{ + { + name: "MultipleResPathInfosAndValuesResources", + p: &mockPartitioner{}, + pis: []*res.PathInfo{{Path: "foo"}}, + vrs: []*res.ValuesResource{ + {Src: &res.PathInfo{Path: "bar"}}, + {Src: &res.PathInfo{Path: "baz"}}, + }, + errs: []error{}, + }, + { + name: "NoValues", + p: &mockPartitioner{}, + pis: []*res.PathInfo{}, + vrs: []*res.ValuesResource{}, + errs: []error{}, + }, + { + name: "ErrorOccurred", + p: &mockPartitioner{}, + pis: []*res.PathInfo{{Path: "foo"}}, + vrs: []*res.ValuesResource{}, + errs: []error{fmt.Errorf("failure")}, + wantErr: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + piC := make(chan *res.PathInfo) + go func() { + defer close(piC) + for _, pi := range tc.pis { + piC <- pi + } + }() + vrC := make(chan *res.ValuesResource) + go func() { + defer close(vrC) + for _, vr := range tc.vrs { + vrC <- vr + } + }() + raC := make(chan *ResourcesAttribute) + go func() { + defer close(raC) + for _, ra := range tc.ras { + nra := new(ResourcesAttribute) + *nra = ra + raC <- nra + } + }() + errC := make(chan error) + go func() { + defer close(errC) + for _, err := range tc.errs { + errC <- err + } + }() + a, err := makeArchiver([]string{}, tc.p) + if err != nil { + t.Errorf("MakeArchiver got error: %v", err) + return + } + ctx, cxlFn := context.WithCancel(context.Background()) + defer cxlFn() + if err := a.archive(ctx, piC, vrC, raC, errC); err != nil { + if !tc.wantErr { + t.Errorf("archive got unexpected error: %v", err) + } + return + } + }) + } +} + +func TestSyncParseReader(t *testing.T) { + tcs := []struct { + name string + pi *res.PathInfo + content *bytes.Buffer + want map[string]string + wantErr bool + }{ + { + name: "SingleResourcesBlock", + pi: &res.PathInfo{}, + content: bytes.NewBufferString(` + hello world + bar + + `), + want: map[string]string{ + "introduction-string": "hello world", + "foo-string": "bar", + "baz-attr": "", + }, + }, + { + name: "MultipleResourcesBlocks", + pi: &res.PathInfo{}, + content: bytes.NewBufferString(` + hello world + bar + + + + 23 + `), + want: map[string]string{ + "introduction-string": "hello world", + "foo-string": "bar", + }, + }, + { + name: "NamespacedResourcesBlock", + pi: &res.PathInfo{}, + content: bytes.NewBufferString(` + hello world + `), + want: map[string]string{ + "resource_attribute-xmlns:foo": "bar", + "namespaced-string": "hello world", + }, + }, + { + name: "DeclareStyleable", + pi: &res.PathInfo{}, + content: bytes.NewBufferString("baz"), + want: map[string]string{ + "foo-styleable": "baz", + "bar-attr": "baz", + }, + }, + { + name: "NamespacedStyleableBlock", + pi: &res.PathInfo{}, + content: bytes.NewBufferString("baz"), + want: map[string]string{ + "resource_attribute-xmlns:zoo": "zoo", + "foo-styleable": "baz", + "bar-attr": "baz", + }, + }, + { + name: "PluralsStringArrayOutputToStringToo", + pi: &res.PathInfo{}, + content: bytes.NewBufferString(` + barbaz + quxquux + `), + want: map[string]string{ + "foo-array": "barbaz", + "corge-plurals": "quxquux", + }, + }, + { + name: "AttrWithFlagOrEnumChildren", + pi: &res.PathInfo{}, + content: bytes.NewBufferString(` + + + `), + want: map[string]string{ + "foo-attr": "", + "qux-attr": "", + }, + }, + { + name: "Style", + pi: &res.PathInfo{}, + content: bytes.NewBufferString(` + + `), + want: map[string]string{ + "foo-style": "", + }, + }, + { + name: "ArraysGoToStingAndInteger", + pi: &res.PathInfo{}, + content: bytes.NewBufferString(` + bar1 + `), + want: map[string]string{ + "foo-array": "bar1", + }, + }, + { + name: "NoContent", + pi: &res.PathInfo{}, + content: &bytes.Buffer{}, + want: map[string]string{}, + }, + { + name: "EmptyResources", + pi: &res.PathInfo{}, + content: bytes.NewBufferString(""), + want: map[string]string{}, + }, + { + name: "IgnoredContent", + pi: &res.PathInfo{}, + content: bytes.NewBufferString(` + + + ignore random string. + + + barqux + ignore this random string too. + + + + `), + want: map[string]string{ + "foo-attr": "barqux", + }, + }, + { + name: "TagMissingNameAttribute", + pi: &res.PathInfo{}, + content: bytes.NewBufferString(`MissingNameAttr`), + wantErr: true, + }, + { + name: "ItemTagMissingTypeAttribute", + pi: &res.PathInfo{}, + content: bytes.NewBufferString(`bar`), + wantErr: true, + }, + { + name: "ItemTagUnknownTypeAttribute", + pi: &res.PathInfo{}, + content: bytes.NewBufferString(``), + wantErr: true, + }, + { + name: "UnhandledTag", + pi: &res.PathInfo{}, + content: bytes.NewBufferString(``), + wantErr: true, + }, + { + name: "MalFormedXml_OpenResourcesTag", + pi: &res.PathInfo{}, + content: bytes.NewBufferString(``), + wantErr: true, + }, + { + name: "MalFormedXml_Unabalanced", + pi: &res.PathInfo{}, + content: bytes.NewBufferString(``), + wantErr: true, + }, + { + name: "NamespaceUsedWithoutNamespaceDefinition", + pi: &res.PathInfo{}, + content: bytes.NewBufferString(`Oh no!`), + wantErr: true, + }, + { + // Verify parent Encoder is properly shadowing the xml file. + name: "NamespaceUsedOutsideOfDefinition", + pi: &res.PathInfo{}, + content: bytes.NewBufferString(` + + qux + Oh no! + `), + wantErr: true, + }, + } + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + ctx, cxlFn := context.WithCancel(context.Background()) + defer cxlFn() + vrC := make(chan *res.ValuesResource) + raC := make(chan *ResourcesAttribute) + errC := make(chan error) + go func() { + defer close(vrC) + defer close(raC) + defer close(errC) + syncParseReader(ctx, tc.pi, xml.NewDecoder(tc.content), vrC, raC, errC) + }() + got := make(map[string]string) + errMs := make([]string, 0) + for errC != nil || vrC != nil { + select { + case e, ok := <-errC: + if !ok { + errC = nil + } + if e != nil { + errMs = append(errMs, e.Error()) + } + case ra, ok := <-raC: + if !ok { + raC = nil + } + if ra != nil { + a := ra.Attribute + got[fmt.Sprintf("resource_attribute-%s:%s", a.Name.Space, a.Name.Local)] = a.Value + } + case vr, ok := <-vrC: + if !ok { + vrC = nil + } + if vr != nil { + got[fmt.Sprintf("%s-%s", vr.N.Name, vr.N.Type.String())] = string(vr.Payload) + } + } + } + + // error handling + if tc.wantErr { + if len(errMs) == 0 { + t.Errorf("syncParseReader expected an error.") + } + return + } + if len(errMs) > 0 { + t.Errorf("syncParserReader got unexpected error(s): \n%s", strings.Join(errMs, "\n")) + return + } + + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("DeepEqual(\n%#v\n,\n%#v\n): returned false", got, tc.want) + } + }) + } +} + +// mockPartitioner is a Partitioner mock used for testing. +type mockPartitioner struct { + strPI []res.PathInfo + cvVR []res.ValuesResource + ra []*ResourcesAttribute +} + +func (mp *mockPartitioner) Close() error { + return nil +} + +func (mp *mockPartitioner) CollectPathResource(src res.PathInfo) { + mp.strPI = append(mp.strPI, src) +} + +func (mp *mockPartitioner) CollectValues(vr *res.ValuesResource) error { + mp.cvVR = append(mp.cvVR, res.ValuesResource{vr.Src, vr.N, vr.Payload}) + return nil +} + +func (mp *mockPartitioner) CollectResourcesAttribute(ra *ResourcesAttribute) { + mp.ra = append(mp.ra, ra) +} diff --git a/src/tools/ak/bucketize/partitioner.go b/src/tools/ak/bucketize/partitioner.go new file mode 100644 index 0000000..97a328d --- /dev/null +++ b/src/tools/ak/bucketize/partitioner.go @@ -0,0 +1,319 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bucketize + +import ( + "archive/zip" + "bytes" + "encoding/xml" + "fmt" + "io" + "os" + "path" + "path/filepath" + "sort" + "strings" + + "src/common/golang/shard" + "src/common/golang/xml2" + "src/tools/ak/res/res" +) + +// Helper struct to sort paths by index +type indexedPaths struct { + order map[string]int + ps []string +} + +type byPathIndex indexedPaths + +func (b byPathIndex) Len() int { return len(b.ps) } +func (b byPathIndex) Swap(i, j int) { b.ps[i], b.ps[j] = b.ps[j], b.ps[i] } +func (b byPathIndex) Less(i, j int) bool { + iIdx := pathIdx(b.ps[i], b.order) + jIdx := pathIdx(b.ps[j], b.order) + // Files exist in the same directory + if iIdx == jIdx { + return b.ps[i] < b.ps[j] + } + return iIdx < jIdx +} + +// Helper struct to sort valuesKeys by index +type indexedValuesKeys struct { + order map[string]int + ks []valuesKey +} + +type byValueKeyIndex indexedValuesKeys + +func (b byValueKeyIndex) Len() int { return len(b.ks) } +func (b byValueKeyIndex) Swap(i, j int) { b.ks[i], b.ks[j] = b.ks[j], b.ks[i] } +func (b byValueKeyIndex) Less(i, j int) bool { + iIdx := pathIdx(b.ks[i].sourcePath.Path, b.order) + jIdx := pathIdx(b.ks[j].sourcePath.Path, b.order) + // Files exist in the same directory + if iIdx == jIdx { + return b.ks[i].sourcePath.Path < b.ks[j].sourcePath.Path + } + return iIdx < jIdx +} + +type valuesKey struct { + sourcePath res.PathInfo + resType res.Type +} + +// PartitionSession consumes resources and partitions them into archives by the resource type. +// The typewise partitions can be further sharded by the provided shardFn +type PartitionSession struct { + typedOutput map[res.Type][]*zip.Writer + sharder shard.Func + collectedVals map[valuesKey]map[string][]byte + collectedPaths map[string]res.PathInfo + collectedRAs map[string][]xml.Attr + resourceOrder map[string]int +} + +// Partitioner takes the provided resource values and paths and stores the data sharded +type Partitioner interface { + Close() error + CollectValues(vr *res.ValuesResource) error + CollectPathResource(src res.PathInfo) + CollectResourcesAttribute(attr *ResourcesAttribute) +} + +// makePartitionSession creates a PartitionSession that writes to the given outputs. +func makePartitionSession(outputs map[res.Type][]io.Writer, sharder shard.Func, resourceOrder map[string]int) (*PartitionSession, error) { + typeToArchs := make(map[res.Type][]*zip.Writer) + for t, ws := range outputs { + archs := make([]*zip.Writer, 0, len(ws)) + for _, w := range ws { + archs = append(archs, zip.NewWriter(w)) + } + typeToArchs[t] = archs + } + return &PartitionSession{ + typeToArchs, + sharder, + make(map[valuesKey]map[string][]byte), + make(map[string]res.PathInfo), + make(map[string][]xml.Attr), + resourceOrder, + }, nil +} + +// Close finalizes all archives in this partition session. +func (ps *PartitionSession) Close() error { + if err := ps.flushCollectedPaths(); err != nil { + return fmt.Errorf("got error flushing collected paths: %v", err) + } + if err := ps.flushCollectedVals(); err != nil { + return fmt.Errorf("got error flushing collected values: %v", err) + } + // close archives. + for _, as := range ps.typedOutput { + for _, a := range as { + if err := a.Close(); err != nil { + return fmt.Errorf("%s: could not close: %v", a, err) + } + } + } + return nil +} + +// CollectPathResource takes a file system resource and tracks it so that it can be stored in an output partition and shard. +func (ps *PartitionSession) CollectPathResource(src res.PathInfo) { + // store the path only if the type is accepted by the underlying partitions. + if ps.isTypeAccepted(src.Type) { + ps.collectedPaths[src.Path] = src + } +} + +// CollectValues stores the xml representation of a particular resource from a particular file. +func (ps *PartitionSession) CollectValues(vr *res.ValuesResource) error { + // store the value only if the type is accepted by the underlying partitions. + if ps.isTypeAccepted(vr.N.Type) { + // Don't store style attr's from other packages + if !(vr.N.Type == res.Attr && vr.N.Package != "res-auto") { + k := valuesKey{*vr.Src, vr.N.Type} + if tv, ok := ps.collectedVals[k]; !ok { + ps.collectedVals[k] = make(map[string][]byte) + ps.collectedVals[k][vr.N.String()] = vr.Payload + } else { + if p, ok := tv[vr.N.String()]; !ok { + ps.collectedVals[k][vr.N.String()] = vr.Payload + } else if len(p) < len(vr.Payload) { + ps.collectedVals[k][vr.N.String()] = vr.Payload + } else if len(p) == len(vr.Payload) && bytes.Compare(p, vr.Payload) != 0 { + return fmt.Errorf("different values for resource %q", vr.N.String()) + } + } + } + } + return nil +} + +// CollectResourcesAttribute stores the xml attributes of the resources tag from a particular file. +func (ps *PartitionSession) CollectResourcesAttribute(ra *ResourcesAttribute) { + ps.collectedRAs[ra.ResFile.Path] = append(ps.collectedRAs[ra.ResFile.Path], ra.Attribute) +} + +func (ps *PartitionSession) isTypeAccepted(t res.Type) bool { + _, ok := ps.typedOutput[t] + return ok +} + +func (ps *PartitionSession) flushCollectedPaths() error { + // sort keys so that data is written to the archives in a deterministic order + // specifically the same order in which they were declared + ks := make([]string, 0, len(ps.collectedPaths)) + for k := range ps.collectedPaths { + ks = append(ks, k) + } + sort.Sort(byPathIndex(indexedPaths{order: ps.resourceOrder, ps: ks})) + for _, k := range ks { + v := ps.collectedPaths[k] + f, err := os.Open(v.Path) + if err != nil { + return fmt.Errorf("%s: could not be opened for reading: %v", v.Path, err) + } + if err := ps.storePathResource(v, f); err != nil { + return fmt.Errorf("%s: got error storing path resource: %v", v.Path, err) + } + f.Close() + } + return nil +} + +func (ps *PartitionSession) storePathResource(src res.PathInfo, r io.Reader) error { + p := path.Base(src.Path) + if dot := strings.Index(p, "."); dot == 0 { + // skip files where the name starts with a ".", these are already ignored by aapt + return nil + } else if dot > 0 { + p = p[:dot] + } + fqn, err := res.ParseName(p, src.Type) + if err != nil { + return fmt.Errorf("%s: %q could not be parsed into a res name: %v", src.Path, p, err) + } + arch, err := ps.archiveFor(fqn) + if err != nil { + return fmt.Errorf("%s: could not get partitioned archive: %v", src.Path, err) + } + w, err := arch.Create(pathResSuffix(src.Path)) + if err != nil { + return fmt.Errorf("%s: could not create writer: %v", src.Path, err) + } + if _, err = io.Copy(w, r); err != nil { + return fmt.Errorf("%s: could not copy into archive: %v", src.Path, err) + } + return nil +} + +func (ps *PartitionSession) archiveFor(fqn res.FullyQualifiedName) (*zip.Writer, error) { + archs, ok := ps.typedOutput[fqn.Type] + if !ok { + return nil, fmt.Errorf("%s: do not have output stream for this res type", fqn.Type) + } + shard := ps.sharder(fqn.String(), len(archs)) + if shard > len(archs) || 0 > shard { + return nil, fmt.Errorf("%v: bad sharder f(%v, %d) -> %d must be [0,%d)", ps.sharder, fqn, len(archs), shard, len(archs)) + } + return archs[shard], nil +} + +var ( + resXMLHeader = []byte("") + resXMLFooter = []byte("") +) + +func (ps *PartitionSession) flushCollectedVals() error { + // sort keys so that data is written to the archives in a deterministic order + // specifically the same order in which blaze provides them + ks := make([]valuesKey, 0, len(ps.collectedVals)) + for k := range ps.collectedVals { + ks = append(ks, k) + } + sort.Sort(byValueKeyIndex(indexedValuesKeys{order: ps.resourceOrder, ks: ks})) + for _, k := range ks { + as, ok := ps.typedOutput[k.resType] + if !ok { + return fmt.Errorf("%s: no output for res type", k.resType) + } + ws := make([]io.Writer, 0, len(as)) + // For each given source file, create a corresponding file in each of the shards. A file in a particular shard may be empty, if none of the resources defined in the source file ended up in that shard. + for _, a := range as { + w, err := a.Create(pathResSuffix(k.sourcePath.Path)) + if err != nil { + return fmt.Errorf("%s: could not create entry: %v", k.sourcePath.Path, err) + } + if _, err = w.Write(resXMLHeader); err != nil { + return fmt.Errorf("%s: could not write xml header: %v", k.sourcePath.Path, err) + } + // Write the resources open tag, with the attributes collected. + b := bytes.Buffer{} + xml2.NewEncoder(&b).EncodeToken(xml.StartElement{ + Name: res.ResourcesTagName, + Attr: ps.collectedRAs[k.sourcePath.Path], + }) + if _, err = w.Write(b.Bytes()); err != nil { + return fmt.Errorf("%s: could not write resources tag %q: %v", k.sourcePath.Path, b.String(), err) + } + ws = append(ws, w) + } + v := ps.collectedVals[k] + var keys []string + for k := range v { + keys = append(keys, k) + } + sort.Strings(keys) + for _, fqn := range keys { + p := v[fqn] + shard := ps.sharder(fqn, len(ws)) + if shard < 0 || shard >= len(ws) { + return fmt.Errorf("%v: bad sharder f(%s, %d) -> %d must be [0,%d)", ps.sharder, fqn, len(ws), shard, len(ws)) + } + if _, err := ws[shard].Write(p); err != nil { + return fmt.Errorf("%s: writing resource %s failed: %v", k.sourcePath.Path, fqn, err) + } + } + for _, w := range ws { + if _, err := w.Write(resXMLFooter); err != nil { + return fmt.Errorf("%s: could not write xml footer: %v", k.sourcePath.Path, err) + } + } + } + return nil +} + +func pathIdx(path string, order map[string]int) int { + if idx, ok := order[path]; ok == true { + return idx + } + // TODO(mauriciogg): maybe replace with prefix search + // list of resources might contain directories so exact match might not exist + dirPos := strings.LastIndex(path, "/res/") + idx, _ := order[path[0:dirPos+4]] + return idx +} + +func pathResSuffix(path string) string { + // returns the relative resource path from the full path + // e.g. /foo/bar/res/values/strings.xml -> res/values/strings.xml + parentDir := filepath.Dir(filepath.Dir(filepath.Dir(path))) + return strings.TrimPrefix(path, parentDir+string(filepath.Separator)) +} diff --git a/src/tools/ak/bucketize/partitioner_test.go b/src/tools/ak/bucketize/partitioner_test.go new file mode 100644 index 0000000..846718e --- /dev/null +++ b/src/tools/ak/bucketize/partitioner_test.go @@ -0,0 +1,349 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bucketize + +import ( + "archive/zip" + "bytes" + "encoding/xml" + "fmt" + "io" + "io/ioutil" + "reflect" + "sort" + "strconv" + "strings" + "testing" + + "src/common/golang/shard" + "src/tools/ak/res/res" +) + +func TestInternalStorePathResource(t *testing.T) { + // test internal storePathResource and skip the creation of real files. + tcs := []struct { + name string + inFiles map[string]string + partitions map[res.Type][]io.Writer + shardFn shard.Func + want map[res.Type][][]string + wantErr bool + }{ + { + name: "MultipleResTypeFilesWithShardsOfDifferentSizes", + inFiles: map[string]string{ + "res/drawable/2-foo.xml": "all", + "res/layout/0-bar.xml": "your", + "res/color/0-baz.xml": "base", + "res/layout/1-qux.xml": "are", + "res/drawable/0-quux.xml": "belong", + "res/color/0-corge.xml": "to", + "res/color/0-grault.xml": "us", + "res/layout/0-garply.xml": "!", + }, + shardFn: shard.Func(func(fqn string, shardCount int) int { + // sharding strategy is built into the file name as "-foo.bar" (i.e. 8-baz.xml) + name := strings.Split(fqn, "/")[1] + ai := strings.SplitN(name, "-", 2)[0] + shard, err := strconv.Atoi(ai) + if err != nil { + t.Fatalf("Atoi(%s) got err: %v", ai, err) + } + return shard + }), + partitions: map[res.Type][]io.Writer{ + res.Drawable: {&bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{}}, + res.Color: {&bytes.Buffer{}}, + res.Layout: {&bytes.Buffer{}, &bytes.Buffer{}}, + }, + want: map[res.Type][][]string{ + res.Drawable: {{"res/drawable/0-quux.xml"}, {}, {"res/drawable/2-foo.xml"}}, + res.Color: {{"res/color/0-baz.xml", "res/color/0-corge.xml", "res/color/0-grault.xml"}}, + res.Layout: {{"res/layout/0-bar.xml", "res/layout/0-garply.xml"}, {"res/layout/1-qux.xml"}}, + }, + }, + { + name: "IgnoredFilePatterns", + inFiles: map[string]string{ + "res/drawable/.ignore": "me", + }, + shardFn: shard.FNV, + partitions: map[res.Type][]io.Writer{res.Drawable: {&bytes.Buffer{}}}, + wantErr: true, + }, + { + name: "NoFiles", + inFiles: map[string]string{}, + shardFn: shard.FNV, + partitions: map[res.Type][]io.Writer{res.Drawable: {&bytes.Buffer{}}}, + want: map[res.Type][][]string{res.Drawable: {{}}}, + }, + } + + order := make(map[string]int) + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + ps, err := makePartitionSession(tc.partitions, tc.shardFn, order) + if err != nil { + t.Errorf("MakePartitionSession(%v, %v, %d) got err: %v", tc.partitions, tc.shardFn, 0, err) + return + } + + for k, v := range tc.inFiles { + pi, err := res.ParsePath(k) + if err != nil { + if !tc.wantErr { + t.Fatalf("ParsePath(%s) got err: %v", k, err) + } + return + } + if err := ps.storePathResource(pi, strings.NewReader(v)); err != nil { + t.Fatalf("storePathResource got unexpected err: %v", err) + } + } + + if err := ps.Close(); err != nil { + t.Errorf("partition Close() got err: %v", err) + return + } + + // validate data outputted to the partitions + got := make(map[res.Type][][]string) + for rt, shards := range tc.partitions { + shardPaths := make([][]string, 0, len(shards)) + for _, shard := range shards { + br := bytes.NewReader(shard.(*bytes.Buffer).Bytes()) + rr, err := zip.NewReader(br, br.Size()) + if err != nil { + t.Errorf("NewReader(%v, %d) got err: %v", br, br.Size(), err) + return + } + paths := make([]string, 0, len(rr.File)) + for _, f := range rr.File { + paths = append(paths, f.Name) + c, err := readAll(f) + if err != nil { + t.Errorf("readAll got err: %v", err) + return + } + if tc.inFiles[f.Name] != c { + t.Errorf("error copying data for %s got %q but wanted %q", f.Name, c, tc.inFiles[f.Name]) + return + } + } + sort.Strings(paths) + shardPaths = append(shardPaths, paths) + } + got[rt] = shardPaths + } + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("DeepEqual(\n%#v\n,\n%#v\n): returned false", got, tc.want) + } + }) + } +} + +func TestCollectValues(t *testing.T) { + tcs := []struct { + name string + pathVPsMap map[string]map[res.FullyQualifiedName][]byte + pathRAMap map[string][]xml.Attr + partitions map[res.Type][]io.Writer + want map[res.Type][][]string + wantErr bool + }{ + { + name: "MultipleResTypesShardsResources", + partitions: map[res.Type][]io.Writer{ + res.Attr: {&bytes.Buffer{}, &bytes.Buffer{}}, + res.String: {&bytes.Buffer{}, &bytes.Buffer{}}, + res.Color: {&bytes.Buffer{}, &bytes.Buffer{}}, + }, + pathVPsMap: map[string]map[res.FullyQualifiedName][]byte{ + "res/values/strings.xml": { + res.FullyQualifiedName{Package: "res-auto", Type: res.String, Name: "foo"}: []byte("bar"), + res.FullyQualifiedName{Package: "android", Type: res.String, Name: "baz"}: []byte("qux"), + res.FullyQualifiedName{Package: "res-auto", Type: res.Attr, Name: "quux"}: []byte("corge"), + }, + "res/values/attr.xml": { + res.FullyQualifiedName{Package: "android", Type: res.Attr, Name: "foo"}: []byte("bar"), + }, + "baz/res/values/attr.xml": { + res.FullyQualifiedName{Package: "android", Type: res.Attr, Name: "bazfoo"}: []byte("qix"), + }, + "baz/res/values/strings.xml": { + res.FullyQualifiedName{Package: "android", Type: res.String, Name: "baz"}: []byte("qux"), + }, + "foo/res/values/attr.xml": { + res.FullyQualifiedName{Package: "android", Type: res.Attr, Name: "foofoo"}: []byte("qex"), + }, + "foo/res/values/color.xml": { + res.FullyQualifiedName{Package: "android", Type: res.Color, Name: "foobar"}: []byte("#FFFFFFFF"), + }, + "dir/res/values/strings.xml": { + res.FullyQualifiedName{Package: "android", Type: res.String, Name: "dirbaz"}: []byte("qux"), + }, + "dir/res/values/color.xml": { + res.FullyQualifiedName{Package: "android", Type: res.Color, Name: "dirfoobar"}: []byte("#FFFFFFFF"), + }, + }, + pathRAMap: map[string][]xml.Attr{ + "res/values/strings.xml": { + xml.Attr{Name: xml.Name{Space: "xmlns", Local: "ns1"}, Value: "path1"}, + xml.Attr{Name: xml.Name{Space: "xmlns", Local: "ns2"}, Value: "path2"}, + }, + }, + want: map[res.Type][][]string{ + res.Attr: { + { + "res/values/strings.xml", "corge", + }, + { + "res/values/strings.xml", "", + }, + }, + res.String: { + { + "res/values/strings.xml", "quxbar", + "res/values/strings.xml", "qux", + "res/values/strings.xml", "qux", + }, + { + "res/values/strings.xml", "", + "res/values/strings.xml", "", + "res/values/strings.xml", "", + }, + }, + res.Color: { + { + "res/values/color.xml", "#FFFFFFFF", + "res/values/color.xml", "#FFFFFFFF", + }, + { + "res/values/color.xml", "", + "res/values/color.xml", "", + }, + }, + }, + }, + { + name: "NoValuesPayloads", + pathVPsMap: map[string]map[res.FullyQualifiedName][]byte{ + "res/values/strings.xml": {}, + }, + partitions: map[res.Type][]io.Writer{res.String: {&bytes.Buffer{}}}, + want: map[res.Type][][]string{res.String: {{}}}, + }, + { + name: "ResTypeValuesResTypeMismatch", + pathVPsMap: map[string]map[res.FullyQualifiedName][]byte{ + "res/values/strings.xml": { + res.FullyQualifiedName{ + Package: "res-auto", + Type: res.String, + Name: "foo", + }: []byte("bar"), + }, + }, + partitions: map[res.Type][]io.Writer{res.Attr: {&bytes.Buffer{}}}, + want: map[res.Type][][]string{res.Attr: {{}}}, + }, + } + + shardFn := func(name string, shardCount int) int { return 0 } + order := map[string]int{ + "foo/res/values/attr.xml": 0, + "foo/res/values/color.xml": 1, + "res/values/attr.xml": 2, + "res/values/strings.xml": 3, + "dir/res": 4, + "baz/res/values/attr.xml": 5, + "baz/res/values/strings.xml": 6, + } + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + ps, err := makePartitionSession(tc.partitions, shardFn, order) + if err != nil { + t.Errorf("makePartitionSession(%v, %v, %d) got err: %v", tc.partitions, shard.FNV, 0, err) + return + } + for p, vps := range tc.pathVPsMap { + pi, err := res.ParsePath(p) + if err != nil { + t.Errorf("ParsePath(%s) got err: %v", p, err) + return + } + for fqn, p := range vps { + ps.CollectValues(&res.ValuesResource{Src: &pi, N: fqn, Payload: p}) + } + } + for p, as := range tc.pathRAMap { + pi, err := res.ParsePath(p) + if err != nil { + t.Errorf("ParsePath(%s) got err: %v", p, err) + return + } + for _, a := range as { + ps.CollectResourcesAttribute(&ResourcesAttribute{ResFile: &pi, Attribute: a}) + } + } + if err := ps.Close(); err != nil { + t.Errorf("partition Close() got err: %v", err) + return + } + + // validate data outputted to the partitions. + got := make(map[res.Type][][]string) + for rt, shards := range tc.partitions { + shardPaths := make([][]string, 0, len(shards)) + for _, shard := range shards { + br := bytes.NewReader(shard.(*bytes.Buffer).Bytes()) + rr, err := zip.NewReader(br, br.Size()) + if err != nil { + t.Errorf("NewReader(%v, %d) got err: %v", br, br.Size(), err) + return + } + paths := make([]string, 0, len(rr.File)) + for _, f := range rr.File { + c, err := readAll(f) + if err != nil { + t.Errorf("readAll got err: %v", err) + return + } + paths = append(paths, f.Name, c) + } + shardPaths = append(shardPaths, paths) + } + got[rt] = shardPaths + } + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("DeepEqual(\n%#v\n,\n%#v\n): returned false", got, tc.want) + } + }) + } +} + +func readAll(f *zip.File) (string, error) { + rc, err := f.Open() + if err != nil { + return "", fmt.Errorf("%q: Open got err: %v", f.Name, err) + } + defer rc.Close() + body, err := ioutil.ReadAll(rc) + if err != nil { + return "", fmt.Errorf("%q: ReadAll got err: %v", f.Name, err) + } + return string(body), nil +} diff --git a/src/tools/ak/bucketize/pipe.go b/src/tools/ak/bucketize/pipe.go new file mode 100644 index 0000000..7162232 --- /dev/null +++ b/src/tools/ak/bucketize/pipe.go @@ -0,0 +1,154 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bucketize + +import ( + "context" + "fmt" + "strings" + "sync" + + "src/tools/ak/res/res" +) + +type contextKey int + +const ( + ctxErr contextKey = 0 +) + +// errorf returns a formatted error with any context sensitive information prefixed to the error +func errorf(ctx context.Context, fmts string, a ...interface{}) error { + if s, ok := ctx.Value(ctxErr).(string); ok { + return fmt.Errorf(strings.Join([]string{s, fmts}, ""), a...) + } + return fmt.Errorf(fmts, a...) +} + +// prefixErr returns a context which adds a prefix to error messages. +func prefixErr(ctx context.Context, add string) context.Context { + if s, ok := ctx.Value(ctxErr).(string); ok { + return context.WithValue(ctx, ctxErr, strings.Join([]string{s, add}, "")) + } + return context.WithValue(ctx, ctxErr, add) +} + +func separatePathInfosByValues(ctx context.Context, pis []*res.PathInfo) (<-chan *res.PathInfo, <-chan *res.PathInfo) { + valuesPIC := make(chan *res.PathInfo) + nonValuesPIC := make(chan *res.PathInfo) + go func() { + defer close(valuesPIC) + defer close(nonValuesPIC) + for _, pi := range pis { + if pi.Type.Kind() == res.Value || pi.Type.Kind() == res.Both && strings.HasPrefix(pi.TypeDir, "values") { + select { + case valuesPIC <- pi: + case <-ctx.Done(): + return + } + } else { + select { + case nonValuesPIC <- pi: + case <-ctx.Done(): + return + } + } + } + }() + return valuesPIC, nonValuesPIC +} + +func mergeValuesResourceStreams(ctx context.Context, vrCs []<-chan *res.ValuesResource) <-chan *res.ValuesResource { + vrC := make(chan *res.ValuesResource) + var wg sync.WaitGroup + wg.Add(len(vrCs)) + output := func(c <-chan *res.ValuesResource) { + defer wg.Done() + for vr := range c { + select { + case vrC <- vr: + case <-ctx.Done(): + return + } + } + } + for _, c := range vrCs { + go output(c) + } + go func() { + wg.Wait() + close(vrC) + }() + return vrC +} + +func mergeResourcesAttributeStreams(ctx context.Context, raCs []<-chan *ResourcesAttribute) <-chan *ResourcesAttribute { + raC := make(chan *ResourcesAttribute) + var wg sync.WaitGroup + wg.Add(len(raCs)) + output := func(c <-chan *ResourcesAttribute) { + defer wg.Done() + for ra := range c { + select { + case raC <- ra: + case <-ctx.Done(): + return + } + } + } + for _, c := range raCs { + go output(c) + } + go func() { + wg.Wait() + close(raC) + }() + return raC +} + +// mergeErrStreams fans in multiple error streams into a single stream. +func mergeErrStreams(ctx context.Context, errCs []<-chan error) <-chan error { + errC := make(chan error) + var wg sync.WaitGroup + wg.Add(len(errCs)) + output := func(c <-chan error) { + defer wg.Done() + for e := range c { + select { + case errC <- e: + case <-ctx.Done(): + return + } + } + } + for _, rc := range errCs { + go output(rc) + } + go func() { + wg.Wait() + close(errC) + }() + return errC +} + +// sendErr attempts to send the provided error to the provided chan, however is the context is canceled, it will return false. +func sendErr(ctx context.Context, errC chan<- error, err error) bool { + select { + case <-ctx.Done(): + return false + case errC <- err: + return true + } +} diff --git a/src/tools/ak/bucketize/pipe_test.go b/src/tools/ak/bucketize/pipe_test.go new file mode 100644 index 0000000..81456ce --- /dev/null +++ b/src/tools/ak/bucketize/pipe_test.go @@ -0,0 +1,75 @@ +// Copyright 2018 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bucketize + +import ( + "context" + "errors" + "reflect" + "testing" +) + +func TestPrefixErr(t *testing.T) { + tests := []struct { + ctx context.Context + fmts string + args []interface{} + want error + }{ + { + ctx: context.Background(), + fmts: "Hello world", + want: errors.New("Hello world"), + }, + { + ctx: prefixErr(context.Background(), "file: foo: "), + fmts: "Hello world: %d", + args: []interface{}{1}, + want: errors.New("file: foo: Hello world: 1"), + }, + { + ctx: prefixErr(prefixErr(context.Background(), "file: foo: "), "tag: : "), + fmts: "Hello world: %d", + args: []interface{}{1}, + want: errors.New("file: foo: tag: : Hello world: 1"), + }, + } + for _, tc := range tests { + got := errorf(tc.ctx, tc.fmts, tc.args...) + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("Errorf(%v, %v, %v): %v wanted %v", tc.ctx, tc.fmts, tc.args, got, tc.want) + } + } +} + +func TestMergeErrStreams(t *testing.T) { + ctx := context.Background() + sendClose := func(e error, eC chan<- error) { + defer close(eC) + eC <- e + } + in1 := make(chan error) + in2 := make(chan error) + go sendClose(errors.New("hi"), in1) + go sendClose(errors.New("hello"), in2) + merged := mergeErrStreams(ctx, []<-chan error{in1, in2}) + var rcv []error + for r := range merged { + rcv = append(rcv, r) + } + if len(rcv) != 2 { + t.Errorf("got: %v on merged stream, wanted only 2 elements", rcv) + } +} diff --git a/src/tools/ak/extractaar/BUILD b/src/tools/ak/extractaar/BUILD new file mode 100644 index 0000000..a4da9ce --- /dev/null +++ b/src/tools/ak/extractaar/BUILD @@ -0,0 +1,44 @@ +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +# Description: +# Package for extractaar module +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +go_library( + name = "extractaar", + srcs = [ + "buildozer.go", + "extractaar.go", + "validator.go", + ], + importpath = "src/tools/ak/extractaar/extractaar", + deps = [ + "//src/tools/ak:types", + ], +) + +go_binary( + name = "extractaar_bin", + srcs = ["extractaar_bin.go"], + deps = [ + ":extractaar", + "//src/common/golang:flagfile", + ], +) + +go_test( + name = "extractaar_test", + size = "small", + srcs = [ + "extractaar_test.go", + "validator_test.go", + ], + embed = [":extractaar"], + deps = [ + "@com_github_google_go_cmp//cmp:go_default_library", + "@com_github_google_go_cmp//cmp/cmpopts:go_default_library", + ], +) diff --git a/src/tools/ak/extractaar/buildozer.go b/src/tools/ak/extractaar/buildozer.go new file mode 100644 index 0000000..f7f52a5 --- /dev/null +++ b/src/tools/ak/extractaar/buildozer.go @@ -0,0 +1,48 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package extractaar + +import ( + "fmt" + "strings" +) + +// BuildozerError represent a rule configuration error fixable with a buildozer command. +type BuildozerError struct { + Msg string + RuleAttr string + NewValue string +} + +func mergeBuildozerErrors(label string, errs []*BuildozerError) string { + var msg strings.Builder + msg.WriteString(fmt.Sprintf("error(s) found while processing aar '%s':\n", label)) + var buildozerCommand strings.Builder + buildozerCommand.WriteString("Use the following command to fix the target:\nbuildozer ") + useBuildozer := false + for _, err := range errs { + msg.WriteString(fmt.Sprintf("\t- %s\n", err.Msg)) + if err.NewValue != "" { + useBuildozer = true + buildozerCommand.WriteString(fmt.Sprintf("'set %s %s' ", err.RuleAttr, err.NewValue)) + } + } + buildozerCommand.WriteString(label) + + if useBuildozer { + msg.WriteString(buildozerCommand.String()) + } + return msg.String() +} diff --git a/src/tools/ak/extractaar/extractaar.go b/src/tools/ak/extractaar/extractaar.go new file mode 100644 index 0000000..2a431fa --- /dev/null +++ b/src/tools/ak/extractaar/extractaar.go @@ -0,0 +1,286 @@ +// Copyright 2021 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package extractaar extracts files from an aar. +package extractaar + +import ( + "archive/zip" + "errors" + "flag" + "fmt" + "io" + "log" + "os" + "path/filepath" + "strings" + "sync" + + "src/tools/ak/types" +) + +// A tristate may be true, false, or unset +type tristate int + +func (t tristate) isSet() bool { + return t == tsTrue || t == tsFalse +} + +func (t tristate) value() bool { + return t == tsTrue +} + +const ( + tsTrue = 1 + tsFalse = -1 + + manifest = iota + res + assets +) + +var ( + // Cmd defines the command to run the extractor. + Cmd = types.Command{ + Init: Init, + Run: Run, + Desc: desc, + Flags: []string{ + "aar", "label", + "out_manifest", "out_res_dir", "out_assets_dir", + "has_res", "has_assets", + }, + } + + aar string + label string + outputManifest string + outputResDir string + outputAssetsDir string + hasRes int + hasAssets int + + initOnce sync.Once +) + +// Init initializes the extractor. +func Init() { + initOnce.Do(func() { + flag.StringVar(&aar, "aar", "", "Path to the aar") + flag.StringVar(&label, "label", "", "Target's label") + flag.StringVar(&outputManifest, "out_manifest", "", "Output manifest") + flag.StringVar(&outputResDir, "out_res_dir", "", "Output resources directory") + flag.StringVar(&outputAssetsDir, "out_assets_dir", "", "Output assets directory") + flag.IntVar(&hasRes, "has_res", 0, "Whether the aar has resources") + flag.IntVar(&hasAssets, "has_assets", 0, "Whether the aar has assets") + }) +} + +func desc() string { + return "Extracts files from an AAR" +} + +type aarFile struct { + path string + relPath string +} + +func (file *aarFile) String() string { + return fmt.Sprintf("%s:%s", file.path, file.relPath) +} + +type toCopy struct { + src string + dest string +} + +// Run runs the extractor +func Run() { + if err := doWork(aar, label, outputManifest, outputResDir, outputAssetsDir, hasRes, hasAssets); err != nil { + log.Fatal(err) + } +} + +func doWork(aar, label, outputManifest, outputResDir, outputAssetsDir string, hasRes, hasAssets int) error { + tmpDir, err := os.MkdirTemp("", "extractaar_") + if err != nil { + return err + } + defer os.RemoveAll(tmpDir) + + files, err := extractAAR(aar, tmpDir) + if err != nil { + return err + } + + validators := map[int]validator{ + manifest: manifestValidator{dest: outputManifest}, + res: resourceValidator{dest: outputResDir, hasRes: tristate(hasRes), ruleAttr: "has_res"}, + assets: resourceValidator{dest: outputAssetsDir, hasRes: tristate(hasAssets), ruleAttr: "has_assets"}, + } + + var filesToCopy []*toCopy + var validationErrs []*BuildozerError + for fileType, files := range groupAARFiles(files) { + validatedFiles, err := validators[fileType].validate(files) + if err != nil { + validationErrs = append(validationErrs, err) + continue + } + filesToCopy = append(filesToCopy, validatedFiles...) + } + + if len(validationErrs) != 0 { + return errors.New(mergeBuildozerErrors(label, validationErrs)) + } + + for _, file := range filesToCopy { + if err := copyFile(file.src, file.dest); err != nil { + return err + } + } + + // TODO(ostonge): Add has_res/has_assets attr to avoid having to do this + // We need to create at least one file so that Bazel does not complain + // that the output tree artifact was not created. + if err := createIfEmpty(outputResDir, "res/values/empty.xml", ""); err != nil { + return err + } + // aapt will ignore this file and not print an error message, because it + // thinks that it is a swap file + if err := createIfEmpty(outputAssetsDir, "assets/empty_asset_generated_by_bazel~", ""); err != nil { + return err + } + return nil +} + +func groupAARFiles(aarFiles []*aarFile) map[int][]*aarFile { + // Map of file type to channel of aarFile + filesMap := make(map[int][]*aarFile) + for _, fileType := range []int{manifest, res, assets} { + filesMap[fileType] = make([]*aarFile, 0) + } + + for _, file := range aarFiles { + if file.relPath == "AndroidManifest.xml" { + filesMap[manifest] = append(filesMap[manifest], file) + } else if strings.HasPrefix(file.relPath, "res"+string(os.PathSeparator)) { + filesMap[res] = append(filesMap[res], file) + } else if strings.HasPrefix(file.relPath, "assets"+string(os.PathSeparator)) { + filesMap[assets] = append(filesMap[assets], file) + } + // TODO(ostonge): support jar and aidl files + } + return filesMap +} + +func extractAAR(aar string, dest string) ([]*aarFile, error) { + reader, err := zip.OpenReader(aar) + if err != nil { + return nil, err + } + defer reader.Close() + + var files []*aarFile + for _, f := range reader.File { + if f.FileInfo().IsDir() { + continue + } + extractedPath := filepath.Join(dest, f.Name) + if err := extractFile(f, extractedPath); err != nil { + return nil, err + } + files = append(files, &aarFile{path: extractedPath, relPath: f.Name}) + } + return files, nil +} + +func extractFile(file *zip.File, dest string) error { + if err := os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil { + return err + } + outFile, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE, file.Mode()) + if err != nil { + return err + } + defer outFile.Close() + + rc, err := file.Open() + if err != nil { + return err + } + defer rc.Close() + + _, err = io.Copy(outFile, rc) + if err != nil { + return err + } + return nil +} + +func copyFile(name, dest string) error { + in, err := os.Open(name) + if err != nil { + return err + } + defer in.Close() + + if err := os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil { + return err + } + out, err := os.Create(dest) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, in) + if err != nil { + return err + } + return nil +} + +func dirIsEmpty(dir string) (bool, error) { + f, err := os.Open(dir) + if os.IsNotExist(err) { + return true, nil + } + if err != nil { + return false, err + } + defer f.Close() + + _, err = f.Readdirnames(1) + if err == io.EOF { + return true, nil + } + return false, err +} + +// Create the file with the content if the directory is empty or does not exists +func createIfEmpty(dir, filename, content string) error { + isEmpty, err := dirIsEmpty(dir) + if err != nil { + return err + } + if isEmpty { + dest := filepath.Join(dir, filename) + if err := os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil { + return err + } + return os.WriteFile(dest, []byte(content), 0644) + } + return nil +} diff --git a/src/tools/ak/extractaar/extractaar_bin.go b/src/tools/ak/extractaar/extractaar_bin.go new file mode 100644 index 0000000..bc7488b --- /dev/null +++ b/src/tools/ak/extractaar/extractaar_bin.go @@ -0,0 +1,29 @@ +// Copyright 2021 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// extract_aar_bin is a command line tool that extracts files from an aar. +package main + +import ( + "flag" + + _ "src/common/golang/flagfile" + "src/tools/ak/extractaar/extractaar" +) + +func main() { + extractaar.Init() + flag.Parse() + extractaar.Run() +} diff --git a/src/tools/ak/extractaar/extractaar_test.go b/src/tools/ak/extractaar/extractaar_test.go new file mode 100644 index 0000000..e0595c2 --- /dev/null +++ b/src/tools/ak/extractaar/extractaar_test.go @@ -0,0 +1,73 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package extractaar + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestGroupAARFiles(t *testing.T) { + tests := []struct { + name string + files []*aarFile + expectedMap map[int][]*aarFile + }{ + { + name: "empty aar", + files: []*aarFile{}, + expectedMap: map[int][]*aarFile{ + manifest: []*aarFile{}, + res: []*aarFile{}, + assets: []*aarFile{}, + }, + }, + { + name: "simple aar", + files: []*aarFile{ + &aarFile{relPath: "AndroidManifest.xml"}, + &aarFile{relPath: "res/values/strings.xml"}, + &aarFile{relPath: "lint.jar"}, + &aarFile{relPath: "proguard.txt"}, + &aarFile{relPath: "classes.jar"}, + &aarFile{relPath: "assetsdir/values.txt"}, + &aarFile{relPath: "libs/foo.jar"}, + &aarFile{relPath: "resource/some/file.txt"}, + &aarFile{relPath: "assets/some/asset.png"}, + }, + expectedMap: map[int][]*aarFile{ + manifest: []*aarFile{ + &aarFile{relPath: "AndroidManifest.xml"}, + }, + res: []*aarFile{ + &aarFile{relPath: "res/values/strings.xml"}, + }, + assets: []*aarFile{ + &aarFile{relPath: "assets/some/asset.png"}, + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + filesMap := groupAARFiles(tc.files) + if diff := cmp.Diff(tc.expectedMap, filesMap, cmp.AllowUnexported(aarFile{})); diff != "" { + t.Errorf("groupAARFiles(%v) returned diff (-want, +got):\n%v", tc.files, diff) + } + }) + } +} diff --git a/src/tools/ak/extractaar/validator.go b/src/tools/ak/extractaar/validator.go new file mode 100644 index 0000000..2a0d845 --- /dev/null +++ b/src/tools/ak/extractaar/validator.go @@ -0,0 +1,77 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package extractaar + +import ( + "fmt" + "path/filepath" + "strings" +) + +func boolToString(b bool) string { + return strings.Title(fmt.Sprintf("%t", b)) +} + +type validator interface { + validate(files []*aarFile) ([]*toCopy, *BuildozerError) +} + +type manifestValidator struct { + dest string +} + +func (v manifestValidator) validate(files []*aarFile) ([]*toCopy, *BuildozerError) { + var filesToCopy []*toCopy + seen := false + for _, file := range files { + if seen { + return nil, &BuildozerError{Msg: "More than one manifest was found"} + } + seen = true + filesToCopy = append(filesToCopy, &toCopy{src: file.path, dest: v.dest}) + } + if !seen { + return nil, &BuildozerError{Msg: "No manifest was found"} + } + return filesToCopy, nil +} + +type resourceValidator struct { + dest string + ruleAttr string + hasRes tristate +} + +func (v resourceValidator) validate(files []*aarFile) ([]*toCopy, *BuildozerError) { + var filesToCopy []*toCopy + seen := false + for _, file := range files { + seen = true + filesToCopy = append(filesToCopy, + &toCopy{src: file.path, dest: filepath.Join(v.dest, file.relPath)}, + ) + } + if v.hasRes.isSet() { + if seen != v.hasRes.value() { + var not string + if !seen { + not = "not " + } + msg := fmt.Sprintf("%s attribute is %s, but files were %sfound", v.ruleAttr, boolToString(v.hasRes.value()), not) + return nil, &BuildozerError{Msg: msg, RuleAttr: v.ruleAttr, NewValue: boolToString(seen)} + } + } + return filesToCopy, nil +} diff --git a/src/tools/ak/extractaar/validator_test.go b/src/tools/ak/extractaar/validator_test.go new file mode 100644 index 0000000..24b7003 --- /dev/null +++ b/src/tools/ak/extractaar/validator_test.go @@ -0,0 +1,175 @@ +// Copyright 2022 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package extractaar + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" +) + +func TestValidateManifest(t *testing.T) { + tests := []struct { + name string + files []*aarFile + dest string + expectedFiles []*toCopy + }{ + { + name: "one manifest", + files: []*aarFile{ + &aarFile{path: "/tmp/aar/AndroidManifest.xml"}, + }, + dest: "/dest/outputManifest.xml", + expectedFiles: []*toCopy{ + &toCopy{src: "/tmp/aar/AndroidManifest.xml", dest: "/dest/outputManifest.xml"}, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + validator := manifestValidator{dest: tc.dest} + files, err := validator.validate(tc.files) + if err != nil { + t.Fatalf("manifestValidator.validate(%s) unexpected error: %v", tc.files, err) + } + if diff := cmp.Diff(tc.expectedFiles, files, cmp.AllowUnexported(toCopy{})); diff != "" { + t.Errorf("manifestValidator.validate(%s) returned diff (-want, +got):\n%v", tc.files, diff) + } + }) + } +} + +func TestValidateManifestError(t *testing.T) { + tests := []struct { + name string + files []*aarFile + }{ + { + name: "no manifest", + files: []*aarFile{}, + }, + { + name: "multiple manifests", + files: []*aarFile{ + &aarFile{path: "/tmp/aar/AndroidManifest.xml"}, + &aarFile{path: "/tmp/aar/SecondAndroidManifest.xml"}, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + validator := manifestValidator{} + if _, err := validator.validate(tc.files); err == nil { + t.Errorf("manifestValidator.validate(%s) expected error but test succeeded: %v", tc.files, err) + } + }) + } +} + +func TestValidateResources(t *testing.T) { + tests := []struct { + name string + files []*aarFile + dest string + hasRes tristate + expectedFiles []*toCopy + }{ + { + name: "has resources with valid hasRes attribute", + files: []*aarFile{ + &aarFile{path: "/tmp/aar/res/values/strings.xml", relPath: "res/values/strings.xml"}, + &aarFile{path: "/tmp/aar/res/layout/activity.xml", relPath: "res/layout/activity.xml"}, + }, + hasRes: tristate(1), + dest: "/dest/outputres", + expectedFiles: []*toCopy{ + &toCopy{src: "/tmp/aar/res/values/strings.xml", dest: "/dest/outputres/res/values/strings.xml"}, + &toCopy{src: "/tmp/aar/res/layout/activity.xml", dest: "/dest/outputres/res/layout/activity.xml"}, + }, + }, + { + name: "does not have resources with valid hasRes attribute", + files: []*aarFile{}, + hasRes: tristate(0), + dest: "/dest/outputres", + expectedFiles: nil, + }, + { + name: "no resources and checks disabled", + files: []*aarFile{}, + hasRes: tristate(-1), + dest: "/dest/outputres", + expectedFiles: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + validator := resourceValidator{dest: tc.dest, hasRes: tc.hasRes} + files, err := validator.validate(tc.files) + if err != nil { + t.Fatalf("resourceValidator.validate(%s) unexpected error: %v", tc.files, err) + } + if diff := cmp.Diff(tc.expectedFiles, files, cmp.AllowUnexported(toCopy{})); diff != "" { + t.Errorf("resourceValidator.validate(%s) returned diff (-want, +got):\n%v", tc.files, diff) + } + }) + } +} + +func TestValidateResourcesError(t *testing.T) { + tests := []struct { + name string + files []*aarFile + hasRes tristate + ruleAttr string + expectedError *BuildozerError + }{ + { + name: "has resources with invalid hasRes attribute", + files: []*aarFile{ + &aarFile{path: "/tmp/aar/res/values/strings.xml", relPath: "res/values/strings.xml"}, + &aarFile{path: "/tmp/aar/res/layout/activity.xml", relPath: "res/layout/activity.xml"}, + }, + hasRes: tristate(-1), + ruleAttr: "test", + expectedError: &BuildozerError{RuleAttr: "test", NewValue: "True"}, + }, + { + name: "no resources with invalid hasRes attribute", + files: []*aarFile{}, + hasRes: tristate(1), + ruleAttr: "test", + expectedError: &BuildozerError{RuleAttr: "test", NewValue: "False"}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + validator := resourceValidator{ruleAttr: tc.ruleAttr, hasRes: tc.hasRes} + _, err := validator.validate(tc.files) + if err == nil { + t.Fatalf("resourceValidator.validate(%s) expected error but test succeeded: %v", tc.files, err) + } + if diff := cmp.Diff(tc.expectedError, err, cmpopts.IgnoreFields(BuildozerError{}, "Msg")); diff != "" { + t.Errorf("resourceValidator.validate(%s) returned diff (-want, +got):\n%v", tc.files, diff) + } + }) + } +} -- cgit v1.2.3 From ce37817d8589cac4a7cc20cb4d51fe8ad459dea1 Mon Sep 17 00:00:00 2001 From: A Googler Date: Tue, 20 Dec 2022 00:01:53 -0800 Subject: The attribute is used to set additional flags (e.g. -Weverything) to the AOSP AIDL compiler. PiperOrigin-RevId: 496583151 Change-Id: I4f2d533913b3f2febd89ee237c30ec7281d7d8b6 --- rules/android_library/attrs.bzl | 8 ++++++++ rules/android_library/impl.bzl | 9 +++++++++ rules/idl.bzl | 12 ++++++++++-- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/rules/android_library/attrs.bzl b/rules/android_library/attrs.bzl index 468ac9d..2bc3a0d 100644 --- a/rules/android_library/attrs.bzl +++ b/rules/android_library/attrs.bzl @@ -152,6 +152,14 @@ ATTRS = _attrs.add( "strictly required." ), ), + idlopts = attr.string_list( + mandatory = False, + allow_empty = True, + default = [], + doc = ( + "Add these flags to the AIDL compiler command." + ), + ), neverlink = attr.bool( default = False, doc = ( diff --git a/rules/android_library/impl.bzl b/rules/android_library/impl.bzl index e2bfc2a..4f00ec4 100644 --- a/rules/android_library/impl.bzl +++ b/rules/android_library/impl.bzl @@ -56,6 +56,10 @@ _IDL_USES_AOSP_COMPILER_ERROR = ( "Use of `idl_uses_aosp_compiler` is not allowed for %s." ) +_IDL_IDLOPTS_UNSUPPORTERD_ERROR = ( + "`idlopts` is supported only if `idl_uses_aosp_compiler` is set to true." +) + # Android library AAR context attributes. _PROVIDERS = "providers" _VALIDATION_OUTPUTS = "validation_outputs" @@ -110,6 +114,10 @@ def _validate_rule_context(ctx): not acls.in_android_library_use_aosp_aidl_compiler_allowlist(str(ctx.label))): log.error(_IDL_USES_AOSP_COMPILER_ERROR % ctx.label) + # Check if idlopts is with idl_uses_aosp_compiler + if ctx.attr.idlopts and not ctx.attr.idl_uses_aosp_compiler: + log.error(_IDL_IDLOPTS_UNSUPPORTERD_ERROR) + return struct( enable_deps_without_srcs = _check_deps_without_java_srcs(ctx), ) @@ -213,6 +221,7 @@ def _process_idl(ctx, **unused_sub_ctxs): aidl_lib = get_android_sdk(ctx).aidl_lib, aidl_framework = get_android_sdk(ctx).framework_aidl, uses_aosp_compiler = ctx.attr.idl_uses_aosp_compiler, + idlopts = ctx.attr.idlopts, ), ) diff --git a/rules/idl.bzl b/rules/idl.bzl index ee0ed21..42ea015 100644 --- a/rules/idl.bzl +++ b/rules/idl.bzl @@ -48,7 +48,8 @@ def _gen_java_from_idl( aidl = None, aidl_lib = None, aidl_framework = None, - uses_aosp_compiler = False): + uses_aosp_compiler = False, + idlopts = []): args = ctx.actions.args() # Note: at the moment (2022/11/07), the flags that the AOSP compiler accepts is a superset of @@ -56,6 +57,9 @@ def _gen_java_from_idl( if uses_aosp_compiler: args.add("--use-aosp-compiler") + for opt in idlopts: + args.add(opt) + args.add("-b") # fail on parcelable args.add_all(transitive_idl_import_roots, format_each = "-I%s") args.add(aidl_framework, format = "-p%s") @@ -137,7 +141,8 @@ def _process( aidl = None, aidl_lib = None, aidl_framework = None, - uses_aosp_compiler = False): + uses_aosp_compiler = False, + idlopts = []): """Processes Android IDL. Args: @@ -193,6 +198,8 @@ def _process( union, and many more. On the other hand, using this may cause noticeable regression in terms of code size and performance as the compiler doesn't implement several optimization techniques that the Google3 compiler has. + idlopts: list of string. Additional flags to add to the AOSP AIDL compiler + invocation. Returns: A IDLContextInfo provider. @@ -239,6 +246,7 @@ def _process( aidl_lib = aidl_lib, aidl_framework = aidl_framework, uses_aosp_compiler = uses_aosp_compiler, + idlopts = idlopts, ) return IDLContextInfo( -- cgit v1.2.3 From da636283f19cf9c15ad46c61699ab342ee1aacb8 Mon Sep 17 00:00:00 2001 From: A Googler Date: Wed, 21 Dec 2022 02:39:03 -0800 Subject: Implement native libs collection in Starlark for `android_binary` With this change, native deps for `android_binary` are collected (and linked if needed) in Starlark and propagated to the native rule via a new provider `AndroidBinaryNativeLibsInfo`. The code has been transliterated from `bazel/src/main/java/com/google/devtools/build/lib/rules/android/NativeLibs.java` and `bazel/src/main/java/com/google/devtools/build/lib/rules/nativedeps/NativeDepsHelper.java` PiperOrigin-RevId: 496872684 Change-Id: Ib943b8d51d1f88d56d733a1668fefbd6589acb02 --- rules/android_binary_internal/attrs.bzl | 15 ++ rules/android_binary_internal/impl.bzl | 15 ++ rules/android_binary_internal/rule.bzl | 1 + rules/native_deps.bzl | 310 ++++++++++++++++++++++++++++++++ 4 files changed, 341 insertions(+) create mode 100644 rules/native_deps.bzl diff --git a/rules/android_binary_internal/attrs.bzl b/rules/android_binary_internal/attrs.bzl index d0d2197..2cc6a3d 100644 --- a/rules/android_binary_internal/attrs.bzl +++ b/rules/android_binary_internal/attrs.bzl @@ -18,6 +18,10 @@ load( "//rules:attrs.bzl", _attrs = "attrs", ) +load( + "//rules:native_deps.bzl", + "split_config_aspect", +) ATTRS = _attrs.replace( _attrs.add( @@ -59,6 +63,17 @@ ATTRS = _attrs.replace( ), _defined_resource_files = attr.bool(default = False), _enable_manifest_merging = attr.bool(default = True), + _cc_toolchain_split = attr.label( + cfg = android_common.multi_cpu_configuration, + default = "@bazel_tools//tools/cpp:current_cc_toolchain", + aspects = [split_config_aspect], + ), + _grep_includes = attr.label( + allow_single_file = True, + executable = True, + cfg = "exec", + default = Label("@@bazel_tools//tools/cpp:grep-includes"), + ), ), _attrs.COMPILATION, _attrs.DATA_CONTEXT, diff --git a/rules/android_binary_internal/impl.bzl b/rules/android_binary_internal/impl.bzl index 847844a..9d8864f 100644 --- a/rules/android_binary_internal/impl.bzl +++ b/rules/android_binary_internal/impl.bzl @@ -23,6 +23,10 @@ load( ) load("//rules:resources.bzl", _resources = "resources") load("//rules:utils.bzl", "compilation_mode", "get_android_toolchain", "utils") +load( + "//rules:native_deps.bzl", + _process_native_deps = "process", +) def _process_manifest(ctx, **unused_ctxs): manifest_ctx = _resources.bump_min_sdk( @@ -85,6 +89,16 @@ def _validate_manifest(ctx, packaged_resources_ctx, **unused_ctxs): value = manifest_validation_ctx, ) +def _process_native_libs(ctx, **_unusued_ctxs): + native_libs_info = _process_native_deps( + ctx, + filename = "nativedeps", + ) + return ProviderInfo( + name = "native_libs_ctx", + value = struct(providers = [native_libs_info]), + ) + def use_legacy_manifest_merger(ctx): """Whether legacy manifest merging is enabled. @@ -130,6 +144,7 @@ PROCESSORS = dict( ManifestProcessor = _process_manifest, ResourceProcessor = _process_resources, ValidateManifestProcessor = _validate_manifest, + NativeLibsProcessor = _process_native_libs, ) _PROCESSING_PIPELINE = processing_pipeline.make_processing_pipeline( diff --git a/rules/android_binary_internal/rule.bzl b/rules/android_binary_internal/rule.bzl index ec8abf3..b08a39e 100644 --- a/rules/android_binary_internal/rule.bzl +++ b/rules/android_binary_internal/rule.bzl @@ -48,6 +48,7 @@ def make_rule( fragments = [ "android", "java", + "cpp", ], ) diff --git a/rules/native_deps.bzl b/rules/native_deps.bzl new file mode 100644 index 0000000..7702e38 --- /dev/null +++ b/rules/native_deps.bzl @@ -0,0 +1,310 @@ +# Copyright 2022 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Defines the native libs processing and an aspect to collect build configuration +of split deps +""" + +load("//rules:common.bzl", "common") + +SplitConfigInfo = provider( + doc = "Provides information about configuration for a split config dep", + fields = dict( + build_config = "The build configuration of the dep.", + ), +) + +def _split_config_aspect_impl(__, ctx): + return SplitConfigInfo(build_config = ctx.configuration) + +split_config_aspect = aspect( + implementation = _split_config_aspect_impl, +) + +def process(ctx, filename): + """ Links native deps into a shared library + + Args: + ctx: The context. + filename: String. The name of the artifact containing the name of the + linked shared library + + Returns: + Tuple of (libs, libs_name) where libs is a depset of all native deps + and libs_name is a File containing the basename of the linked shared + library + """ + actual_target_name = ctx.label.name.removesuffix(common.PACKAGED_RESOURCES_SUFFIX) + native_libs_basename = None + libs_name = None + libs = dict() + for key, deps in ctx.split_attr.deps.items(): + cc_toolchain_dep = ctx.split_attr._cc_toolchain_split[key] + cc_toolchain = cc_toolchain_dep[cc_common.CcToolchainInfo] + build_config = cc_toolchain_dep[SplitConfigInfo].build_config + linker_input = cc_common.create_linker_input( + owner = ctx.label, + user_link_flags = ["-Wl,-soname=lib" + actual_target_name], + ) + cc_info = cc_common.merge_cc_infos( + cc_infos = _concat( + [CcInfo(linking_context = cc_common.create_linking_context( + linker_inputs = depset([linker_input]), + ))], + [dep[JavaInfo].cc_link_params_info for dep in deps if JavaInfo in dep], + [dep[AndroidCcLinkParamsInfo].link_params for dep in deps if AndroidCcLinkParamsInfo in dep], + [dep[CcInfo] for dep in deps if CcInfo in dep], + ), + ) + libraries = [] + + native_deps_lib = _link_native_deps_if_present(ctx, cc_info, cc_toolchain, build_config, actual_target_name) + if native_deps_lib: + libraries.append(native_deps_lib) + native_libs_basename = native_deps_lib.basename + + libraries.extend(_filter_unique_shared_libs(native_deps_lib, cc_info)) + + if libraries: + libs[key] = depset(libraries) + + if libs and native_libs_basename: + libs_name = ctx.actions.declare_file("nativedeps_filename/" + actual_target_name + "/" + filename) + ctx.actions.write(output = libs_name, content = native_libs_basename) + + return AndroidBinaryNativeLibsInfo(libs, libs_name) + +def _all_inputs(cc_info): + return [ + lib + for input in cc_info.linking_context.linker_inputs.to_list() + for lib in input.libraries + ] + +def _filter_unique_shared_libs(linked_lib, cc_info): + basenames = {} + artifacts = {} + if linked_lib: + basenames[linked_lib.basename] = linked_lib + for input in _all_inputs(cc_info): + if input.pic_static_library or input.static_library: + # This is not a shared library and will not be loaded by Android, so skip it. + continue + + artifact = None + if input.interface_library: + if input.resolved_symlink_interface_library: + artifact = input.resolved_symlink_interface_library + else: + artifact = input.interface_library + elif input.resolved_symlink_dynamic_library: + artifact = input.resolved_symlink_dynamic_library + else: + artifact = input.dynamic_library + + if not artifact: + fail("Should never happen: did not find artifact for link!") + + if artifact in artifacts: + # We have already reached this library, e.g., through a different solib symlink. + continue + artifacts[artifact] = None + basename = artifact.basename + if basename in basenames: + old_artifact = basenames[basename] + fail( + "Each library in the transitive closure must have a " + + "unique basename to avoid name collisions when packaged into " + + "an apk, but two libraries have the basename '" + basename + + "': " + artifact + " and " + old_artifact + ( + " (the library compiled for this target)" if old_artifact == linked_lib else "" + ), + ) + else: + basenames[basename] = artifact + + return artifacts.keys() + +def _contains_code_to_link(input): + if not input.static_library and not input.pic_static_library: + # this is a shared library so we're going to have to copy it + return False + if input.objects: + object_files = input.objects + elif input.pic_objects: + object_files = input.pic_objects + else: + # this is an opaque library so we're going to have to link it + return True + for obj in object_files: + if not _is_shared_library(obj): + # this library was built with a non-shared-library object so we should link it + return True + return False + +def _is_shared_library(lib_artifact): + if (lib_artifact.extension in ["so", "dll", "dylib"]): + return True + + lib_name = lib_artifact.basename + + # validate against the regex "^.+\\.((so)|(dylib))(\\.\\d\\w*)+$", + # must match VERSIONED_SHARED_LIBRARY. + for ext in (".so.", ".dylib."): + name, _, version = lib_name.rpartition(ext) + if name and version: + version_parts = version.split(".") + for part in version_parts: + if not part[0].isdigit(): + return False + for c in part[1:].elems(): + if not (c.isalnum() or c == "_"): + return False + return True + return False + +def _get_build_info(ctx): + return cc_common.get_build_info(ctx) + +def _get_shared_native_deps_path( + linker_inputs, + link_opts, + linkstamps, + build_info_artifacts, + features, + is_test_target_partially_disabled_thin_lto): + fp = [] + for artifact in linker_inputs: + fp.append(artifact.short_path) + fp.append(str(len(link_opts))) + for opt in link_opts: + fp.append(opt) + for artifact in linkstamps: + fp.append(artifact.short_path) + for artifact in build_info_artifacts: + fp.append(artifact.short_path) + for feature in features: + fp.append(feature) + + fp.append("1" if is_test_target_partially_disabled_thin_lto else "0") + + fingerprint = "%x" % hash("".join(fp)) + return "_nativedeps/" + fingerprint + +def _get_static_mode_params_for_dynamic_library_libraries(libs): + linker_inputs = [] + for lib in libs: + if lib.pic_static_library: + linker_inputs.append(lib.pic_static_library) + elif lib.static_library: + linker_inputs.append(lib.static_library) + elif lib.interface_library: + linker_inputs.append(lib.interface_library) + else: + linker_inputs.append(lib.dynamic_library) + return linker_inputs + +def _link_native_deps_if_present(ctx, cc_info, cc_toolchain, build_config, actual_target_name, is_test_rule_class = False): + needs_linking = False + for input in _all_inputs(cc_info): + needs_linking = needs_linking or _contains_code_to_link(input) + + if not needs_linking: + return None + + # This does not need to be shareable, but we use this API to specify the + # custom file root (matching the configuration) + output_lib = ctx.actions.declare_shareable_artifact( + ctx.label.package + "/nativedeps/" + actual_target_name + "/lib" + actual_target_name + ".so", + build_config.bin_dir, + ) + + link_opts = cc_info.linking_context.user_link_flags + + linkstamps = [] + for input in cc_info.linking_context.linker_inputs.to_list(): + linkstamps.extend(input.linkstamps) + linkstamps_dict = {linkstamp: None for linkstamp in linkstamps} + + build_info_artifacts = _get_build_info(ctx) if linkstamps_dict else [] + requested_features = ["static_linking_mode", "native_deps_link"] + requested_features.extend(ctx.features) + if not "legacy_whole_archive" in ctx.disabled_features: + requested_features.append("legacy_whole_archive") + requested_features = sorted(requested_features) + feature_config = cc_common.configure_features( + ctx = ctx, + cc_toolchain = cc_toolchain, + requested_features = requested_features, + unsupported_features = ctx.disabled_features, + ) + partially_disabled_thin_lto = ( + cc_common.is_enabled( + feature_name = "thin_lto_linkstatic_tests_use_shared_nonlto_backends", + feature_configuration = feature_config, + ) and not cc_common.is_enabled( + feature_name = "thin_lto_all_linkstatic_use_shared_nonlto_backends", + feature_configuration = feature_config, + ) + ) + test_only_target = ctx.attr.testonly or is_test_rule_class + share_native_deps = ctx.fragments.cpp.share_native_deps() + + linker_inputs = _get_static_mode_params_for_dynamic_library_libraries(cc_info.linking_context.libraries_to_link) + + if share_native_deps: + shared_path = _get_shared_native_deps_path( + linker_inputs, + link_opts, + [linkstamp.file() for linkstamp in linkstamps_dict], + build_info_artifacts, + requested_features, + test_only_target and partially_disabled_thin_lto, + ) + linked_lib = ctx.actions.declare_shareable_artifact(shared_path + ".so", build_config.bin_dir) + else: + linked_lib = output_lib + + cc_common.link( + name = ctx.label.name, + actions = ctx.actions, + linking_contexts = [cc_info.linking_context], + output_type = "dynamic_library", + never_link = True, + native_deps = True, + feature_configuration = feature_config, + cc_toolchain = cc_toolchain, + test_only_target = test_only_target, + stamp = ctx.attr.stamp, + grep_includes = ctx.file._grep_includes, + main_output = linked_lib, + use_shareable_artifact_factory = True, + build_config = build_config, + ) + + if (share_native_deps): + ctx.actions.symlink( + output = output_lib, + target_file = linked_lib, + ) + return output_lib + else: + return linked_lib + +def _concat(*list_of_lists): + res = [] + for list in list_of_lists: + res.extend(list) + return res -- cgit v1.2.3 From d677e53fcf7ac747d536b358264ba5d2ee1f9e52 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Wed, 21 Dec 2022 09:09:54 -0800 Subject: Add a layout resource to rules_android/src/java/com/example/sampleapp Part of #12 PiperOrigin-RevId: 496936321 Change-Id: I092d0a86ec8534894a4760692ccad6a7eaa12038 --- src/java/com/example/sampleapp/SampleApp.java | 4 +--- src/java/com/example/sampleapp/res/layout/basic_activity.xml | 12 ++++++++++++ src/java/com/example/sampleapp/res/values/strings.xml | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 src/java/com/example/sampleapp/res/layout/basic_activity.xml diff --git a/src/java/com/example/sampleapp/SampleApp.java b/src/java/com/example/sampleapp/SampleApp.java index 9564755..fe5f6fe 100644 --- a/src/java/com/example/sampleapp/SampleApp.java +++ b/src/java/com/example/sampleapp/SampleApp.java @@ -28,9 +28,7 @@ public class SampleApp extends Activity { public void onCreate(Bundle state) { super.onCreate(state); - TextView view = new TextView(this); - view.setText(getString()); - setContentView(view); + setContentView(R.layout.basic_activity); } public native String getString(); diff --git a/src/java/com/example/sampleapp/res/layout/basic_activity.xml b/src/java/com/example/sampleapp/res/layout/basic_activity.xml new file mode 100644 index 0000000..61fb73e --- /dev/null +++ b/src/java/com/example/sampleapp/res/layout/basic_activity.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/src/java/com/example/sampleapp/res/values/strings.xml b/src/java/com/example/sampleapp/res/values/strings.xml index 28193f2..2065009 100644 --- a/src/java/com/example/sampleapp/res/values/strings.xml +++ b/src/java/com/example/sampleapp/res/values/strings.xml @@ -1,4 +1,5 @@ SampleApp + Hello world! -- cgit v1.2.3 From 502e83f8ef47ea117f025490d627b4c4256a5937 Mon Sep 17 00:00:00 2001 From: A Googler Date: Thu, 5 Jan 2023 16:28:43 -0500 Subject: Define common Android platforms. Users can use these by adding `--android_platforms=@rules_android//:armeabi-v7a` to their builds (replacing `@rules_android` with the name they use to import https://github.com/bazelbuild/rules_android). This is work towards enabling platforms and toolchains for Android builds (bazelbuild/bazel#16285). PiperOrigin-RevId: 499992246 Change-Id: If03f275cb8372cd18a7cd21a94f693ebcc3dd569 --- BUILD | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/BUILD b/BUILD index 4415141..c459979 100644 --- a/BUILD +++ b/BUILD @@ -1,4 +1,41 @@ load("@bazel_gazelle//:def.bzl", "gazelle") +package(default_visibility = ["//visibility:public"]) + # gazelle:prefix github.com/bazelbuild/rules_android gazelle(name = "gazelle") + +# Common default platform definitions for use by Android projects. + +platform( + name = "x86", + constraint_values = [ + "@platforms//os:android", + "@platforms//cpu:x86_32", + ], +) + +platform( + name = "x86_64", + constraint_values = [ + "@platforms//os:android", + "@platforms//cpu:x86_64", + ], +) + +platform( + name = "armeabi-v7a", + constraint_values = [ + "@platforms//os:android", + "@platforms//cpu:armv7", + ], +) + +platform( + name = "arm64-v8a", + constraint_values = + [ + "@platforms//cpu:arm64", + "@platforms//os:android", + ], +) -- cgit v1.2.3 From baf4baebb6e309ad8e84cd1cf464de32490519f7 Mon Sep 17 00:00:00 2001 From: Joseph Park Date: Tue, 10 Jan 2023 20:56:09 -0800 Subject: Migrate getProguardSpecs for android_binary from native to Starlark PiperOrigin-RevId: 501170279 Change-Id: I0b5323059498c456ef1ae1c5df5e6d15fb216d5d --- rules/proguard.bzl | 41 +++++++++++++++++++++++++++++++++++++++++ rules/resources.bzl | 3 +++ 2 files changed, 44 insertions(+) diff --git a/rules/proguard.bzl b/rules/proguard.bzl index 837d622..5b1a16c 100644 --- a/rules/proguard.bzl +++ b/rules/proguard.bzl @@ -14,6 +14,8 @@ """Bazel Android Proguard library for the Android rules.""" +load(":utils.bzl", "utils") + _ProguardContextInfo = provider( doc = "Contains data from processing Proguard specs.", fields = dict( @@ -100,11 +102,50 @@ def _process( ], ) +def _collect_transitive_proguard_specs( + specs_to_include, + local_proguard_specs, + proguard_deps): + if len(local_proguard_specs) == 0: + return [] + + proguard_specs = local_proguard_specs + specs_to_include + for dep in proguard_deps: + proguard_specs.extend(dep.specs.to_list()) + + return sorted(proguard_specs) + +def _get_proguard_specs( + ctx, + resource_proguard_config, + proguard_specs_for_manifest = []): + proguard_deps = utils.collect_providers(ProguardSpecProvider, utils.dedupe_split_attr(ctx.split_attr.deps)) + if ctx.configuration.coverage_enabled and hasattr(ctx.attr, "_jacoco_runtime"): + proguard_deps.append(ctx.attr._jacoco_runtime[ProguardSpecProvider]) + + local_proguard_specs = [] + if ctx.files.proguard_specs: + local_proguard_specs = ctx.files.proguard_specs + proguard_specs = _collect_transitive_proguard_specs( + [resource_proguard_config], + local_proguard_specs, + proguard_deps, + ) + + if len(proguard_specs) > 0 and ctx.fragments.android.assume_min_sdk_version: + # NB: Order here is important. We're including generated Proguard specs before the user's + # specs so that they can override values. + proguard_specs = proguard_specs_for_manifest + proguard_specs + + return proguard_specs + proguard = struct( process = _process, + get_proguard_specs = _get_proguard_specs, ) testing = struct( validate_proguard_spec = _validate_proguard_spec, + collect_transitive_proguard_specs = _collect_transitive_proguard_specs, ProguardContextInfo = _ProguardContextInfo, ) diff --git a/rules/resources.bzl b/rules/resources.bzl index 86e82e0..29cee0e 100644 --- a/rules/resources.bzl +++ b/rules/resources.bzl @@ -116,6 +116,7 @@ _PACKAGED_FINAL_MANIFEST = "processed_manifest" _PACKAGED_RESOURCE_APK = "resources_apk" _PACKAGED_CLASS_JAR = "class_jar" _PACKAGED_VALIDATION_RESULT = "validation_result" +_RESOURCE_PROGUARD_CONFIG = "resource_proguard_config" _ResourcesPackageContextInfo = provider( "Packaged resources context object", @@ -126,6 +127,7 @@ _ResourcesPackageContextInfo = provider( _PACKAGED_VALIDATION_RESULT: "Validation result.", _R_JAVA: "JavaInfo for R.jar", _DATA_BINDING_LAYOUT_INFO: "Databinding layout info file.", + _RESOURCE_PROGUARD_CONFIG: "Resource proguard config", _PROVIDERS: "The list of all providers to propagate.", }, ) @@ -688,6 +690,7 @@ def _package( packaged_resources_ctx[_PACKAGED_FINAL_MANIFEST] = processed_manifest packaged_resources_ctx[_PACKAGED_RESOURCE_APK] = resource_apk packaged_resources_ctx[_PACKAGED_VALIDATION_RESULT] = resource_files_zip + packaged_resources_ctx[_RESOURCE_PROGUARD_CONFIG] = proguard_cfg # Fix class jar name because some tests depend on {label_name}_resources.jar being the suffix of # the path, with _common.PACKAGED_RESOURCES_SUFFIX removed from the label name. -- cgit v1.2.3 From e36435698792adc1a4a1c789c8fd1f8818926ea1 Mon Sep 17 00:00:00 2001 From: Tim Peut Date: Wed, 11 Jan 2023 13:31:08 -0800 Subject: Create acl and wire up with the starlark split transition. PiperOrigin-RevId: 501364665 Change-Id: I08a20a492b6bb33a2a00a0cf03ebb6fae246e898 --- rules/acls.bzl | 7 +++++++ .../android_binary_starlark_split_transition.bzl | 22 ++++++++++++++++++++++ rules/android_binary_internal/impl.bzl | 12 +++++++----- 3 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 rules/acls/android_binary_starlark_split_transition.bzl diff --git a/rules/acls.bzl b/rules/acls.bzl index 30314db..e17e136 100644 --- a/rules/acls.bzl +++ b/rules/acls.bzl @@ -41,6 +41,7 @@ load("//rules/acls:android_test_lockdown.bzl", "ANDROID_TEST_LOCKDOWN_GENERATOR_ load("//rules/acls:android_device_plugin_rollout.bzl", "ANDROID_DEVICE_PLUGIN_FALLBACK", "ANDROID_DEVICE_PLUGIN_ROLLOUT") load("//rules/acls:android_instrumentation_binary_starlark_resources.bzl", "ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK", "ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT") load("//rules/acls:android_binary_starlark_javac.bzl", "ANDROID_BINARY_STARLARK_JAVAC_FALLBACK", "ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT") +load("//rules/acls:android_binary_starlark_split_transition.bzl", "ANDROID_BINARY_STARLARK_SPLIT_TRANSITION_FALLBACK", "ANDROID_BINARY_STARLARK_SPLIT_TRANSITION_ROLLOUT") load("//rules/acls:android_feature_splits_dogfood.bzl", "ANDROID_FEATURE_SPLITS_DOGFOOD") load("//rules/acls:android_library_resources_without_srcs.bzl", "ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS", "ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS") load("//rules/acls:android_library_starlark_resource_outputs.bzl", "ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_FALLBACK", "ANDROID_LIBRARY_STARLARK_RESOURCE_OUTPUTS_ROLLOUT") @@ -102,6 +103,9 @@ def _in_android_instrumentation_binary_starlark_resources(fqn): def _in_android_binary_starlark_javac(fqn): return not matches(fqn, ANDROID_BINARY_STARLARK_JAVAC_FALLBACK_DICT) and matches(fqn, ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT_DICT) +def _in_android_binary_starlark_split_transition(fqn): + return not matches(fqn, ANDROID_BINARY_STARLARK_SPLIT_TRANSITION_FALLBACK_DICT) and matches(fqn, ANDROID_BINARY_STARLARK_SPLIT_TRANSITION_ROLLOUT_DICT) + def _in_android_feature_splits_dogfood(fqn): return matches(fqn, ANDROID_FEATURE_SPLITS_DOGFOOD_DICT) @@ -227,6 +231,8 @@ ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_ROLLOUT_DICT = make_dict(ANDRO ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK_DICT = make_dict(ANDROID_INSTRUMENTATION_BINARY_STARLARK_RESOURCES_FALLBACK) ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT_DICT = make_dict(ANDROID_BINARY_STARLARK_JAVAC_ROLLOUT) ANDROID_BINARY_STARLARK_JAVAC_FALLBACK_DICT = make_dict(ANDROID_BINARY_STARLARK_JAVAC_FALLBACK) +ANDROID_BINARY_STARLARK_SPLIT_TRANSITION_ROLLOUT_DICT = make_dict(ANDROID_BINARY_STARLARK_SPLIT_TRANSITION_ROLLOUT) +ANDROID_BINARY_STARLARK_SPLIT_TRANSITION_FALLBACK_DICT = make_dict(ANDROID_BINARY_STARLARK_SPLIT_TRANSITION_FALLBACK) ANDROID_FEATURE_SPLITS_DOGFOOD_DICT = make_dict(ANDROID_FEATURE_SPLITS_DOGFOOD) ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_DICT = make_dict(ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS) ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS_DICT = make_dict(ANDROID_LIBRARY_RESOURCES_WITHOUT_SRCS_GENERATOR_FUNCTIONS) @@ -331,6 +337,7 @@ acls = struct( in_android_device_plugin_rollout = _in_android_device_plugin_rollout, in_android_instrumentation_binary_starlark_resources = _in_android_instrumentation_binary_starlark_resources, in_android_binary_starlark_javac = _in_android_binary_starlark_javac, + in_android_binary_starlark_split_transition = _in_android_binary_starlark_split_transition, in_android_feature_splits_dogfood = _in_android_feature_splits_dogfood, in_android_library_starlark_resource_outputs_rollout = _in_android_library_starlark_resource_outputs_rollout, in_android_library_resources_without_srcs = _in_android_library_resources_without_srcs, diff --git a/rules/acls/android_binary_starlark_split_transition.bzl b/rules/acls/android_binary_starlark_split_transition.bzl new file mode 100644 index 0000000..0cc737b --- /dev/null +++ b/rules/acls/android_binary_starlark_split_transition.bzl @@ -0,0 +1,22 @@ +# Copyright 2023 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Allow and fallback lists for using the Starlark implementation of the android split transition""" + +ANDROID_BINARY_STARLARK_SPLIT_TRANSITION_ROLLOUT = [ + "//tools/build_defs/android/test/dev/android_binary_internal/java/com/nativelibs:__pkg__", +] + +ANDROID_BINARY_STARLARK_SPLIT_TRANSITION_FALLBACK = [ +] diff --git a/rules/android_binary_internal/impl.bzl b/rules/android_binary_internal/impl.bzl index 9d8864f..ffd798c 100644 --- a/rules/android_binary_internal/impl.bzl +++ b/rules/android_binary_internal/impl.bzl @@ -90,13 +90,15 @@ def _validate_manifest(ctx, packaged_resources_ctx, **unused_ctxs): ) def _process_native_libs(ctx, **_unusued_ctxs): - native_libs_info = _process_native_deps( - ctx, - filename = "nativedeps", - ) + providers = [] + if acls.in_android_binary_starlark_split_transition(str(ctx.label)): + providers.append(_process_native_deps( + ctx, + filename = "nativedeps", + )) return ProviderInfo( name = "native_libs_ctx", - value = struct(providers = [native_libs_info]), + value = struct(providers = providers), ) def use_legacy_manifest_merger(ctx): -- cgit v1.2.3 From eaf8274659bce3d91563df1dc09fe2653da309a6 Mon Sep 17 00:00:00 2001 From: Mauricio Galindo Date: Wed, 16 Nov 2022 19:22:18 -0800 Subject: [SNAP FORK][UPSTREAMED] Enable support for M1 macs --- toolchains/android_sdk/BUILD | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/toolchains/android_sdk/BUILD b/toolchains/android_sdk/BUILD index 105b773..1069735 100644 --- a/toolchains/android_sdk/BUILD +++ b/toolchains/android_sdk/BUILD @@ -16,10 +16,11 @@ toolchain_type( toolchain( name = "android_sdk_tools", - exec_compatible_with = [ - "@platforms//cpu:x86_64", - "@platforms//os:linux", - ], + # This causes the toolchain to not be selected under arm, so + # disable for now. + #exec_compatible_with = [ + # "@platforms//cpu:x86_64", + #], # TODO(b/175833893): This causes the toolchain to not be selected, so # disable for now. #target_compatible_with = [ -- cgit v1.2.3 From 695aafc52fd6f3ff87513cd61c062e5f8b5767cb Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Thu, 19 Jan 2023 14:20:42 -0800 Subject: OSS release of mobile-install v3 adapters bzl files PiperOrigin-RevId: 503264342 Change-Id: I9b437b9e0b8154872314539bcde772886c72e7ba --- mobile_install/adapters/aar_import.bzl | 113 ++++++++++++++++ mobile_install/adapters/android_binary.bzl | 149 +++++++++++++++++++++ .../adapters/android_instrumentation_test.bzl | 76 +++++++++++ mobile_install/adapters/android_library.bzl | 117 ++++++++++++++++ mobile_install/adapters/android_sdk.bzl | 39 ++++++ mobile_install/adapters/apk_import.bzl | 47 +++++++ mobile_install/adapters/base.bzl | 33 +++++ mobile_install/adapters/java_import.bzl | 71 ++++++++++ mobile_install/adapters/java_library.bzl | 70 ++++++++++ mobile_install/adapters/java_lite_grpc_library.bzl | 59 ++++++++ .../adapters/java_lite_proto_library.bzl | 57 ++++++++ mobile_install/adapters/java_rpc_toolchain.bzl | 39 ++++++ mobile_install/adapters/proto_lang_toolchain.bzl | 50 +++++++ mobile_install/adapters/proto_library.bzl | 47 +++++++ 14 files changed, 967 insertions(+) create mode 100644 mobile_install/adapters/aar_import.bzl create mode 100644 mobile_install/adapters/android_binary.bzl create mode 100644 mobile_install/adapters/android_instrumentation_test.bzl create mode 100644 mobile_install/adapters/android_library.bzl create mode 100644 mobile_install/adapters/android_sdk.bzl create mode 100644 mobile_install/adapters/apk_import.bzl create mode 100644 mobile_install/adapters/base.bzl create mode 100644 mobile_install/adapters/java_import.bzl create mode 100644 mobile_install/adapters/java_library.bzl create mode 100644 mobile_install/adapters/java_lite_grpc_library.bzl create mode 100644 mobile_install/adapters/java_lite_proto_library.bzl create mode 100644 mobile_install/adapters/java_rpc_toolchain.bzl create mode 100644 mobile_install/adapters/proto_lang_toolchain.bzl create mode 100644 mobile_install/adapters/proto_library.bzl diff --git a/mobile_install/adapters/aar_import.bzl b/mobile_install/adapters/aar_import.bzl new file mode 100644 index 0000000..f1e5db9 --- /dev/null +++ b/mobile_install/adapters/aar_import.bzl @@ -0,0 +1,113 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Rule adapter for aar_import.""" + +load(":adapters/base.bzl", "make_adapter") +load( + ":providers.bzl", + "MIAndroidAarNativeLibsInfo", + "MIAndroidAssetsInfo", + "MIAndroidDexInfo", + "MIAndroidResourcesInfo", + "MIJavaResourcesInfo", + "providers", +) +load(":resources.bzl", "liteparse") +load(":transform.bzl", "dex") +load("//rules:java.bzl", _java = "java") + +def _aspect_attrs(): + """Attrs of the rule requiring traversal by the aspect.""" + return ["deps", "exports"] + +def _adapt(target, ctx): + """Adapts the rule and target data. + + Args: + target: The target. + ctx: The context. + + Returns: + A list of providers. + """ + + assets = depset() + assets_dir = None + if AndroidAssetsInfo in target: + assets = target[AndroidAssetsInfo].assets + assets_dir = target[AndroidAssetsInfo].local_asset_dir + + label = None + resources = depset() + if AndroidResourcesInfo in target: + label = target[AndroidResourcesInfo].label + resources = target[AndroidResourcesInfo].direct_android_resources + + return [ + providers.make_mi_android_aar_native_libs_info( + native_libs = target[AndroidNativeLibsInfo].native_libs, + deps = providers.collect( + MIAndroidAarNativeLibsInfo, + ctx.rule.attr.deps, + ctx.rule.attr.exports, + ), + ), + providers.make_mi_android_assets_info( + assets = assets, + assets_dir = assets_dir, + deps = providers.collect( + MIAndroidAssetsInfo, + ctx.rule.attr.deps, + ctx.rule.attr.exports, + ), + ), + providers.make_mi_android_dex_info( + dex_shards = dex( + ctx, + target[JavaInfo].runtime_output_jars, + target[JavaInfo].transitive_deps, + ), + deps = providers.collect( + MIAndroidDexInfo, + ctx.rule.attr.deps, + ctx.rule.attr.exports, + ), + ), + providers.make_mi_android_resources_info( + # TODO(b/124229660): The package for an aar should be retrieved from + # the AndroidManifest.xml in the aar. Using the package is a short + # term work-around. + package = _java.resolve_package_from_label( + ctx.label, + ctx.rule.attr.package, + ), + label = label, + r_pb = liteparse(ctx), + resources = resources, + deps = providers.collect( + MIAndroidResourcesInfo, + ctx.rule.attr.deps, + ctx.rule.attr.exports, + ), + ), + providers.make_mi_java_resources_info( + deps = providers.collect( + MIJavaResourcesInfo, + ctx.rule.attr.deps, + ctx.rule.attr.exports, + ), + ), + ] + +aar_import = make_adapter(_aspect_attrs, _adapt) diff --git a/mobile_install/adapters/android_binary.bzl b/mobile_install/adapters/android_binary.bzl new file mode 100644 index 0000000..98641e1 --- /dev/null +++ b/mobile_install/adapters/android_binary.bzl @@ -0,0 +1,149 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Rule adapter for android_binary.""" + +load(":adapters/base.bzl", "make_adapter") +load(":launcher.bzl", "make_launcher") +load(":launcher_direct.bzl", "make_direct_launcher") +load(":process.bzl", "process") +load( + ":providers.bzl", + "MIAndroidAarNativeLibsInfo", + "MIAndroidAssetsInfo", + "MIAndroidDexInfo", + "MIAndroidResourcesInfo", + "MIJavaResourcesInfo", + "providers", +) +load(":resources.bzl", "get_assets_dir") +load(":transform.bzl", "dex", "filter_jars") +load(":utils.bzl", "utils") +load("//rules/flags:flags.bzl", "flags") + +def _aspect_attrs(): + """Attrs of the rule requiring traversal by the aspect.""" + return ["_android_sdk", "deps", "resources", "instruments"] + +def extract(target, ctx): + # extract is made visibile for testing + """extract the rule and target data. + + Args: + target: The target. + ctx: The context. + + Returns: + Input for process method + """ + return dict( + debug_key = utils.only(ctx.rule.files.debug_key, allow_empty = True), + debug_signing_keys = ctx.rule.files.debug_signing_keys, + debug_signing_lineage_file = utils.only(ctx.rule.files.debug_signing_lineage_file, allow_empty = True), + key_rotation_min_sdk = ctx.rule.attr.key_rotation_min_sdk, + merged_manifest = target[AndroidIdeInfo].generated_manifest, + native_libs = target[AndroidIdeInfo].native_libs, + package = target[AndroidIdeInfo].java_package, + resource_apk = target[AndroidIdeInfo].resource_apk, + resource_src_jar = target[AndroidIdeInfo].resource_jar.source_jar, # This is the R with real ids. + aar_native_libs_info = providers.make_mi_android_aar_native_libs_info( + deps = providers.collect( + MIAndroidAarNativeLibsInfo, + ctx.rule.attr.deps, + ), + ), + android_assets_info = providers.make_mi_android_assets_info( + assets = depset(ctx.rule.files.assets), + assets_dir = get_assets_dir( + ctx.rule.files.assets[0], + ctx.rule.attr.assets_dir, + ) if ctx.rule.files.assets else None, + deps = providers.collect( + MIAndroidAssetsInfo, + ctx.rule.attr.deps, + ), + ), + android_dex_info = providers.make_mi_android_dex_info( + dex_shards = dex( + ctx, + filter_jars( + ctx.label.name + "_resources.jar", + target[JavaInfo].runtime_output_jars, + ) + + ( + ), + target[JavaInfo].transitive_deps, + ), + deps = providers.collect(MIAndroidDexInfo, ctx.rule.attr.deps), + ), + # TODO(djwhang): It wasteful to collect packages in + # android_resources_info, rather we should be looking to pull them + # from the resources_v3_info. + android_resources_info = providers.make_mi_android_resources_info( + package = target[AndroidIdeInfo].java_package, + deps = providers.collect( + MIAndroidResourcesInfo, + ctx.rule.attr.deps, + ), + ), + java_resources_info = providers.make_mi_java_resources_info( + deps = providers.collect( + MIJavaResourcesInfo, + ctx.rule.attr.deps, + ), + ), + android_jar = ctx.rule.attr._android_sdk[AndroidSdkInfo].android_jar, + instrumented_app = ctx.rule.attr.instruments, + apk = target.android.apk, + ) + +def adapt(target, ctx): + # adapt is made visibile for testing + """Adapts the android rule + + Args: + target: The target. + ctx: The context. + Returns: + A list of providers + """ + + # launcher is created here to be used as the sibling everywhere else. + launcher = utils.isolated_declare_file(ctx, ctx.label.name + "_mi/launcher") + mi_app_info = process(ctx, sibling = launcher, **extract(target, ctx)) + + if flags.get(ctx).use_direct_deploy: + mi_app_launch_info = make_direct_launcher( + ctx, + mi_app_info, + launcher, + use_adb_root = flags.get(ctx).use_adb_root, + ) + else: + mi_app_launch_info = make_launcher( + ctx, + mi_app_info, + launcher, + use_adb_root = flags.get(ctx).use_adb_root, + ) + + return [ + mi_app_info, + mi_app_launch_info, + OutputGroupInfo( + mobile_install_INTERNAL_ = depset(mi_app_launch_info.runfiles).to_list(), + mobile_install_launcher_INTERNAL_ = [mi_app_launch_info.launcher], + ), + ] + +android_binary = make_adapter(_aspect_attrs, adapt) diff --git a/mobile_install/adapters/android_instrumentation_test.bzl b/mobile_install/adapters/android_instrumentation_test.bzl new file mode 100644 index 0000000..ffd9c4d --- /dev/null +++ b/mobile_install/adapters/android_instrumentation_test.bzl @@ -0,0 +1,76 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Rule adapter for android_instrumentation_test.""" + +load(":adapters/base.bzl", "make_adapter") +load(":launcher.bzl", "make_launcher") +load(":launcher_direct.bzl", "make_direct_launcher") +load(":providers.bzl", "MIAppInfo") +load(":utils.bzl", "utils") +load("//rules/flags:flags.bzl", "flags") + +def _aspect_attrs(): + """Attrs of the rule requiring traversal by the aspect.""" + return ["test_app", "support_apps"] + +def _adapt(target, ctx): + if not hasattr(ctx.attr, "_android_test_runner"): + fail("mobile-install does not support running tests on mac, check b/134172473 for more details") + + # TODO(b/): Tests have yet to be optimized so, this is an irrelevant error. + # if flags.get(ctx).enable_splits: + # fail("mobile-install does not support running tests for split apks, check b/139762843 for more details! To run tests with mobile-install without splits, pass --define=enable_splits=False") + + launcher = utils.isolated_declare_file(ctx, ctx.label.name + "_mi/launcher") + + test_app = ctx.rule.attr.test_app + + # TODO(manalinandan): Re-enable direct deploy for test. + # if _flags.get(ctx).use_direct_deploy: + if False: + mi_app_launch_info = make_direct_launcher( + ctx, + test_app[MIAppInfo], + launcher, + test_args = ctx.rule.attr.args, + test_support_apps = ctx.rule.attr.support_apps, + use_adb_root = flags.get(ctx).use_adb_root, + is_test = True, + ) + else: + googplayservices_container_app = None + test_support_apps = [] + for support_app in ctx.rule.attr.support_apps: + # Checks if the support_apps is an android_binary rule and 'GoogPlayServices' is present in the label + # This implies there is a GoogPlayServices container binary in the dependency + if MIAppInfo in support_app and "GoogPlayServices" in str(support_app.label): + googplayservices_container_app = support_app + elif MIAppInfo in support_app: + test_support_apps.append(support_app[MIAppInfo].apk) + mi_app_launch_info = make_launcher( + ctx, + test_app[MIAppInfo], + launcher, + test_args = ctx.rule.attr.args, + test_support_apks = test_support_apps, + googplayservices_container_app = googplayservices_container_app, + use_adb_root = flags.get(ctx).use_adb_root, + is_test = True, + ) + return [OutputGroupInfo( + mobile_install_INTERNAL_ = depset(mi_app_launch_info.runfiles).to_list(), + mobile_install_launcher_INTERNAL_ = [mi_app_launch_info.launcher], + )] + +android_instrumentation_test = make_adapter(_aspect_attrs, _adapt) diff --git a/mobile_install/adapters/android_library.bzl b/mobile_install/adapters/android_library.bzl new file mode 100644 index 0000000..698bf27 --- /dev/null +++ b/mobile_install/adapters/android_library.bzl @@ -0,0 +1,117 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Rule adapter for android_library.""" + +load(":adapters/base.bzl", "make_adapter") +load( + ":providers.bzl", + "MIAndroidAarNativeLibsInfo", + "MIAndroidAssetsInfo", + "MIAndroidDexInfo", + "MIAndroidResourcesInfo", + "MIAndroidSdkInfo", + "MIJavaResourcesInfo", + "providers", +) +load(":resources.bzl", "get_assets_dir") +load(":transform.bzl", "dex", "filter_jars") + +def _aspect_attrs(): + """Attrs of the rule requiring traversal by the aspect.""" + return [ + "_android_sdk", + + # For the Google-internal kotlin rule to access the toolchain to + # get kotlin std and runtime libs. + "_toolchain", + "deps", + "exports", + ] + +def _adapt(target, ctx): + """Adapts the rule and target data. + + Args: + target: The target. + ctx: The context. + + Returns: + A list of providers. + """ + kt_toolchain = [ctx.rule.attr._toolchain] if hasattr(ctx.rule.attr, "_toolchain") else [] + if ctx.rule.attr.neverlink: + return [] + + if target[AndroidIdeInfo].idl_generated_java_files: + aidl_lib = [ctx.rule.attr._android_sdk[MIAndroidSdkInfo].aidl_lib] + else: + aidl_lib = [] + + return [ + providers.make_mi_android_aar_native_libs_info( + deps = providers.collect( + MIAndroidAarNativeLibsInfo, + ctx.rule.attr.deps, + ctx.rule.attr.exports, + ), + ), + providers.make_mi_android_assets_info( + assets = depset(ctx.rule.files.assets), + assets_dir = get_assets_dir( + ctx.rule.files.assets[0], + ctx.rule.attr.assets_dir, + ) if ctx.rule.files.assets else None, + deps = providers.collect( + MIAndroidAssetsInfo, + ctx.rule.attr.deps, + ctx.rule.attr.exports, + ), + ), + providers.make_mi_android_dex_info( + dex_shards = dex( + ctx, + filter_jars( + ctx.label.name + "_resources.jar", + target[JavaInfo].runtime_output_jars, + ), + target[JavaInfo].transitive_deps, + ), + deps = providers.collect( + MIAndroidDexInfo, + ctx.rule.attr.deps, + ctx.rule.attr.exports, + aidl_lib, + kt_toolchain, + ), + ), + providers.make_mi_android_resources_info( + package = target[AndroidIdeInfo].java_package, + deps = providers.collect( + MIAndroidResourcesInfo, + ctx.rule.attr.deps, + ctx.rule.attr.exports, + ), + ), + providers.make_mi_java_resources_info( + deps = providers.collect( + MIJavaResourcesInfo, + ctx.rule.attr.deps, + ctx.rule.attr.exports, + aidl_lib, + kt_toolchain, + ), + ), + ] + +android_library = make_adapter(_aspect_attrs, _adapt) diff --git a/mobile_install/adapters/android_sdk.bzl b/mobile_install/adapters/android_sdk.bzl new file mode 100644 index 0000000..44f8f19 --- /dev/null +++ b/mobile_install/adapters/android_sdk.bzl @@ -0,0 +1,39 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Rule adapter for android_sdk.""" + +load(":adapters/base.bzl", "make_adapter") +load(":providers.bzl", "MIAndroidSdkInfo") + +def _aspect_attrs(): + """Attrs of the rule requiring traversal by the aspect.""" + return ["aidl_lib"] + +def _adapt(unused_target, ctx): + """Adapts the rule and target data. + + Args: + unused_target: The target. + ctx: The context. + + Returns: + A list of providers. + """ + return [ + MIAndroidSdkInfo( + aidl_lib = ctx.rule.attr.aidl_lib, + ), + ] + +android_sdk = make_adapter(_aspect_attrs, _adapt) diff --git a/mobile_install/adapters/apk_import.bzl b/mobile_install/adapters/apk_import.bzl new file mode 100644 index 0000000..425d230 --- /dev/null +++ b/mobile_install/adapters/apk_import.bzl @@ -0,0 +1,47 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Rule adapter for android_binary.""" + +load(":adapters/base.bzl", "make_adapter") +load(":providers.bzl", "MIAppInfo") +load(":utils.bzl", "utils") + +def _aspect_attrs(): + """Attrs of the rule requiring traversal by the aspect.""" + return ["unsigned_apk"] + +def adapt(target, ctx): + # adapt is made visibile for testing + """Adapts the android rule + + Args: + target: The target. + ctx: The context. + Returns: + A list of providers + """ + apk = ctx.rule.file.unsigned_apk + + package_name_output_file = utils.isolated_declare_file(ctx, ctx.label.name + "/manifest_package_name.txt") + + utils.extract_package_name(ctx, apk, package_name_output_file) + + return [ + MIAppInfo( + apk = apk, + manifest_package_name = package_name_output_file, + ), + ] + +apk_import = make_adapter(_aspect_attrs, adapt) diff --git a/mobile_install/adapters/base.bzl b/mobile_install/adapters/base.bzl new file mode 100644 index 0000000..c969d9f --- /dev/null +++ b/mobile_install/adapters/base.bzl @@ -0,0 +1,33 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Provides the base adapter functions.""" + +def make_adapter(aspect_attrs, adapt): + """Creates an Adapter. + + Args: + aspect_attrs: A function that returns a list of attrs for the aspect. + adapt: A function that extracts and processes data from the target. + + Returns: + A struct that represents an adapter. + """ + if not aspect_attrs: + fail("aspect_attrs is None.") + if not adapt: + fail("adapt is None.") + return struct( + aspect_attrs = aspect_attrs, + adapt = adapt, + ) diff --git a/mobile_install/adapters/java_import.bzl b/mobile_install/adapters/java_import.bzl new file mode 100644 index 0000000..d4b9c92 --- /dev/null +++ b/mobile_install/adapters/java_import.bzl @@ -0,0 +1,71 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Rule adapter for java_import.""" + +load(":adapters/base.bzl", "make_adapter") +load( + ":providers.bzl", + "MIAndroidDexInfo", + "MIJavaResourcesInfo", + "providers", +) +load(":transform.bzl", "dex", "extract_jar_resources") +load(":utils.bzl", "utils") + +def _aspect_attrs(): + """Attrs of the rule requiring traversal by the aspect.""" + return ["deps", "exports"] + +def _adapt(target, ctx): + """Adapts the rule and target data. + + Args: + target: The target. + ctx: The context. + + Returns: + A list of providers. + """ + if ctx.rule.attr.neverlink: + return [] + + return [ + providers.make_mi_android_dex_info( + dex_shards = dex( + ctx, + target[JavaInfo].runtime_output_jars, + target[JavaInfo].transitive_deps, + create_file = utils.declare_file, + ), + deps = providers.collect( + MIAndroidDexInfo, + ctx.rule.attr.deps, + ctx.rule.attr.exports, + ), + ), + providers.make_mi_java_resources_info( + java_resources = extract_jar_resources( + ctx, + target[JavaInfo].runtime_output_jars, + create_file = utils.declare_file, + ), + deps = providers.collect( + MIJavaResourcesInfo, + ctx.rule.attr.deps, + ctx.rule.attr.exports, + ), + ), + ] + +java_import = make_adapter(_aspect_attrs, _adapt) diff --git a/mobile_install/adapters/java_library.bzl b/mobile_install/adapters/java_library.bzl new file mode 100644 index 0000000..afeee64 --- /dev/null +++ b/mobile_install/adapters/java_library.bzl @@ -0,0 +1,70 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Rule adapter for java_library.""" + +load(":adapters/base.bzl", "make_adapter") +load( + ":providers.bzl", + "MIAndroidDexInfo", + "MIJavaResourcesInfo", + "providers", +) +load(":transform.bzl", "dex", "extract_jar_resources") + +def _aspect_attrs(): + """Attrs of the rule requiring traversal by the aspect.""" + return ["deps", "exports", "runtime_deps"] + +def _adapt(target, ctx): + """Adapts the rule and target data. + + Args: + target: The target. + ctx: The context. + + Returns: + A list of providers. + """ + if ctx.rule.attr.neverlink: + return [] + + return [ + providers.make_mi_android_dex_info( + dex_shards = dex( + ctx, + target[JavaInfo].runtime_output_jars, + target[JavaInfo].transitive_deps, + ), + deps = providers.collect( + MIAndroidDexInfo, + ctx.rule.attr.deps, + ctx.rule.attr.runtime_deps, + ctx.rule.attr.exports, + ), + ), + providers.make_mi_java_resources_info( + java_resources = extract_jar_resources( + ctx, + target[JavaInfo].runtime_output_jars, + ), + deps = providers.collect( + MIJavaResourcesInfo, + ctx.rule.attr.deps, + ctx.rule.attr.runtime_deps, + ctx.rule.attr.exports, + ), + ), + ] + +java_library = make_adapter(_aspect_attrs, _adapt) diff --git a/mobile_install/adapters/java_lite_grpc_library.bzl b/mobile_install/adapters/java_lite_grpc_library.bzl new file mode 100644 index 0000000..5eff4a8 --- /dev/null +++ b/mobile_install/adapters/java_lite_grpc_library.bzl @@ -0,0 +1,59 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Rule adapter for _java_lite_grpc_library.""" + +load(":adapters/base.bzl", "make_adapter") +load(":providers.bzl", "MIAndroidDexInfo", "MIJavaResourcesInfo", "providers") +load(":transform.bzl", "dex", "extract_jar_resources") + +def _aspect_attrs(): + """Attrs of the rule requiring traversal by the aspect.""" + return ["deps", "_toolchain"] + +def _adapt(target, ctx): + """Adapts the rule and target data. + + Args: + target: The target. + ctx: The context. + + Returns: + A list of providers. + """ + return [ + providers.make_mi_android_dex_info( + dex_shards = dex( + ctx, + target[JavaInfo].runtime_output_jars, + target[JavaInfo].transitive_deps, + ), + deps = providers.collect( + MIAndroidDexInfo, + ctx.rule.attr.deps, + [ctx.rule.attr._toolchain], + ), + ), + providers.make_mi_java_resources_info( + java_resources = extract_jar_resources( + ctx, + target[JavaInfo].runtime_output_jars, + ), + deps = providers.collect( + MIJavaResourcesInfo, + ctx.rule.attr.deps, + ), + ), + ] + +java_lite_grpc_library = make_adapter(_aspect_attrs, _adapt) diff --git a/mobile_install/adapters/java_lite_proto_library.bzl b/mobile_install/adapters/java_lite_proto_library.bzl new file mode 100644 index 0000000..b251b65 --- /dev/null +++ b/mobile_install/adapters/java_lite_proto_library.bzl @@ -0,0 +1,57 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Rule adapter for java_lite_proto_library. + +The java_lite_proto_library rule applies an aspect onto its proto dependencies. +Creates a "lite.jar" at every proto traversed. This adapter is used to just +propagate the deps, the proto_library rules. +""" + +load(":adapters/base.bzl", "make_adapter") +load(":providers.bzl", "MIAndroidDexInfo", "MIJavaResourcesInfo", "providers") + +def _aspect_attrs(): + """Attrs of the rule requiring traversal by the aspect.""" + return ["deps", "_aspect_proto_toolchain_for_javalite"] + +def _adapt(target, ctx): + """Adapts the rule and target data. + + Args: + target: The target. + ctx: The context. + + Returns: + A list of providers. + """ + if not ctx.rule.attr.deps: + return [] + return [ + providers.make_mi_android_dex_info( + deps = providers.collect( + MIAndroidDexInfo, + ctx.rule.attr.deps, + [ctx.rule.attr._aspect_proto_toolchain_for_javalite], + ), + ), + providers.make_mi_java_resources_info( + deps = providers.collect( + MIJavaResourcesInfo, + ctx.rule.attr.deps, + [ctx.rule.attr._aspect_proto_toolchain_for_javalite], + ), + ), + ] + +java_lite_proto_library = make_adapter(_aspect_attrs, _adapt) diff --git a/mobile_install/adapters/java_rpc_toolchain.bzl b/mobile_install/adapters/java_rpc_toolchain.bzl new file mode 100644 index 0000000..76ee505 --- /dev/null +++ b/mobile_install/adapters/java_rpc_toolchain.bzl @@ -0,0 +1,39 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Rule adapter for java_rpc_toolchain.bzl.""" + +load(":adapters/base.bzl", "make_adapter") +load(":providers.bzl", "MIAndroidDexInfo", "providers") + +def _aspect_attrs(): + """Attrs of the rule requiring traversal by the aspect.""" + return ["runtime"] # all potential implicit runtime deps + +def _adapt(unused_target, ctx): + """Adapts the rule and target data. + + Args: + unused_target: The target. + ctx: The context. + + Returns: + A list of providers. + """ + return [ + providers.make_mi_android_dex_info( + deps = providers.collect(MIAndroidDexInfo, ctx.rule.attr.runtime), + ), + ] + +java_rpc_toolchain = make_adapter(_aspect_attrs, _adapt) diff --git a/mobile_install/adapters/proto_lang_toolchain.bzl b/mobile_install/adapters/proto_lang_toolchain.bzl new file mode 100644 index 0000000..1e767e2 --- /dev/null +++ b/mobile_install/adapters/proto_lang_toolchain.bzl @@ -0,0 +1,50 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Rule adapter for proto_lang_toolchain.""" + +load(":adapters/base.bzl", "make_adapter") +load(":providers.bzl", "MIAndroidDexInfo", "MIJavaResourcesInfo", "providers") + +def _aspect_attrs(): + """Attrs of the rule requiring traversal by the aspect.""" + return ["runtime"] + +def _adapt(unused_target, ctx): + """Adapts the rule and target data. + + Args: + unused_target: The target. + ctx: The context. + + Returns: + A list of providers. + """ + if not ctx.rule.attr.runtime: + return [] + return [ + providers.make_mi_android_dex_info( + deps = providers.collect( + MIAndroidDexInfo, + [ctx.rule.attr.runtime], + ), + ), + providers.make_mi_java_resources_info( + deps = providers.collect( + MIJavaResourcesInfo, + [ctx.rule.attr.runtime], + ), + ), + ] + +proto_lang_toolchain = make_adapter(_aspect_attrs, _adapt) diff --git a/mobile_install/adapters/proto_library.bzl b/mobile_install/adapters/proto_library.bzl new file mode 100644 index 0000000..9e5d8da --- /dev/null +++ b/mobile_install/adapters/proto_library.bzl @@ -0,0 +1,47 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Rule adapter for proto_library.""" + +load(":adapters/base.bzl", "make_adapter") +load(":providers.bzl", "MIAndroidDexInfo", "providers") +load(":transform.bzl", "dex") + +def _aspect_attrs(): + """Attrs of the rule requiring traversal by the aspect.""" + return ["deps"] + +def _adapt(target, ctx): + """Adapts the rule and target data. + + Args: + target: The target. + ctx: The context. + + Returns: + A list of providers. + """ + if not JavaInfo in target: + return [] + return [ + providers.make_mi_android_dex_info( + dex_shards = dex( + ctx, + [j.class_jar for j in target[JavaInfo].outputs.jars], + target[JavaInfo].transitive_deps, + ), + deps = providers.collect(MIAndroidDexInfo, ctx.rule.attr.deps), + ), + ] + +proto_library = make_adapter(_aspect_attrs, _adapt) -- cgit v1.2.3 From c559620cfe496e264275ebc6ea4a97cab1a2d00b Mon Sep 17 00:00:00 2001 From: Andrew Sinclair Date: Thu, 26 Jan 2023 14:59:48 -0800 Subject: Build the android_feature_module library with the Android Config. PiperOrigin-RevId: 504950648 Change-Id: I3def32c1c17470b95bef7fab25a348f58b5183c5 --- rules/android_application/android_application_rule.bzl | 1 + rules/android_application/android_feature_module_rule.bzl | 5 +++-- rules/android_application/attrs.bzl | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/rules/android_application/android_application_rule.bzl b/rules/android_application/android_application_rule.bzl index 5def705..3ecc670 100644 --- a/rules/android_application/android_application_rule.bzl +++ b/rules/android_application/android_application_rule.bzl @@ -326,6 +326,7 @@ def _impl(ctx): android_application = rule( attrs = ANDROID_APPLICATION_ATTRS, + cfg = android_common.android_platforms_transition, fragments = [ "android", "java", diff --git a/rules/android_application/android_feature_module_rule.bzl b/rules/android_application/android_feature_module_rule.bzl index ec051d5..a0f7d2b 100644 --- a/rules/android_application/android_feature_module_rule.bzl +++ b/rules/android_application/android_feature_module_rule.bzl @@ -24,6 +24,7 @@ load("//rules:acls.bzl", "acls") load( "//rules:utils.bzl", "get_android_toolchain", + "utils", ) def _impl(ctx): @@ -39,7 +40,7 @@ def _impl(ctx): args.add(ctx.attr.binary[ApkInfo].unsigned_apk.path) args.add(ctx.configuration.coverage_enabled) args.add(ctx.fragments.android.desugar_java8_libs) - args.add(ctx.attr.library.label) + args.add(utils.dedupe_split_attr(ctx.split_attr.library).label) args.add(get_android_toolchain(ctx).xmllint_tool.files_to_run.executable) args.add(get_android_toolchain(ctx).unzip_tool.files_to_run.executable) @@ -59,7 +60,7 @@ def _impl(ctx): return [ AndroidFeatureModuleInfo( binary = ctx.attr.binary, - library = ctx.attr.library, + library = utils.dedupe_split_attr(ctx.split_attr.library), title_id = ctx.attr.title_id, title_lib = ctx.attr.title_lib, feature_name = ctx.attr.feature_name, diff --git a/rules/android_application/attrs.bzl b/rules/android_application/attrs.bzl index 131fb46..55864c2 100644 --- a/rules/android_application/attrs.bzl +++ b/rules/android_application/attrs.bzl @@ -84,6 +84,7 @@ ANDROID_FEATURE_MODULE_ATTRS = dict( feature_name = attr.string(), library = attr.label( allow_rules = ["android_library"], + cfg = android_common.multi_cpu_configuration, mandatory = True, doc = "android_library target to include as a feature split.", ), -- cgit v1.2.3 From 574c513a8196b869c84a5779059c725ac1632292 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 27 Jan 2023 13:39:08 -0800 Subject: Internal change PiperOrigin-RevId: 505198531 Change-Id: I026ba0824985b9a747d6cb8ae88220c71ac84fd3 --- rules/android_application/android_application_rule.bzl | 1 - rules/java.bzl | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/rules/android_application/android_application_rule.bzl b/rules/android_application/android_application_rule.bzl index 3ecc670..ce3f321 100644 --- a/rules/android_application/android_application_rule.bzl +++ b/rules/android_application/android_application_rule.bzl @@ -140,7 +140,6 @@ def _process_feature_module( ctx, inputs = [filtered_res, native_libs], output = out, - exclude_build_data = True, java_toolchain = _common.get_java_toolchain(ctx), ) diff --git a/rules/java.bzl b/rules/java.bzl index 2e2fc58..25c7090 100644 --- a/rules/java.bzl +++ b/rules/java.bzl @@ -359,14 +359,14 @@ def _singlejar( output, mnemonic = "SingleJar", progress_message = "Merge into a single jar.", - exclude_build_data = False, + include_build_data = False, java_toolchain = None): args = ctx.actions.args() args.add("--output") args.add(output) args.add("--compression") args.add("--normalize") - if exclude_build_data: + if not include_build_data: args.add("--exclude_build_data") args.add("--warn_duplicate_resources") if inputs: -- cgit v1.2.3 From 873ac4581bb26459fec2331cd94511a7b1ad1a29 Mon Sep 17 00:00:00 2001 From: A Googler Date: Tue, 31 Jan 2023 07:29:50 -0800 Subject: added new action type BUMP_DEFAULT for use in aosp updates min_sdk to a default value only if it is not already set in the manifest Bug: 249265719 PiperOrigin-RevId: 505996954 Change-Id: I41b3d1c7e119fc4dfe6f740031cabbf90c245975 --- .../enforce_min_sdk_floor/enforce_min_sdk_floor.py | 92 +++++++++++++++++++--- .../enforce_min_sdk_floor_test.py | 15 ++++ 2 files changed, 96 insertions(+), 11 deletions(-) diff --git a/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py b/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py index b7192f7..d45921a 100644 --- a/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py +++ b/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor.py @@ -28,6 +28,7 @@ from absl import flags BUMP = "bump" VALIDATE = "validate" +SET_DEFAULT = "set_default" USES_SDK = "uses-sdk" MIN_SDK_ATTRIB = "{http://schemas.android.com/apk/res/android}minSdkVersion" @@ -37,8 +38,8 @@ FLAGS = flags.FLAGS flags.DEFINE_enum( "action", None, - [BUMP, VALIDATE], - f"Action to perform, either {BUMP} or {VALIDATE}") + [BUMP, VALIDATE, SET_DEFAULT], + f"Action to perform, either {BUMP}, {VALIDATE}, or {SET_DEFAULT}") flags.DEFINE_string( "manifest", None, @@ -48,6 +49,12 @@ flags.DEFINE_integer( 0, "Min SDK floor", lower_bound=0) +# Needed for SET_DEFAULT +flags.DEFINE_string( + "default_min_sdk", + None, + "Default min SDK") +# Needed for BUMP and SET_DEFAULT flags.DEFINE_string( "output", None, @@ -59,6 +66,22 @@ class MinSdkError(Exception): """Raised when there is a problem with the min SDK attribute in AndroidManifest.xml.""" +def ParseNamespaces(xml_content): + """Parse namespaces first to keep the prefix. + + Args: + xml_content: str, the contents of the AndroidManifest.xml file + """ + ns_parser = ET.XMLPullParser(events=["start-ns"]) + ns_parser.feed(xml_content) + ns_parser.close() + for _, ns_tuple in ns_parser.read_events(): + try: + ET.register_namespace(ns_tuple[0], ns_tuple[1]) + except ValueError: + pass + + def _BumpMinSdk(xml_content, min_sdk_floor): """Checks the min SDK in xml_content and replaces with min_sdk_floor if needed. @@ -76,15 +99,7 @@ def _BumpMinSdk(xml_content, min_sdk_floor): if min_sdk_floor == 0: return xml_content, "No min SDK floor specified. Manifest unchanged." - # Parse namespaces first to keep the prefix. - ns_parser = ET.XMLPullParser(events=["start-ns"]) - ns_parser.feed(xml_content) - ns_parser.close() - for _, ns_tuple in ns_parser.read_events(): - try: - ET.register_namespace(ns_tuple[0], ns_tuple[1]) - except ValueError: - pass + ParseNamespaces(xml_content) root = ET.fromstring(xml_content) uses_sdk = root.find(USES_SDK) @@ -163,6 +178,49 @@ def _ValidateMinSdk(xml_content, min_sdk_floor): return f"minSdkVersion = {min_sdk}\n min SDK floor = {min_sdk_floor}" +def _SetDefaultMinSdk(xml_content, default_min_sdk): + """Checks the min SDK in xml_content and replaces with default_min_sdk if it is not already set. + + Args: + xml_content: str, the contents of the AndroidManifest.xml file + default_min_sdk: str, can be set to either a number or an unreleased version + full name + + Returns: + A tuple with the following elements: + - str: The xml contents of the manifest with the min SDK floor enforced. + This string will be equal to the input if the min SDK is already set. + - str: log message of action taken + """ + if default_min_sdk is None: + return xml_content, ("No default min SDK floor specified. Manifest " + "unchanged.") + + ParseNamespaces(xml_content) + + root = ET.fromstring(xml_content) + uses_sdk = root.find(USES_SDK) + if uses_sdk is None: + ET.SubElement(root, USES_SDK, {MIN_SDK_ATTRIB: default_min_sdk}) + return ( + ET.tostring(root, encoding="utf-8", xml_declaration=True), + "No uses-sdk element found while default is specified. " + + f"Min SDK ({default_min_sdk}) added.") + + min_sdk = uses_sdk.get(MIN_SDK_ATTRIB) + if min_sdk is None: + uses_sdk.set(MIN_SDK_ATTRIB, str(default_min_sdk)) + return ( + ET.tostring(root, encoding="utf-8", xml_declaration=True), + "No minSdkVersion attribute found while default is specified" + + f"({default_min_sdk}). Min SDK set to default.") + + return ( + xml_content, + f"minSdkVersion attribute specified in the manifest ({min_sdk}) " + + ". Manifest unchanged.") + + def main(unused_argv): manifest_path = FLAGS.manifest with open(manifest_path, "rb") as f: @@ -178,6 +236,18 @@ def main(unused_argv): with open(output_path, "wb") as f: f.write(out_contents) + elif FLAGS.action == SET_DEFAULT: + output_path = FLAGS.output + dirname = os.path.dirname(output_path) + if not os.path.exists(dirname): + os.makedirs(dirname) + + out_contents, log_message = _SetDefaultMinSdk( + manifest, FLAGS.default_min_sdk + ) + with open(output_path, "wb") as f: + f.write(out_contents) + elif FLAGS.action == VALIDATE: try: log_message = _ValidateMinSdk(manifest, FLAGS.min_sdk_floor) diff --git a/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor_test.py b/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor_test.py index e49d2ac..87f9a8d 100644 --- a/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor_test.py +++ b/src/tools/enforce_min_sdk_floor/enforce_min_sdk_floor_test.py @@ -17,6 +17,7 @@ import unittest import xml.etree.ElementTree as ET from google3.third_party.bazel_rules.rules_android.src.tools.enforce_min_sdk_floor.enforce_min_sdk_floor import _BumpMinSdk +from google3.third_party.bazel_rules.rules_android.src.tools.enforce_min_sdk_floor.enforce_min_sdk_floor import _SetDefaultMinSdk from google3.third_party.bazel_rules.rules_android.src.tools.enforce_min_sdk_floor.enforce_min_sdk_floor import _ValidateMinSdk from google3.third_party.bazel_rules.rules_android.src.tools.enforce_min_sdk_floor.enforce_min_sdk_floor import MIN_SDK_ATTRIB @@ -80,6 +81,20 @@ class EnforceMinSdkFloorTest(unittest.TestCase): min_sdk = ET.fromstring(out).find(USES_SDK).get(MIN_SDK_ATTRIB) self.assertEqual(min_sdk, "14") + def test_set_default_no_uses(self): + out, _ = _SetDefaultMinSdk(MANIFEST_NO_USES_SDK, "11") + min_sdk = ET.fromstring(out).find(USES_SDK).get(MIN_SDK_ATTRIB) + self.assertEqual(min_sdk, "11") + + def test_set_default_no_min_sdk(self): + out, _ = _SetDefaultMinSdk(MANIFEST_NO_USES_SDK, "current") + min_sdk = ET.fromstring(out).find(USES_SDK).get(MIN_SDK_ATTRIB) + self.assertEqual(min_sdk, "current") + + def test_set_default_min_sdk_already_specified(self): + out, _ = _SetDefaultMinSdk(MANIFEST_MIN_SDK, "14") + self.assertEqual(out, MANIFEST_MIN_SDK) + def test_validate_no_min_sdk_floor(self): _ = _ValidateMinSdk(MANIFEST_NO_USES_SDK, 0) -- cgit v1.2.3 From e72add45b05cee6dc640477bd5cda7276d08d9f1 Mon Sep 17 00:00:00 2001 From: A Googler Date: Tue, 7 Feb 2023 07:32:13 -0800 Subject: Create action for setting a default min sdk value if not already specified. For use in AOSP Bug: 249265719 PiperOrigin-RevId: 507774915 Change-Id: I28e9b43ea014aab24438742f4fdb130031a6a956 --- rules/aar_import/impl.bzl | 4 +-- rules/android_binary_internal/impl.bzl | 2 +- rules/android_library/impl.bzl | 4 +-- rules/resources.bzl | 61 +++++++++++++++++++++++++++++++--- 4 files changed, 62 insertions(+), 9 deletions(-) diff --git a/rules/aar_import/impl.bzl b/rules/aar_import/impl.bzl index 894baba..46e1337 100644 --- a/rules/aar_import/impl.bzl +++ b/rules/aar_import/impl.bzl @@ -475,7 +475,7 @@ def impl(ctx): ctx, aar = aar, package = package, - manifest = manifest_ctx.min_sdk_bumped_manifest, + manifest = manifest_ctx.processed_manifest, deps = ctx.attr.deps, aar_resources_extractor_tool = _get_android_toolchain(ctx).aar_resources_extractor.files_to_run, @@ -555,7 +555,7 @@ def impl(ctx): ctx, aar = aar, package = package, - manifest = manifest_ctx.min_sdk_bumped_manifest, + manifest = manifest_ctx.processed_manifest, checks = _get_android_toolchain(ctx).aar_import_checks.files_to_run, )) diff --git a/rules/android_binary_internal/impl.bzl b/rules/android_binary_internal/impl.bzl index ffd798c..3d89a7b 100644 --- a/rules/android_binary_internal/impl.bzl +++ b/rules/android_binary_internal/impl.bzl @@ -47,7 +47,7 @@ def _process_resources(ctx, manifest_ctx, java_package, **unused_ctxs): assets = ctx.files.assets, assets_dir = ctx.attr.assets_dir, resource_files = ctx.files.resource_files, - manifest = manifest_ctx.min_sdk_bumped_manifest, + manifest = manifest_ctx.processed_manifest, manifest_values = utils.expand_make_vars(ctx, ctx.attr.manifest_values), resource_configs = ctx.attr.resource_configuration_filters, densities = ctx.attr.densities, diff --git a/rules/android_library/impl.bzl b/rules/android_library/impl.bzl index 4f00ec4..72429fd 100644 --- a/rules/android_library/impl.bzl +++ b/rules/android_library/impl.bzl @@ -151,7 +151,7 @@ def _process_resources(ctx, java_package, manifest_ctx, **unused_ctxs): # Process Android Resources resources_ctx = _resources.process( ctx, - manifest = manifest_ctx.min_sdk_bumped_manifest, + manifest = manifest_ctx.processed_manifest, resource_files = ctx.attr.resource_files, defined_assets = ctx.attr._defined_assets, assets = ctx.attr.assets, @@ -406,7 +406,7 @@ def _process_intellij(ctx, java_package, manifest_ctx, resources_ctx, idl_ctx, j android_ide_info = _intellij.make_android_ide_info( ctx, java_package = java_package, - manifest = manifest_ctx.min_sdk_bumped_manifest, + manifest = manifest_ctx.processed_manifest, defines_resources = resources_ctx.defines_resources, merged_manifest = resources_ctx.merged_manifest, resources_apk = resources_ctx.resources_apk, diff --git a/rules/resources.bzl b/rules/resources.bzl index 29cee0e..93a66a5 100644 --- a/rules/resources.bzl +++ b/rules/resources.bzl @@ -133,12 +133,12 @@ _ResourcesPackageContextInfo = provider( ) # Manifest context attributes -_MIN_SDK_BUMPED_MANIFEST = "min_sdk_bumped_manifest" +_PROCESSED_MANIFEST = "processed_manifest" _ManifestContextInfo = provider( "Manifest context object", fields = { - _MIN_SDK_BUMPED_MANIFEST: "The manifest with the min SDK bumped to the floor.", + _PROCESSED_MANIFEST: "The manifest after the min SDK has been changed as necessary.", }, ) @@ -1017,7 +1017,7 @@ def _bump_min_sdk( """ manifest_ctx = {} if not manifest or floor <= 0: - manifest_ctx[_MIN_SDK_BUMPED_MANIFEST] = manifest + manifest_ctx[_PROCESSED_MANIFEST] = manifest return _ManifestContextInfo(**manifest_ctx) args = ctx.actions.args() @@ -1043,7 +1043,57 @@ def _bump_min_sdk( mnemonic = "BumpMinSdkFloor", progress_message = "Bumping up AndroidManifest min SDK %s" % str(ctx.label), ) - manifest_ctx[_MIN_SDK_BUMPED_MANIFEST] = out_manifest + manifest_ctx[_PROCESSED_MANIFEST] = out_manifest + + return _ManifestContextInfo(**manifest_ctx) + +def _set_default_min_sdk( + ctx, + manifest, + default, + enforce_min_sdk_floor_tool): + """ Sets the min SDK attribute of AndroidManifest to default if it is not already set. + + Args: + ctx: The rules context. + manifest: File. The AndroidManifest.xml file. + default: string. The default value for min SDK. The manifest is unchanged if it already + specifies a min SDK. + enforce_min_sdk_floor_tool: FilesToRunProvider. The enforce_min_sdk_tool executable or + FilesToRunprovider + + Returns: + A dict containing _ManifestContextInfo provider fields. + """ + manifest_ctx = {} + if not manifest or not default: + manifest_ctx[_PROCESSED_MANIFEST] = manifest + return _ManifestContextInfo(**manifest_ctx) + + args = ctx.actions.args() + args.add("-action", "set_default") + args.add("-manifest", manifest) + args.add("-default_min_sdk", default) + + out_dir = "_migrated/_min_sdk_default_set/" + ctx.label.name + "/" + log = ctx.actions.declare_file( + out_dir + "log.txt", + ) + args.add("-log", log.path) + + out_manifest = ctx.actions.declare_file( + out_dir + "AndroidManifest.xml", + ) + args.add("-output", out_manifest.path) + ctx.actions.run( + executable = enforce_min_sdk_floor_tool, + inputs = [manifest], + outputs = [out_manifest, log], + arguments = [args], + mnemonic = "SetDefaultMinSdkFloor", + progress_message = "Setting AndroidManifest min SDK to default %s" % str(ctx.label), + ) + manifest_ctx[_PROCESSED_MANIFEST] = out_manifest return _ManifestContextInfo(**manifest_ctx) @@ -1794,6 +1844,9 @@ resources = struct( # Exposed for android_library, aar_import, and android_binary bump_min_sdk = _bump_min_sdk, + # Exposed for use in AOSP + set_default_sdk = _set_default_min_sdk, + # Exposed for android_binary validate_min_sdk = _validate_min_sdk, -- cgit v1.2.3 From e6bfa0e2e9122a42138d6d7071cb45a318ff9384 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Tue, 7 Feb 2023 12:45:08 -0800 Subject: Add robolectric as a dep to rules_android PiperOrigin-RevId: 507858594 Change-Id: Ibf02dbf22ca9328fdd9aca0a3adf2e153c2a5fe7 --- defs.bzl | 3 +++ prereqs.bzl | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/defs.bzl b/defs.bzl index 85536d4..8abd100 100644 --- a/defs.bzl +++ b/defs.bzl @@ -17,6 +17,7 @@ load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") +load("@robolectric//bazel:robolectric.bzl", "robolectric_repositories") load("@rules_jvm_external//:defs.bzl", "maven_install") def rules_android_workspace(): @@ -62,4 +63,6 @@ def rules_android_workspace(): version = "v0.0.0-20210220032951-036812b2e83c", ) + robolectric_repositories() + diff --git a/prereqs.bzl b/prereqs.bzl index 09cfeec..d106a88 100644 --- a/prereqs.bzl +++ b/prereqs.bzl @@ -76,4 +76,12 @@ def rules_android_prereqs(): ], ) + maybe( + http_archive, + name = "robolectric", + urls = ["https://github.com/robolectric/robolectric-bazel/archive/4.9.2.tar.gz"], + strip_prefix = "robolectric-bazel-4.9.2", + sha256 = "7e007fcfdca7b7228cb4de72707e8b317026ea95000f963e91d5ae365be52d0d", + ) + -- cgit v1.2.3 From 2b59dee7171669d07d7060900a4b7257683f5050 Mon Sep 17 00:00:00 2001 From: Ted Xie Date: Fri, 10 Feb 2023 12:00:29 -0800 Subject: Add some simple branch conditions to rules_android basic app PiperOrigin-RevId: 508719550 Change-Id: I248eecd71e28f644beb40113e93fb4d070aa5d47 --- .../basicapp/java/com/basicapp/BasicActivity.java | 21 ++++++++++++++++++++- .../java/com/basicapp/res/layout/basic_activity.xml | 11 +++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/examples/basicapp/java/com/basicapp/BasicActivity.java b/examples/basicapp/java/com/basicapp/BasicActivity.java index 14042be..03c9aef 100644 --- a/examples/basicapp/java/com/basicapp/BasicActivity.java +++ b/examples/basicapp/java/com/basicapp/BasicActivity.java @@ -17,9 +17,10 @@ package com.basicapp; import android.app.Activity; import android.os.Bundle; import android.view.Menu; +import android.view.View; +import android.widget.Button; import android.widget.TextView; - /** * The main activity of the Basic Sample App. */ @@ -29,6 +30,24 @@ public class BasicActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.basic_activity); + + final Button buttons[] = { + findViewById(R.id.button_id_fizz), findViewById(R.id.button_id_buzz), + }; + + for (Button b : buttons) { + b.setOnClickListener( + new View.OnClickListener() { + public void onClick(View v) { + TextView tv = findViewById(R.id.text_hello); + if (v.getId() == R.id.button_id_fizz) { + tv.setText("fizz"); + } else if (v.getId() == R.id.button_id_buzz) { + tv.setText("buzz"); + } + } + }); + } } @Override diff --git a/examples/basicapp/java/com/basicapp/res/layout/basic_activity.xml b/examples/basicapp/java/com/basicapp/res/layout/basic_activity.xml index 61fb73e..f84199c 100644 --- a/examples/basicapp/java/com/basicapp/res/layout/basic_activity.xml +++ b/examples/basicapp/java/com/basicapp/res/layout/basic_activity.xml @@ -9,4 +9,15 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" /> +