diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml
index cd8bc4bea..f6b8a5d72 100644
--- a/.bazelci/presubmit.yml
+++ b/.bazelci/presubmit.yml
@@ -175,3 +175,23 @@ tasks:
<<: *rules_flags
build_targets:
- "//java/com/basicapp:basic_app"
+ ubuntu2004_bundle:
+ name: "Bundle app ubuntu"
+ platform: ubuntu2004
+ bazel: ${{ bazel }}
+ working_directory: examples/bundle
+ build_flags:
+ <<: *rules_flags
+ build_targets:
+ - "//app:assets"
+ ubuntu2004_bundle_bzlmod:
+ name: "Bundle app ubuntu bzlmod"
+ platform: ubuntu2004
+ bazel: ${{ bazel }}
+ working_directory: examples/bundle
+ build_flags:
+ <<: *rules_flags
+ ? "--enable_bzlmod"
+ ? "--enable_workspace=false"
+ build_targets:
+ - "//app:assets"
diff --git a/examples/bundle/.bazelrc b/examples/bundle/.bazelrc
new file mode 100644
index 000000000..a28203e1e
--- /dev/null
+++ b/examples/bundle/.bazelrc
@@ -0,0 +1,10 @@
+# Flags needed while the Android rules are being migrated to Starlark.
+common --experimental_google_legacy_api
+common --experimental_enable_android_migration_apis
+common --android_sdk=@androidsdk//:sdk
+common:core_library_desugaring --desugar_java8_libs
+
+# Flags to enable mobile-install v3
+mobile-install --mode=skylark --mobile_install_aspect=@rules_android//mobile_install:mi.bzl --mobile_install_supported_rules=android_binary
+# Required to invoke the Studio deployer jar
+mobile-install --tool_java_runtime_version=17
diff --git a/examples/bundle/.gitignore b/examples/bundle/.gitignore
new file mode 100644
index 000000000..63f1fef0e
--- /dev/null
+++ b/examples/bundle/.gitignore
@@ -0,0 +1 @@
+*.lock
diff --git a/examples/bundle/BUILD b/examples/bundle/BUILD
new file mode 100644
index 000000000..a09fce916
--- /dev/null
+++ b/examples/bundle/BUILD
@@ -0,0 +1 @@
+# Empty build file to satisfy gazelle for rules_go.
\ No newline at end of file
diff --git a/examples/bundle/MODULE.bazel b/examples/bundle/MODULE.bazel
new file mode 100644
index 000000000..ef2860cd8
--- /dev/null
+++ b/examples/bundle/MODULE.bazel
@@ -0,0 +1,55 @@
+module(
+ name = "bundle",
+)
+
+bazel_dep(name = "rules_java", version = "7.4.0")
+bazel_dep(name = "bazel_skylib", version = "1.3.0")
+
+bazel_dep(
+ name = "rules_android",
+ version = "0.5.1",
+)
+
+local_path_override(
+ module_name = "rules_android",
+ path = "../../",
+)
+
+remote_android_extensions = use_extension(
+ "@rules_android//bzlmod_extensions:android_extensions.bzl",
+ "remote_android_tools_extensions")
+use_repo(remote_android_extensions, "android_gmaven_r8", "android_tools")
+
+register_toolchains(
+ "@rules_android//toolchains/android:android_default_toolchain",
+ "@rules_android//toolchains/android_sdk:android_sdk_tools",
+)
+
+android_sdk_repository_extension = use_extension("@rules_android//rules/android_sdk_repository:rule.bzl", "android_sdk_repository_extension")
+use_repo(android_sdk_repository_extension, "androidsdk")
+
+register_toolchains("@androidsdk//:sdk-toolchain", "@androidsdk//:all")
+
+bazel_dep(name = "rules_jvm_external", version = "5.3")
+
+# Load the maven extension from rules_jvm_external
+maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
+
+maven.install(
+ name = "maven",
+ aar_import_bzl_label = "@rules_android//rules:rules.bzl",
+ artifacts = [
+ "com.google.guava:guava:32.1.2-android",
+ "com.arthenica:ffmpeg-kit-https:4.4.LTS",
+ ],
+ repositories = [
+ "https://maven.google.com",
+ "https://repo1.maven.org/maven2",
+ ],
+ use_starlark_android_rules = True,
+)
+use_repo(maven, "maven")
+
+
+
+
diff --git a/examples/bundle/README.md b/examples/bundle/README.md
new file mode 100644
index 000000000..0d923e32d
--- /dev/null
+++ b/examples/bundle/README.md
@@ -0,0 +1,12 @@
+To build, ensure that the `ANDROID_HOME` environment variable is set to the path
+to an Android SDK, and run:
+
+```
+bazel build app:assets
+```
+
+This will build application bundle containing a dynamic feature containing assets (named assets.txt). Verify with :
+
+```
+jar -tf bazel-bin/app/assets_unsigned.aab | grep assets.txt
+```
diff --git a/examples/bundle/WORKSPACE b/examples/bundle/WORKSPACE
new file mode 100644
index 000000000..73a42c522
--- /dev/null
+++ b/examples/bundle/WORKSPACE
@@ -0,0 +1,73 @@
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
+
+local_repository(
+ name = "rules_android",
+ path = "../..", # rules_android's WORKSPACE relative to this inner workspace
+)
+
+# rules_license
+http_archive(
+ name = "rules_license",
+ urls = [
+ "https://github.com/bazelbuild/rules_license/releases/download/1.0.0/rules_license-1.0.0.tar.gz",
+ ],
+ sha256 = "26d4021f6898e23b82ef953078389dd49ac2b5618ac564ade4ef87cced147b38",
+)
+
+# rules_jvm_external
+RULES_JVM_EXTERNAL_TAG = "6.5"
+RULES_JVM_EXTERNAL_SHA = "3a4d56357851cf5b0dae538b3f3e0612a4f58925dfb3cadb2e0c4e87d51e629e"
+
+http_archive(
+ name = "rules_jvm_external",
+ strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
+ sha256 = RULES_JVM_EXTERNAL_SHA,
+ url = "https://github.com/bazelbuild/rules_jvm_external/releases/download/%s/rules_jvm_external-%s.tar.gz" % (RULES_JVM_EXTERNAL_TAG, RULES_JVM_EXTERNAL_TAG)
+)
+
+# bazel_features
+BAZEL_FEATURES_VERSION = "1.20.0"
+BAZEL_FEATURES_HASH = "c2596994cf63513bd44180411a4ac3ae95d32bf59148fcb6087a4642b3ffef11"
+maybe(
+ http_archive,
+ name = "bazel_features",
+ sha256 = BAZEL_FEATURES_HASH,
+ strip_prefix = "bazel_features-" + BAZEL_FEATURES_VERSION,
+ url = "https://github.com/bazel-contrib/bazel_features/releases/download/v" + BAZEL_FEATURES_VERSION + "/bazel_features-v" + BAZEL_FEATURES_VERSION + ".tar.gz",
+)
+maybe(
+ http_archive,
+ name = "proto_bazel_features",
+ sha256 = BAZEL_FEATURES_HASH,
+ strip_prefix = "bazel_features-" + BAZEL_FEATURES_VERSION,
+ url = "https://github.com/bazel-contrib/bazel_features/releases/download/v" + BAZEL_FEATURES_VERSION + "/bazel_features-v" + BAZEL_FEATURES_VERSION + ".tar.gz",
+)
+load("@bazel_features//:deps.bzl", "bazel_features_deps")
+bazel_features_deps()
+
+# Skylib
+http_archive(
+ name = "bazel_skylib",
+ sha256 = "bc283cdfcd526a52c3201279cda4bc298652efa898b10b4db0837dc51652756f",
+ urls = [
+ "https://github.com/bazelbuild/bazel-skylib/releases/download/1.7.1/bazel-skylib-1.7.1.tar.gz",
+ ],
+)
+load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
+bazel_skylib_workspace()
+
+load("@rules_android//:prereqs.bzl", "rules_android_prereqs")
+rules_android_prereqs()
+load("@rules_android//:defs.bzl", "rules_android_workspace")
+rules_android_workspace()
+
+load("@rules_android//rules:rules.bzl", "android_sdk_repository")
+android_sdk_repository(
+ name = "androidsdk",
+)
+
+register_toolchains(
+ "@rules_android//toolchains/android:android_default_toolchain",
+ "@rules_android//toolchains/android_sdk:android_sdk_tools",
+)
diff --git a/examples/bundle/WORKSPACE.bzlmod b/examples/bundle/WORKSPACE.bzlmod
new file mode 100644
index 000000000..df9ed0ec9
--- /dev/null
+++ b/examples/bundle/WORKSPACE.bzlmod
@@ -0,0 +1 @@
+workspace(name = "bundle")
diff --git a/examples/bundle/app/AndroidManifest.xml b/examples/bundle/app/AndroidManifest.xml
new file mode 100644
index 000000000..1a4a7a99d
--- /dev/null
+++ b/examples/bundle/app/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/bundle/app/BUILD b/examples/bundle/app/BUILD
new file mode 100644
index 000000000..029acad09
--- /dev/null
+++ b/examples/bundle/app/BUILD
@@ -0,0 +1,19 @@
+load("@rules_android//android:rules.bzl", "android_application", "android_library")
+
+android_application(
+ name = "assets",
+ manifest_values = {
+ "applicationId" : "com.examples.bundle.app",
+ "versionCode": "0",
+ },
+ feature_modules = ["//features/assets:feature_module"],
+ manifest = "AndroidManifest.xml",
+ deps = [":lib"],
+)
+
+android_library(
+ name = "lib",
+ srcs = ["BasicActivity.java"],
+ manifest = "AndroidManifest.xml",
+ resource_files = glob(["res/**"]),
+)
diff --git a/examples/bundle/app/BasicActivity.java b/examples/bundle/app/BasicActivity.java
new file mode 100644
index 000000000..7baf701e2
--- /dev/null
+++ b/examples/bundle/app/BasicActivity.java
@@ -0,0 +1,52 @@
+// 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.examples.bundle.app;
+
+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.
+ */
+public class BasicActivity extends Activity {
+
+ @Override
+ 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 ");
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/examples/bundle/app/res/drawable-hdpi/ic_launcher.png b/examples/bundle/app/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..6ab2adde2
Binary files /dev/null and b/examples/bundle/app/res/drawable-hdpi/ic_launcher.png differ
diff --git a/examples/bundle/app/res/layout/basic_activity.xml b/examples/bundle/app/res/layout/basic_activity.xml
new file mode 100644
index 000000000..f84199cb5
--- /dev/null
+++ b/examples/bundle/app/res/layout/basic_activity.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
diff --git a/examples/bundle/app/res/values/strings.xml b/examples/bundle/app/res/values/strings.xml
new file mode 100644
index 000000000..451d3739f
--- /dev/null
+++ b/examples/bundle/app/res/values/strings.xml
@@ -0,0 +1,8 @@
+
+
+
+ basicbundle
+ Hello world!
+ Settings
+
+
diff --git a/examples/bundle/features/assets/BUILD b/examples/bundle/features/assets/BUILD
new file mode 100644
index 000000000..72e269726
--- /dev/null
+++ b/examples/bundle/features/assets/BUILD
@@ -0,0 +1,17 @@
+load("@rules_android//rules:rules.bzl", "android_library", "android_feature_module")
+
+android_library(
+ name = "lib",
+ manifest = "src/AndroidManifest.xml",
+ assets = ["src/assets.txt"],
+)
+
+android_feature_module(
+ name = "feature_module",
+ custom_package = "com.example.bundle.features.assets",
+ manifest = "src/AndroidManifest.xml",
+ title = "asset_feature",
+ library = ":lib",
+ feature_name = "asset_feature",
+ visibility = ["//visibility:public"],
+)
diff --git a/examples/bundle/features/assets/src/AndroidManifest.xml b/examples/bundle/features/assets/src/AndroidManifest.xml
new file mode 100644
index 000000000..c9556abd1
--- /dev/null
+++ b/examples/bundle/features/assets/src/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
diff --git a/examples/bundle/features/assets/src/assets.txt b/examples/bundle/features/assets/src/assets.txt
new file mode 100644
index 000000000..08c4a31e7
--- /dev/null
+++ b/examples/bundle/features/assets/src/assets.txt
@@ -0,0 +1 @@
+This text originates from a dynamically loaded feature.
\ No newline at end of file
diff --git a/rules/android_application/BUILD b/rules/android_application/BUILD
index bca820377..ddf3f930a 100644
--- a/rules/android_application/BUILD
+++ b/rules/android_application/BUILD
@@ -41,10 +41,3 @@ py_binary(
"@py_absl//absl/flags",
],
)
-
-filegroup(
- name = "merge_feature_manifests.par",
- srcs = [":merge_feature_manifests"],
- output_group = "python_zip_file",
- visibility = ["//visibility:public"],
-)
diff --git a/rules/android_application/android_feature_module_rule.bzl b/rules/android_application/android_feature_module_rule.bzl
index 4ee0a32e2..c55242fe9 100644
--- a/rules/android_application/android_feature_module_rule.bzl
+++ b/rules/android_application/android_feature_module_rule.bzl
@@ -94,10 +94,10 @@ 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),
+ binary = native.package_relative_label("%s_bin" % fqn),
+ manifest_lib = native.package_relative_label("%s_AndroidManifest" % fqn),
+ title_strings_xml = native.package_relative_label("%s_title_strings_xml" % fqn),
+ title_lib = native.package_relative_label("%s_title_lib" % fqn),
)
def android_feature_module_macro(_android_binary, _android_library, **attrs):
diff --git a/rules/android_application/attrs.bzl b/rules/android_application/attrs.bzl
index 5cd7382f6..a37eba39a 100644
--- a/rules/android_application/attrs.bzl
+++ b/rules/android_application/attrs.bzl
@@ -73,7 +73,7 @@ ANDROID_APPLICATION_ATTRS = _attrs.add(
),
_bundle_keystore_properties = attr.label(
allow_single_file = True,
- default = "//rules:bundle_keystore_properties.tmpl",
+ default = None,
),
_feature_manifest_script = attr.label(
allow_single_file = True,
@@ -85,8 +85,7 @@ ANDROID_APPLICATION_ATTRS = _attrs.add(
default = Label("//tools/jdk:toolchain_android_only"),
),
_merge_manifests = attr.label(
- default = ":merge_feature_manifests.par",
- allow_single_file = True,
+ default = ":merge_feature_manifests",
cfg = "exec",
executable = True,
),
@@ -120,6 +119,7 @@ ANDROID_FEATURE_MODULE_ATTRS = dict(
manifest = attr.label(allow_single_file = True),
title_id = attr.string(),
title_lib = attr.string(),
+ fused = attr.bool(),
_feature_module_validation_script = attr.label(
allow_single_file = True,
cfg = "exec",
diff --git a/rules/android_application/feature_module_validation.sh b/rules/android_application/feature_module_validation.sh
old mode 100644
new mode 100755
diff --git a/rules/android_application/gen_priority_android_feature_manifest.sh b/rules/android_application/gen_priority_android_feature_manifest.sh
old mode 100644
new mode 100755
diff --git a/rules/rules.bzl b/rules/rules.bzl
index 4e225d008..79c8bd1f8 100644
--- a/rules/rules.bzl
+++ b/rules/rules.bzl
@@ -53,6 +53,10 @@ load(
"//rules/android_library:rule.bzl",
_android_library = "android_library_macro",
)
+load(
+ "//rules/android_application:android_feature_module.bzl",
+ _android_feature_module = "android_feature_module",
+)
load(
"//rules/android_local_test:rule.bzl",
_android_local_test = "android_local_test",
@@ -81,6 +85,7 @@ aar_import = _aar_import
android_application = _android_application
android_binary = _android_binary
android_library = _android_library
+android_feature_module = _android_feature_module
android_local_test = _android_local_test
android_ndk_repository = _android_ndk_repository
android_sandboxed_sdk = _android_sandboxed_sdk
diff --git a/toolchains/android/BUILD b/toolchains/android/BUILD
index 941fbe17f..68e805a2f 100644
--- a/toolchains/android/BUILD
+++ b/toolchains/android/BUILD
@@ -56,3 +56,19 @@ sh_binary(
srcs = [":unzip.sh"],
visibility = ["//visibility:public"],
)
+
+genrule(
+ name = "gen_xmllint",
+ outs = ["xmllint.sh"],
+ cmd = """cat > $@ <