From df2d9d2c2410b793847d01e7703f35f3b25a3325 Mon Sep 17 00:00:00 2001 From: Olivier Notteghem Date: Sat, 7 Feb 2026 12:38:00 -0800 Subject: [PATCH 1/3] [Uber] Add fat_aar rule for bundling Android libraries into a distributable SDK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces a new fat_aar rule that consolidates multiple android_library targets and their transitive dependencies into a single self-contained AAR file, complete with a companion fat_aar_pom rule for Maven POM generation. Motivation At Uber, shipping SDK artifacts to external partners (e.g., logistics, delivery, mapping capabilities distributed to third-party apps) requires packaging internal Android modules as a single distributable unit. Building those SDKs directly from the Bazel monorepo avoids the maintenance cost of maintaining a separate SDK project: the SDK AAR is produced as a build output of the same android_library graph that ships inside Uber's own apps, keeping the SDK and the app in lock-step with no extra integration layer. Dependency graph traversal via aspect A fat_aar_aspect walks the full transitive dependency graph and collects Android providers — resources (AndroidResourcesInfo / StarlarkAndroidResourcesInfo), assets (AndroidAssetsInfo), native libraries (AndroidNativeLibsInfo), manifests, and ProGuard specs (ProguardSpecInfo) — as (label, provider) tuples. Tracking labels alongside providers is what enables the filtering step that separates Uber's internal modules from external Maven dependencies. Selective bundling via exclude patterns The exclude attribute accepts label prefix patterns (e.g., "@maven//", "@@com_github_jetbrains_kotlin"). Any dependency whose label or file path matches a pattern is omitted from the bundled AAR. External dependencies excluded this way are tracked in a FatAarDependenciesInfo provider and written to an excluded_deps output group, which fat_aar_pom consumes to generate correct entries in the POM. Partners are expected to provide those external dependencies themselves via the generated POM. Classes.jar All transitive runtime JARs from included android_library targets are merged into a single classes.jar using singlejar with --normalize for deterministic output. External Maven JARs are excluded from the merge via the same label-pattern filtering applied to resources. Resources and manifests Transitive resource files, R.txt files, and assets are collected from all included android_library providers, filtered by the exclude patterns, and passed to the AAPT2 resource bundler. Multiple AndroidManifest.xml files are merged using the Android manifest merger tool (APPLICATION merge type, dependency order) with a synthetic primary manifest pinning the minimum SDK version. Native libraries Native .so files from AndroidNativeLibsInfo are extracted from ZIP archives and converted from lib/ARCH/*.so layout (Bazel convention) to jni/ARCH/*.so layout (AAR spec) via add_native_libs.sh before being inserted into the final AAR. ProGuard rules Transitive ProGuard specs are merged and embedded in the AAR's proguard.txt, ensuring consumers applying R8 or ProGuard at the app level receive the correct keep rules for the bundled SDK. Optional R8 optimization An optional r8_config attribute accepts a .pro file. When provided, R8 runs in --classfile mode (outputting .class files rather than .dex, as required for AAR distribution) with --release optimization, using the user-provided config combined with all transitive ProGuard specs. This enables shrinking and obfuscation of the SDK surface before distribution. POM generation fat_aar_pom maps the excluded Bazel labels back to their Maven coordinates (group:artifact:version, with proper type handling for both 3-part and 4-part coordinate formats) and generates a well-formed POM file suitable for publishing the AAR to Maven/Artifactory. Special internal Bazel targets (jarinfer_, proguard_ prefixes) are filtered out during POM generation. In the Uber Android monorepo, these rules are wrapped by the uber_fat_aar macro in android/defs.bzl, which pairs the fat AAR with an aar_import for internal consumption and a publish target for Artifactory delivery. --- README.md | 18 + examples/fat_aar/AndroidManifest.xml | 4 + examples/fat_aar/BUILD | 44 ++ examples/fat_aar/Lib1.java | 7 + examples/fat_aar/Lib2.java | 9 + examples/fat_aar/README.md | 60 +++ examples/fat_aar/assets/sample_asset.txt | 1 + examples/fat_aar/jni/arm64-v8a/libnative.so | 1 + examples/fat_aar/res/layout/lib_layout.xml | 12 + examples/fat_aar/res/values/strings.xml | 5 + rules/fat_aar/BUILD | 18 + rules/fat_aar/README.md | 207 +++++++++ rules/fat_aar/add_native_libs.sh | 43 ++ rules/fat_aar/aspect.bzl | 112 +++++ rules/fat_aar/pom_from_fat_aar.bzl | 179 ++++++++ rules/fat_aar/rule.bzl | 458 ++++++++++++++++++++ rules/rules.bzl | 10 + 17 files changed, 1188 insertions(+) create mode 100644 examples/fat_aar/AndroidManifest.xml create mode 100644 examples/fat_aar/BUILD create mode 100644 examples/fat_aar/Lib1.java create mode 100644 examples/fat_aar/Lib2.java create mode 100644 examples/fat_aar/README.md create mode 100644 examples/fat_aar/assets/sample_asset.txt create mode 100644 examples/fat_aar/jni/arm64-v8a/libnative.so create mode 100644 examples/fat_aar/res/layout/lib_layout.xml create mode 100644 examples/fat_aar/res/values/strings.xml create mode 100644 rules/fat_aar/BUILD create mode 100644 rules/fat_aar/README.md create mode 100644 rules/fat_aar/add_native_libs.sh create mode 100644 rules/fat_aar/aspect.bzl create mode 100644 rules/fat_aar/pom_from_fat_aar.bzl create mode 100644 rules/fat_aar/rule.bzl diff --git a/README.md b/README.md index 6831ecbd3..845f9f54f 100644 --- a/README.md +++ b/README.md @@ -108,3 +108,21 @@ android_library( ... ) ``` + +## Available Rules + +- **`android_binary`** - Builds an Android APK +- **`android_library`** - Builds an Android library +- **`android_application`** - Builds an Android application bundle (AAB) +- **`fat_aar`** - Bundles multiple Android libraries into a single AAR file (see [rules/fat_aar](rules/fat_aar/)) +- **`fat_aar_pom`** - Generates a Maven POM from a fat_aar's excluded dependencies + +For a complete list of rules, see the [rules.bzl](rules/rules.bzl) file. + +## Examples + +See the [examples](examples/) directory for sample projects: + +- **[basicapp](examples/basicapp/)** - Basic Android application +- **[bundle](examples/bundle/)** - Android application bundle (AAB) +- **[fat_aar](examples/fat_aar/)** - Fat AAR with resources, assets, and native libraries diff --git a/examples/fat_aar/AndroidManifest.xml b/examples/fat_aar/AndroidManifest.xml new file mode 100644 index 000000000..fe8cfeb89 --- /dev/null +++ b/examples/fat_aar/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + diff --git a/examples/fat_aar/BUILD b/examples/fat_aar/BUILD new file mode 100644 index 000000000..ceca40246 --- /dev/null +++ b/examples/fat_aar/BUILD @@ -0,0 +1,44 @@ +load("//rules/android_library:rule.bzl", "android_library") +load("//rules/fat_aar:rule.bzl", "fat_aar") + +# Library with resources and assets +android_library( + name = "lib1", + srcs = ["Lib1.java"], + manifest = "AndroidManifest.xml", + resource_files = glob(["res/**"]), + assets = glob(["assets/**"]), + assets_dir = "assets", +) + +# Library depending on lib1 (transitive dependency test) +android_library( + name = "lib2", + srcs = ["Lib2.java"], + manifest = "AndroidManifest.xml", + deps = [":lib1"], +) + +# Native library +cc_library( + name = "native_lib", + srcs = ["jni/arm64-v8a/libnative.so"], +) + +# Fat AAR bundling all transitive dependencies +fat_aar( + name = "bundled", + min_sdk_version = "21", + deps = [ + ":lib2", + ":native_lib", + ], +) + +# Fat AAR with exclusions (e.g., exclude external dependencies) +fat_aar( + name = "bundled_filtered", + min_sdk_version = "21", + exclude = ["@maven//"], + deps = [":lib2"], +) diff --git a/examples/fat_aar/Lib1.java b/examples/fat_aar/Lib1.java new file mode 100644 index 000000000..42a3d73b3 --- /dev/null +++ b/examples/fat_aar/Lib1.java @@ -0,0 +1,7 @@ +package com.example.fat_aar; + +public class Lib1 { + public String getMessage() { + return "Hello from Lib1"; + } +} diff --git a/examples/fat_aar/Lib2.java b/examples/fat_aar/Lib2.java new file mode 100644 index 000000000..b06f3134b --- /dev/null +++ b/examples/fat_aar/Lib2.java @@ -0,0 +1,9 @@ +package com.example.fat_aar; + +public class Lib2 { + private Lib1 lib1 = new Lib1(); + + public String getCombinedMessage() { + return "Lib2 says: " + lib1.getMessage(); + } +} diff --git a/examples/fat_aar/README.md b/examples/fat_aar/README.md new file mode 100644 index 000000000..10be480e0 --- /dev/null +++ b/examples/fat_aar/README.md @@ -0,0 +1,60 @@ +# Fat AAR Example + +This example demonstrates how to use the `fat_aar` rule to bundle multiple Android libraries and their transitive dependencies into a single AAR file. + +## Structure + +- **lib1**: Android library with resources, layouts, and assets +- **lib2**: Android library that depends on lib1 (demonstrates transitive bundling) +- **native_lib**: Native library (demonstrates native library bundling) + +## Targets + +### `bundled` +A fat AAR that bundles all transitive dependencies including: +- Java/Kotlin classes from lib1 and lib2 +- Resources from all libraries +- Assets from all libraries +- Native libraries +- Merged AndroidManifest.xml + +```bash +bazel build //examples/fat_aar:bundled +``` + +### `bundled_filtered` +A fat AAR with exclusions - demonstrates filtering out external dependencies: +```bash +bazel build //examples/fat_aar:bundled_filtered +``` + +## What Gets Bundled + +The `fat_aar` rule automatically collects and bundles: + +1. **Classes (classes.jar)**: All Java/Kotlin bytecode from transitive dependencies +2. **Resources (res/)**: All resources from transitive android_library targets +3. **Assets (assets/)**: All assets from transitive dependencies +4. **Native Libraries (jni/)**: Native .so files for all architectures +5. **AndroidManifest.xml**: Merged manifest from all libraries +6. **R.txt**: Combined R.txt for resource IDs +7. **proguard.txt**: Merged ProGuard rules + +## Excluding Dependencies + +Use the `exclude` attribute to filter out dependencies (e.g., external Maven dependencies): + +```python +fat_aar( + name = "my_aar", + exclude = ["@maven//"], # Exclude all Maven dependencies + deps = [":my_lib"], +) +``` + +## Output + +The fat AAR is a standard Android AAR file that can be: +- Published to Maven/Artifactory +- Consumed by other Android projects (Gradle or Bazel) +- Distributed as a reusable component diff --git a/examples/fat_aar/assets/sample_asset.txt b/examples/fat_aar/assets/sample_asset.txt new file mode 100644 index 000000000..ebe542907 --- /dev/null +++ b/examples/fat_aar/assets/sample_asset.txt @@ -0,0 +1 @@ +This is a sample asset file to test asset bundling in fat_aar. diff --git a/examples/fat_aar/jni/arm64-v8a/libnative.so b/examples/fat_aar/jni/arm64-v8a/libnative.so new file mode 100644 index 000000000..9bd98f285 --- /dev/null +++ b/examples/fat_aar/jni/arm64-v8a/libnative.so @@ -0,0 +1 @@ +FAKE_NATIVE_LIB_FOR_TESTING \ No newline at end of file diff --git a/examples/fat_aar/res/layout/lib_layout.xml b/examples/fat_aar/res/layout/lib_layout.xml new file mode 100644 index 000000000..d3b7dfc5b --- /dev/null +++ b/examples/fat_aar/res/layout/lib_layout.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/examples/fat_aar/res/values/strings.xml b/examples/fat_aar/res/values/strings.xml new file mode 100644 index 000000000..ccae96da0 --- /dev/null +++ b/examples/fat_aar/res/values/strings.xml @@ -0,0 +1,5 @@ + + + Fat AAR Example + Hello from resources + diff --git a/rules/fat_aar/BUILD b/rules/fat_aar/BUILD new file mode 100644 index 000000000..5542fed8b --- /dev/null +++ b/rules/fat_aar/BUILD @@ -0,0 +1,18 @@ +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") + +package(default_visibility = ["//visibility:public"]) + +exports_files([ + "add_native_libs.sh", + "rule.bzl", + "aspect.bzl", +]) + +bzl_library( + name = "bzl", + srcs = glob(["*.bzl"]), + visibility = ["//visibility:public"], + deps = [ + "//rules:bzl", + ], +) diff --git a/rules/fat_aar/README.md b/rules/fat_aar/README.md new file mode 100644 index 000000000..aec12d4c9 --- /dev/null +++ b/rules/fat_aar/README.md @@ -0,0 +1,207 @@ +# Fat AAR Rules + +This directory contains the implementation of the `fat_aar` rule and supporting utilities for bundling multiple Android libraries into a single AAR file. + +## Overview + +The `fat_aar` rule consolidates multiple `android_library` targets and their transitive dependencies into a unified AAR package. This is useful for: + +- **Simplified distribution**: Publish a single AAR instead of managing multiple library dependencies +- **Dependency encapsulation**: Hide internal module structure from external consumers +- **Selective bundling**: Exclude external dependencies (e.g., Maven artifacts) while tracking them for POM generation + +## Rules + +### `fat_aar` + +Bundles transitive `android_library` dependencies into a single AAR file. + +**Defined in**: `rule.bzl` + +**Key features**: +- Collects all transitive dependencies using aspects +- Bundles Java/Kotlin classes, resources, assets, manifests, native libraries, and ProGuard rules +- Filters dependencies based on exclusion patterns +- Generates excluded dependencies file as output group + +**Examples**: + +Basic usage: +```python +fat_aar( + name = "my_fat_aar", + deps = [":my_library"], + exclude = ["@maven//", "@@maven//", "@@com_github_jetbrains_kotlin"], + min_sdk_version = "23", +) +``` + +With R8 optimization: +```python +fat_aar( + name = "my_fat_aar_optimized", + deps = [":my_library"], + exclude = ["@maven//", "@@maven//", "@@com_github_jetbrains_kotlin"], + r8_config = "proguard.pro", + min_sdk_version = "23", +) +``` + +**Attributes**: +- `deps` (required): List of `android_library` targets to bundle. All transitive dependencies will be included unless excluded via the `exclude` attribute +- `exclude` (optional, default: `[]`): List of label patterns to exclude from bundling. Commonly used to exclude external dependencies that consumers are expected to provide (e.g., `["@maven//", "@@maven//"]` to exclude all Maven dependencies, `"@@com_github_jetbrains_kotlin"` to exclude Kotlin stdlib) +- `min_sdk_version` (optional, default: `"23"`): Minimum SDK version for the primary manifest. This is used when merging multiple AndroidManifest.xml files +- `r8_config` (optional): ProGuard configuration file (`.pro` or `.txt`) for R8 optimization. If provided, R8 will optimize and shrink the bundled code. The configuration is combined with transitive ProGuard specs from all dependencies + +**Outputs**: +- Default output: The bundled AAR file containing merged classes, resources, assets, manifests, native libraries, and ProGuard rules +- Output group `excluded_deps`: Text file listing all dependencies that were excluded (useful for POM generation) +- Output group `manifest`: The merged AndroidManifest.xml file +- Output group `class_jar`: The classes.jar file (optionally R8-optimized if `r8_config` is provided) + +### `fat_aar_pom` + +Generates a Maven POM file from a fat_aar's excluded dependencies. + +**Defined in**: `pom_from_fat_aar.bzl` + +**Key features**: +- Looks up excluded Bazel labels in Maven coordinates list +- Generates POM with proper dependency declarations +- Supports both 3-part and 4-part Maven coordinates + +**Example**: +```python +fat_aar_pom( + name = "my_pom", + fat_aar = ":my_fat_aar", + maven_coords = MAVEN_ARTIFACTS, + group_id = "com.example", + artifact_id = "my-library", + version = "1.0.0", +) +``` + +**Attributes**: +- `fat_aar`: The `fat_aar` target to generate POM for +- `maven_coords`: List of all Maven coordinates (format: `"group:artifact:type:version"`) +- `group_id`: Maven group ID +- `artifact_id`: Maven artifact ID +- `version`: Maven version + +## Supporting Files + +### `aspect.bzl` + +Defines the `fat_aar_aspect` and providers: + +- **`FatAarInfo`**: Collects Android providers (resources, assets, manifests, native libs, ProGuard) as `(label, provider)` tuples for filtering +- **`FatAarDependenciesInfo`**: Tracks labels of excluded dependencies for POM generation + +The aspect traverses the dependency graph and collects all Android-related providers from transitive dependencies. + +### `add_native_libs.sh` + +Shell script that adds native libraries to the AAR in the correct format. + +**Why it's needed**: +- Android native libraries are distributed in ZIP files with `lib/ARCH/*.so` structure +- The AAR format requires `jni/ARCH/*.so` structure +- This script extracts native libs from ZIP files and converts them to AAR format + +**Usage**: Called automatically by the `fat_aar` rule implementation. + +## R8 Integration + +When `r8_config` is provided, R8 runs with the following behavior: + +- **Output format**: `.class` files (not `.dex`) using the `--classfile` flag +- **Library classpath**: Android SDK jar is provided via `--lib` +- **ProGuard configs**: Combines the user-provided `r8_config` with all transitive ProGuard specifications from dependencies +- **Optimization mode**: `--release` mode for production optimization +- **API level**: Note that `--min-api` is not supported when using `--classfile` mode + +### R8 Configuration Best Practices + +Create a ProGuard configuration file (e.g., `proguard.pro`): + +```proguard +# Keep all public API +-keep public class * { public *; } + +# Keep attributes for debugging +-keepattributes Exceptions,InnerClasses,Signature,SourceFile,LineNumberTable,EnclosingMethod + +# Don't obfuscate (optional - use if you want readable class names) +-dontobfuscate + +# Allow R8 to optimize +-allowaccessmodification + +# Don't warn about excluded dependencies +-dontwarn kotlin.** +-dontwarn org.jetbrains.annotations.** +-dontwarn java.lang.invoke.LambdaMetafactory +``` + +**Key points**: +1. **Keep public API**: Use `-keep public class * { public *; }` to preserve your SDK's public interface +2. **Exclude warnings**: Add `-dontwarn` rules for dependencies you've excluded (e.g., Kotlin stdlib) +3. **Keep attributes**: Include `EnclosingMethod` attribute when keeping `InnerClasses` +4. **Don't obfuscate (optional)**: Use `-dontobfuscate` if you want readable class names in your SDK +5. **Allow optimization**: Use `-allowaccessmodification` to let R8 optimize more aggressively + +## How It Works + +1. **Dependency Collection**: + - The `fat_aar_aspect` traverses the dependency graph + - Collects Android providers as `(label, provider)` tuples + - Allows filtering based on label patterns + +2. **Filtering**: + - Labels and files are checked against `exclude` patterns + - Excluded labels are tracked in `FatAarDependenciesInfo` provider + - Excluded dependencies list is generated as output group + +3. **Bundling**: + - Resources are merged from all included libraries + - Manifests are merged using Android's manifest merger tool + - Classes are combined into a single `classes.jar` + - **R8 Optimization** (optional): If `r8_config` is provided, R8 optimizes and shrinks the merged classes + - Native libraries are converted to AAR format + - R.txt and ProGuard rules are merged + +4. **POM Generation**: + - Excluded labels are looked up in Maven coordinates list + - Matching dependencies are added to POM + - Generated POM can be used for Maven publishing + +## Integration with Uber Repository + +In the Uber Android monorepo, these rules are wrapped by the `uber_fat_aar` macro in `android/defs.bzl`, which: + +- Creates the fat AAR with standard exclusions +- Wraps it with `aar_import` for consumption +- Generates POM file automatically +- Creates publish target for Maven/Artifactory + +See `android/experimental/sample/simple/app_fat_aar/` for a complete example. + +## File Structure + +``` +rules/fat_aar/ +├── README.md # This file +├── BUILD # Exports scripts and bzl files +├── rule.bzl # fat_aar rule implementation +├── aspect.bzl # Aspect and providers +├── pom_from_fat_aar.bzl # fat_aar_pom rule +└── add_native_libs.sh # Native library conversion script +``` + +## See Also + +- **Example**: `bazel_rules_android_legacy/examples/fat_aar/` +- **Uber integration**: `android/defs.bzl` (`uber_fat_aar` macro) +- **Publishing**: `android/rules/publish_aar/aar_publish.bzl` +- **Usage example**: `android/experimental/sample/simple/app_fat_aar/` diff --git a/rules/fat_aar/add_native_libs.sh b/rules/fat_aar/add_native_libs.sh new file mode 100644 index 000000000..4678bc934 --- /dev/null +++ b/rules/fat_aar/add_native_libs.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# Adds native libraries to AAR in the correct jni/ARCH/*.so format. +# +# Why this script is needed: +# - Android native libraries are distributed in ZIP files with lib/ARCH/*.so structure +# (e.g., lib/arm64-v8a/libnative.so, lib/armeabi-v7a/libnative.so) +# - The AAR format requires native libraries in jni/ARCH/*.so structure +# (e.g., jni/arm64-v8a/libnative.so, jni/armeabi-v7a/libnative.so) +# - This script extracts native libs from ZIP files and converts them to the correct format +# +# The conversion is necessary for aar_import to properly recognize and use the native +# libraries when the fat AAR is consumed by other projects. +# +# Usage: add_native_libs.sh BASE_AAR FINAL_AAR TEMP_DIR NATIVE_ZIP1 [NATIVE_ZIP2 ...] + +set -e + +BASE_AAR="$(cd "$(dirname "$1")"; pwd)/$(basename "$1")" +FINAL_AAR="$(cd "$(dirname "$2")"; pwd)/$(basename "$2")" +TEMP_DIR="$3" +shift 3 + +ORIG_DIR="$(pwd)" + +mkdir -p "$TEMP_DIR" +unzip -q "$BASE_AAR" -d "$TEMP_DIR" + +cd "$TEMP_DIR" +mkdir -p jni + +for native_zip in "$@"; do + if [ -f "$ORIG_DIR/$native_zip" ] && [ -s "$ORIG_DIR/$native_zip" ]; then + TEMP_EXTRACT=$(mktemp -d) + unzip -q -o "$ORIG_DIR/$native_zip" -d "$TEMP_EXTRACT" 2>/dev/null || true + # Convert lib/ARCH/*.so to jni/ARCH/*.so + if [ -d "$TEMP_EXTRACT/lib" ]; then + cp -r "$TEMP_EXTRACT/lib/"* jni/ 2>/dev/null || true + fi + rm -rf "$TEMP_EXTRACT" + fi +done + +zip -q -r "$FINAL_AAR" . diff --git a/rules/fat_aar/aspect.bzl b/rules/fat_aar/aspect.bzl new file mode 100644 index 000000000..203c6c097 --- /dev/null +++ b/rules/fat_aar/aspect.bzl @@ -0,0 +1,112 @@ +# 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. + +"""Aspect for collecting transitive Android providers.""" + +load("@rules_java//java/common:proguard_spec_info.bzl", "ProguardSpecInfo") +load("//rules:providers.bzl", "AndroidAssetsInfo", "AndroidNativeLibsInfo", "AndroidResourcesInfo", "StarlarkAndroidResourcesInfo") +load("//rules:visibility.bzl", "PROJECT_VISIBILITY") + +visibility(PROJECT_VISIBILITY) + +FatAarInfo = provider( + "Collects Android providers from transitive dependencies", + fields = { + "resource_infos": "Depset of (label, AndroidResourcesInfo) tuples", + "assets_infos": "Depset of (label, AndroidAssetsInfo) tuples", + "native_libs_infos": "Depset of (label, AndroidNativeLibsInfo) tuples", + "manifest_infos": "Depset of (label, manifest_file) tuples", + "proguard_infos": "Depset of (label, ProguardSpecInfo) tuples", + }, +) + +FatAarDependenciesInfo = provider( + "Tracks dependencies that were excluded during fat_aar bundling", + fields = { + "excluded_labels": "Depset of labels that were excluded", + }, +) + +def _fat_aar_aspect_impl(target, ctx): + """Collects Android providers transitively. + + Args: + target: The target being visited + ctx: The aspect context + + Returns: + List containing FatAarInfo provider + """ + resource_infos = [] + assets_infos = [] + native_libs_infos = [] + manifest_infos = [] + proguard_infos = [] + + # Collect providers with their source label + label = ctx.label + + # Collect both AndroidResourcesInfo and StarlarkAndroidResourcesInfo + if AndroidResourcesInfo != None and AndroidResourcesInfo in target: + resource_infos.append((label, target[AndroidResourcesInfo])) + if StarlarkAndroidResourcesInfo in target: + resource_infos.append((label, target[StarlarkAndroidResourcesInfo])) + if AndroidAssetsInfo != None and AndroidAssetsInfo in target: + assets_infos.append((label, target[AndroidAssetsInfo])) + if AndroidNativeLibsInfo in target: + native_libs_infos.append((label, target[AndroidNativeLibsInfo])) + if ProguardSpecInfo in target: + proguard_infos.append((label, target[ProguardSpecInfo])) + + # Collect manifest if available + if hasattr(ctx.rule.attr, "manifest") and ctx.rule.attr.manifest: + if hasattr(ctx.rule.attr.manifest, "files"): + for f in ctx.rule.attr.manifest.files.to_list(): + manifest_infos.append((label, f)) + + transitive_resource_infos = [] + transitive_assets_infos = [] + transitive_native_libs_infos = [] + transitive_manifest_infos = [] + transitive_proguard_infos = [] + + # Collect from deps and exports attributes (declared in attr_aspects) + for dep in ctx.rule.attr.deps: + transitive_resource_infos.append(dep[FatAarInfo].resource_infos) + transitive_assets_infos.append(dep[FatAarInfo].assets_infos) + transitive_native_libs_infos.append(dep[FatAarInfo].native_libs_infos) + transitive_manifest_infos.append(dep[FatAarInfo].manifest_infos) + transitive_proguard_infos.append(dep[FatAarInfo].proguard_infos) + + if hasattr(ctx.rule.attr, "exports"): + for dep in ctx.rule.attr.exports: + transitive_resource_infos.append(dep[FatAarInfo].resource_infos) + transitive_assets_infos.append(dep[FatAarInfo].assets_infos) + transitive_native_libs_infos.append(dep[FatAarInfo].native_libs_infos) + transitive_manifest_infos.append(dep[FatAarInfo].manifest_infos) + transitive_proguard_infos.append(dep[FatAarInfo].proguard_infos) + + return [FatAarInfo( + resource_infos = depset(resource_infos, transitive = transitive_resource_infos), + assets_infos = depset(assets_infos, transitive = transitive_assets_infos), + native_libs_infos = depset(native_libs_infos, transitive = transitive_native_libs_infos), + manifest_infos = depset(manifest_infos, transitive = transitive_manifest_infos), + proguard_infos = depset(proguard_infos, transitive = transitive_proguard_infos), + )] + +fat_aar_aspect = aspect( + implementation = _fat_aar_aspect_impl, + attr_aspects = ["deps", "exports"], + provides = [FatAarInfo], +) diff --git a/rules/fat_aar/pom_from_fat_aar.bzl b/rules/fat_aar/pom_from_fat_aar.bzl new file mode 100644 index 000000000..41151af24 --- /dev/null +++ b/rules/fat_aar/pom_from_fat_aar.bzl @@ -0,0 +1,179 @@ +# 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. + +"""POM generation for fat_aar targets. + +This rule generates a Maven POM file from a fat_aar's excluded dependencies. +""" + +load("//rules/fat_aar:aspect.bzl", "FatAarDependenciesInfo") +load("//rules:visibility.bzl", "PROJECT_VISIBILITY") + +visibility(PROJECT_VISIBILITY) + +def _pom_from_fat_aar_impl(ctx): + """Generates a Maven POM file from a fat_aar's excluded dependencies. + + Args: + ctx: The context. + + Returns: + DefaultInfo with the generated POM file. + """ + pom_file = ctx.actions.declare_file(ctx.label.name + ".pom") + + excluded_labels = ctx.attr.fat_aar[FatAarDependenciesInfo].excluded_labels.to_list() + + # Build a mapping from Bazel label to Maven coordinate + # maven_coords format: "group:artifact:type:version" + label_to_coord = {} + for coord in ctx.attr.maven_coords: + parts = coord.split(":") + if len(parts) >= 2: + group = parts[0] + artifact = parts[1] + # Convert to Bazel label format + label_name = "{}_{}".format(group, artifact).replace(".", "_").replace("-", "_") + maven_label = "@maven//:{}".format(label_name) + label_to_coord[maven_label] = coord + + # Match excluded labels to Maven coordinates + matched_coords = {} + for label in excluded_labels: + label_str = str(label) + # Handle both @maven// and @@maven// formats + if "@maven//" not in label_str: + continue + + # Extract the target name after the last ':' + if ":" not in label_str: + continue + target = label_str.split(":")[-1] + + # Skip special targets + if target.startswith("jarinfer_") or target.startswith("proguard_") or target.startswith("v1"): + continue + + # Try to find matching coordinate + check_label = "@maven//:{}".format(target) + if check_label in label_to_coord: + coord = label_to_coord[check_label] + matched_coords[coord] = True + + # Generate POM dependencies XML + dependencies_xml = "" + for coord in sorted(matched_coords.keys()): + parts = coord.split(":") + classifier = None + if len(parts) == 3: + # Format: group:artifact:version or group:artifact:version@type + group_id = parts[0] + artifact_id = parts[1] + version_part = parts[2] + if "@" in version_part: + version, packaging = version_part.split("@", 1) + else: + version = version_part + packaging = None + elif len(parts) == 4: + # Format: group:artifact:type:version + group_id = parts[0] + artifact_id = parts[1] + packaging = parts[2] + version = parts[3] + elif len(parts) == 5: + # Format: group:artifact:type:classifier:version + group_id = parts[0] + artifact_id = parts[1] + packaging = parts[2] + classifier = parts[3] + version = parts[4] + else: + continue + + classifier_xml = "" + if classifier: + classifier_xml = " {classifier}\n".format(classifier = classifier) + + packaging_xml = "" + if packaging: + packaging_xml = " {packaging}\n".format(packaging = packaging) + + dependencies_xml += """ + {group_id} + {artifact_id} + {version} +{packaging_xml}{classifier_xml} compile + +""".format(group_id = group_id, artifact_id = artifact_id, version = version, packaging_xml = packaging_xml, classifier_xml = classifier_xml) + + pom_content = """ + + 4.0.0 + + {group_id} + {artifact_id} + {version} + aar + + +{dependencies} + +""".format( + group_id = ctx.attr.group_id, + artifact_id = ctx.attr.artifact_id, + version = ctx.attr.version, + dependencies = dependencies_xml, + ) + + ctx.actions.write( + output = pom_file, + content = pom_content, + ) + + return [ + DefaultInfo( + files = depset([pom_file]), + ), + ] + +fat_aar_pom = rule( + implementation = _pom_from_fat_aar_impl, + attrs = { + "fat_aar": attr.label( + mandatory = True, + providers = [FatAarDependenciesInfo], + doc = "The fat_aar target to generate POM for", + ), + "maven_coords": attr.string_list( + mandatory = True, + doc = "List of all Maven coordinates to match against (e.g., from maven_artifacts.bzl)", + ), + "group_id": attr.string( + mandatory = True, + doc = "Maven group ID for the POM", + ), + "artifact_id": attr.string( + mandatory = True, + doc = "Maven artifact ID for the POM", + ), + "version": attr.string( + mandatory = True, + doc = "Maven version for the POM", + ), + }, + doc = "Generates a Maven POM file from a fat_aar's excluded dependencies by looking them up in the provided maven_coords list.", +) diff --git a/rules/fat_aar/rule.bzl b/rules/fat_aar/rule.bzl new file mode 100644 index 000000000..d26f92000 --- /dev/null +++ b/rules/fat_aar/rule.bzl @@ -0,0 +1,458 @@ +# 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. + +"""fat_aar rule implementation.""" + +load("@rules_java//java/common:java_common.bzl", "java_common") +load("@rules_java//java/common:java_info.bzl", "JavaInfo") +load("@rules_java//java/common:proguard_spec_info.bzl", "ProguardSpecInfo") +load("//rules:busybox.bzl", _busybox = "busybox") +load("//rules:common.bzl", _common = "common") +load("//rules:java.bzl", "java") +load("//rules:providers.bzl", "AndroidAssetsInfo", "AndroidNativeLibsInfo", "AndroidResourcesInfo", "StarlarkAndroidResourcesInfo") +load("//rules:resources.bzl", _resources = "resources") +load("//rules:utils.bzl", "get_android_sdk", "get_android_toolchain", "utils") +load("//rules:visibility.bzl", "PROJECT_VISIBILITY") +load("//rules/fat_aar:aspect.bzl", "FatAarDependenciesInfo", "FatAarInfo", "fat_aar_aspect") + +visibility(PROJECT_VISIBILITY) + +def _fat_aar_impl(ctx): + """Bundles transitive android_library deps into a single AAR. + + When working with modular Android projects, publishing individual libraries can become + unwieldy as the number of modules grows. The fat_aar rule consolidates multiple internal + android_library targets into a unified AAR package, providing several advantages: + + - Dependency management: Consumers depend on a single AAR instead of managing multiple + library versions, reducing integration complexity + - Size optimization: Bundling libraries together can enable better code shrinking and + result in a more compact final artifact + - Encapsulation: Internal module structure remains hidden, giving library authors greater + flexibility to refactor without impacting external consumers + + Args: + ctx: The context. + + Returns: + A list of providers. + """ + + # Collect and merge JavaInfo providers + all_java_infos = utils.collect_providers(JavaInfo, ctx.attr.deps) + merged_java_info = java_common.merge(all_java_infos) + + # Helper function to check if label or file should be excluded + def should_exclude_label(label): + label_str = str(label) + for exclude_pattern in ctx.attr.exclude: + if exclude_pattern in label_str: + return True + return False + + def should_exclude_file(file): + # Check file path for external repo markers + if not hasattr(file, "path"): + return False + path = file.path + for exclude_pattern in ctx.attr.exclude: + # Fast check: external files have paths like "external/maven/..." + if exclude_pattern.startswith("@"): + # Strip all leading @ (handles both @repo and @@repo bzlmod syntax) + repo_name = exclude_pattern.lstrip("@").rstrip("/") + # WORKSPACE format: external// + if "external/" + repo_name + "/" in path: + return True + # Old bzlmod format: ~/ + if "~" + repo_name + "/" in path: + return True + # Bzlmod canonical format: ++/ (e.g. +maven_repos+maven/) + if "+" + repo_name + "/" in path: + return True + return False + + # Collect Android providers from aspect (now as (label, provider) tuples) + # Track excluded labels for POM generation + all_resource_infos = [] + all_assets_infos = [] + all_native_libs_infos = [] + all_manifest_infos = [] + all_proguard_infos = [] + excluded_labels = [] + + for dep in ctx.attr.deps: + if FatAarInfo not in dep: + continue + # Filter based on exclude patterns + for label, info in dep[FatAarInfo].resource_infos.to_list(): + if should_exclude_label(label): + excluded_labels.append(label) + else: + all_resource_infos.append(info) + for label, info in dep[FatAarInfo].assets_infos.to_list(): + if should_exclude_label(label): + excluded_labels.append(label) + else: + all_assets_infos.append(info) + for label, info in dep[FatAarInfo].native_libs_infos.to_list(): + if should_exclude_label(label): + excluded_labels.append(label) + else: + all_native_libs_infos.append(info) + for label, manifest in dep[FatAarInfo].manifest_infos.to_list(): + if should_exclude_label(label): + excluded_labels.append(label) + else: + all_manifest_infos.append(manifest) + for label, info in dep[FatAarInfo].proguard_infos.to_list(): + if should_exclude_label(label): + excluded_labels.append(label) + else: + all_proguard_infos.append(info) + + # Extract transitive data from collected providers + # Handle both AndroidResourcesInfo and StarlarkAndroidResourcesInfo + transitive_resource_files = [] + transitive_assets = [] + transitive_manifests = [] + transitive_r_txts = [] + + for info in all_resource_infos: + if type(info) == "StarlarkAndroidResourcesInfo" or hasattr(info, "transitive_resource_files"): + transitive_resource_files.append(info.transitive_resource_files) + transitive_manifests.append(info.transitive_manifests) + transitive_r_txts.append(info.transitive_r_txts) + if hasattr(info, "transitive_assets"): + transitive_assets.append(info.transitive_assets) + elif hasattr(info, "transitive_resources"): + transitive_resource_files.append(info.transitive_resources) + transitive_manifests.append(info.transitive_manifests) + transitive_r_txts.append(info.transitive_aapt2_r_txt) + + for info in all_assets_infos: + transitive_assets.append(info.assets) + + # Use depset filtering - only convert to list at the end if needed + if ctx.attr.exclude: + # Filter files lazily + all_resource_files = depset(transitive = transitive_resource_files).to_list() + resource_files = [f for f in all_resource_files if not should_exclude_file(f)] + + all_assets = depset(transitive = transitive_assets).to_list() + assets = [f for f in all_assets if not should_exclude_file(f)] + + all_manifests_from_providers = depset(transitive = transitive_manifests).to_list() + filtered_manifests = [f for f in all_manifests_from_providers if not should_exclude_file(f)] + manifests = filtered_manifests + all_manifest_infos + + all_r_txts = depset(transitive = transitive_r_txts).to_list() + r_txts = [f for f in all_r_txts if not should_exclude_file(f)] + else: + # No filtering - use depsets directly + resource_files = depset(transitive = transitive_resource_files).to_list() + assets = depset(transitive = transitive_assets).to_list() + manifests = depset(transitive = transitive_manifests).to_list() + all_manifest_infos + r_txts = depset(transitive = transitive_r_txts).to_list() + + assets_dir = "assets" if assets else None + + # Merge transitive manifests + merged_manifest = ctx.actions.declare_file(ctx.label.name + "_merged/AndroidManifest.xml") + + if not manifests: + ctx.actions.write( + merged_manifest, + content = """ + + +""", + ) + elif len(manifests) == 1: + ctx.actions.run_shell( + inputs = manifests, + outputs = [merged_manifest], + command = "cp $1 $2", + arguments = [manifests[0].path, merged_manifest.path], + mnemonic = "CopyManifest", + ) + else: + primary_manifest = ctx.actions.declare_file(ctx.label.name + "_primary/AndroidManifest.xml") + ctx.actions.write( + primary_manifest, + content = """ + + + + +""".format(min_sdk = ctx.attr.min_sdk_version), + ) + + merge_log = ctx.actions.declare_file(ctx.label.name + "_merged/manifest_merger_log.txt") + + # Use APPLICATION merge type (same as android_binary) + _busybox.merge_manifests( + ctx, + out_file = merged_manifest, + out_log_file = merge_log, + merge_type = "APPLICATION", + manifest = primary_manifest, + mergee_manifests = depset(manifests), + manifest_merge_order = "dependency", + manifest_values = {}, + java_package = None, + busybox = get_android_toolchain(ctx).android_resources_busybox.files_to_run, + host_javabase = _common.get_host_javabase(ctx), + ) + + # Merge transitive runtime JARs into single classes.jar + # Filter out excluded dependencies (e.g., external Maven dependencies) + merged_class_jar = ctx.actions.declare_file(ctx.label.name + "_classes.jar") + + all_jars = merged_java_info.transitive_runtime_jars.to_list() + filtered_jars = [] + + for jar in all_jars: + # Check if jar should be excluded based on exclude patterns + owner_str = str(jar.owner) if jar.owner else "" + should_exclude = False + for exclude_pattern in ctx.attr.exclude: + if exclude_pattern in owner_str: + should_exclude = True + excluded_labels.append(jar.owner) + break + if should_exclude: + continue + filtered_jars.append(jar) + + args = ctx.actions.args() + args.add("--output", merged_class_jar) + args.add("--dont_change_compression") + args.add("--normalize") + + for jar in filtered_jars: + args.add("--sources", jar) + + java_toolchain = _common.get_java_toolchain(ctx) + ctx.actions.run( + executable = java_toolchain[java_common.JavaToolchainInfo].single_jar, + arguments = [args], + inputs = depset(filtered_jars), + outputs = [merged_class_jar], + mnemonic = "MergeLibraryJars", + progress_message = "Merging transitive library jars", + ) + + r_txt = r_txts[0] if r_txts else None + if not r_txt: + r_txt = ctx.actions.declare_file(ctx.label.name + "_R.txt") + ctx.actions.write(r_txt, content = "") + + proguard_specs = [] + for spec_info in all_proguard_infos: + all_specs = spec_info.specs.to_list() + proguard_specs.extend([s for s in all_specs if not should_exclude_file(s)]) + + # Run R8 optimization if r8_config is provided + if ctx.file.r8_config: + class_jar = ctx.actions.declare_file(ctx.label.name + "_optimized_classes.jar") + + # Collect all ProGuard configs: user-provided + transitive deps + all_proguard_configs = [ctx.file.r8_config] + proguard_specs + + # Get android_jar from SDK (same as android_binary) + android_jar = get_android_sdk(ctx).android_jar + + # R8 command line arguments + # Use --classfile to output .class files instead of .dex + # Note: --min-api is not supported with --classfile + r8_args = ctx.actions.args() + r8_args.add("--classfile") + r8_args.add("--lib", android_jar) + r8_args.add("--release") + r8_args.add("--output", class_jar) + + # Add all ProGuard config files + for config in all_proguard_configs: + r8_args.add("--pg-conf", config) + + r8_args.add(merged_class_jar) + + java.run( + ctx = ctx, + host_javabase = _common.get_host_javabase(ctx), + executable = get_android_toolchain(ctx).r8.files_to_run, + arguments = [r8_args], + inputs = [merged_class_jar, android_jar] + all_proguard_configs, + outputs = [class_jar], + mnemonic = "R8Optimize", + progress_message = "Optimizing classes with R8", + ) + else: + class_jar = merged_class_jar + + fat_aar_java_info = JavaInfo( + output_jar = class_jar, + compile_jar = class_jar, + deps = all_java_infos, + ) + + all_native_libs = [] + for info in all_native_libs_infos: + all_native_libs.extend(info.native_libs.to_list()) + native_libs_files = [f for f in all_native_libs if not should_exclude_file(f)] + + aar = ctx.actions.declare_file(ctx.label.name + ".aar") + if native_libs_files: + # Create base AAR then add native libs via script + base_aar = ctx.actions.declare_file(ctx.label.name + "_base.aar") + _busybox.make_aar( + ctx, + out_aar = base_aar, + assets = assets, + assets_dir = assets_dir, + resource_files = resource_files, + class_jar = class_jar, + r_txt = r_txt, + manifest = merged_manifest, + proguard_specs = proguard_specs, + busybox = get_android_toolchain(ctx).android_resources_busybox.files_to_run, + host_javabase = _common.get_host_javabase(ctx), + ) + + temp_dir = ctx.actions.declare_directory(ctx.label.name + "_temp_native_libs") + + args = ctx.actions.args() + args.add(base_aar) + args.add(aar) + args.add(temp_dir.path) + args.add_all(native_libs_files) + + ctx.actions.run_shell( + inputs = [base_aar, ctx.file._add_native_libs_script] + native_libs_files, + outputs = [aar, temp_dir], + command = "bash $1 ${@:2}", + arguments = [ctx.file._add_native_libs_script.path, args], + mnemonic = "AddNativeLibsToAAR", + progress_message = "Adding native libraries to AAR", + ) + else: + aar = _resources.make_aar( + ctx, + assets = assets, + assets_dir = assets_dir, + resource_files = resource_files, + class_jar = class_jar, + r_txt = r_txt, + manifest = merged_manifest, + proguard_specs = proguard_specs, + busybox = get_android_toolchain(ctx).android_resources_busybox.files_to_run, + host_javabase = _common.get_host_javabase(ctx), + ) + + # Generate excluded dependencies file + # Just output the raw excluded labels - consumers can filter/transform as needed + excluded_deps_file = ctx.actions.declare_file(ctx.label.name + "_excluded_deps.txt") + + # Deduplicate labels + unique_labels = {} + for label in excluded_labels: + label_str = str(label) + if label_str not in unique_labels: + unique_labels[label_str] = True + + content = "" + for label_str in sorted(unique_labels.keys()): + content += label_str + "\n" + + ctx.actions.write( + output = excluded_deps_file, + content = content, + ) + + return [ + DefaultInfo( + files = depset([aar]), + ), + OutputGroupInfo( + aar = depset([aar]), + class_jar = depset([class_jar]), + manifest = depset([merged_manifest]), + excluded_deps = depset([excluded_deps_file]), + ), + fat_aar_java_info, + StarlarkAndroidResourcesInfo( + direct_resources_nodes = depset(), + transitive_resources_nodes = depset(), + transitive_assets = depset(assets), + transitive_assets_symbols = depset(), + transitive_compiled_assets = depset(), + direct_compiled_resources = depset(), + transitive_compiled_resources = depset(), + transitive_manifests = depset([merged_manifest]), + transitive_r_txts = depset([r_txt]), + transitive_resource_files = depset(resource_files), + packages_to_r_txts = {}, + transitive_resource_apks = depset(), + ), + AndroidNativeLibsInfo( + native_libs = depset(native_libs_files), + ), + FatAarDependenciesInfo( + excluded_labels = depset(excluded_labels), + ), + ] + +fat_aar = rule( + implementation = _fat_aar_impl, + attrs = { + "deps": attr.label_list( + aspects = [fat_aar_aspect], + doc = "The list of android_library targets to bundle", + ), + "min_sdk_version": attr.string( + default = "23", + doc = "Minimum SDK version for the primary manifest", + ), + "exclude": attr.string_list( + default = [], + doc = "List of patterns to exclude from bundling (e.g., ['@maven//'])", + ), + "r8_config": attr.label( + allow_single_file = [".pro", ".txt"], + doc = "ProGuard configuration file for R8. If provided, R8 optimization is enabled. Combined with transitive ProGuard specs from dependencies.", + ), + "_add_native_libs_script": attr.label( + default = Label("//rules/fat_aar:add_native_libs.sh"), + allow_single_file = True, + ), + "_java_toolchain": attr.label( + default = Label("@bazel_tools//tools/jdk:current_java_toolchain"), + ), + "_host_javabase": attr.label( + default = Label("@bazel_tools//tools/jdk:current_host_java_runtime"), + cfg = "exec", + ), + }, + toolchains = [ + config_common.toolchain_type("@rules_android//toolchains/android:toolchain_type", mandatory = False), + config_common.toolchain_type("//toolchains/android:toolchain_type", mandatory = False), + config_common.toolchain_type("@rules_android//toolchains/android_sdk:toolchain_type", mandatory = False), + config_common.toolchain_type("//toolchains/android_sdk:toolchain_type", mandatory = False), + ], + fragments = ["android", "bazel_android", "java"], + doc = "Bundles transitive android_library dependencies into a single AAR file.", +) diff --git a/rules/rules.bzl b/rules/rules.bzl index af06e80e2..5a8245cc1 100644 --- a/rules/rules.bzl +++ b/rules/rules.bzl @@ -69,6 +69,14 @@ load( "//rules/android_sdk_repository:rule.bzl", _android_sdk_repository = "android_sdk_repository", ) +load( + "//rules/fat_aar:rule.bzl", + _fat_aar = "fat_aar", +) +load( + "//rules/fat_aar:pom_from_fat_aar.bzl", + _fat_aar_pom = "fat_aar_pom", +) # Current version. Tools may check this to determine compatibility. RULES_ANDROID_VERSION = "0.1.0" @@ -84,6 +92,8 @@ android_sdk = _android_sdk android_sdk_repository = _android_sdk_repository android_tools_defaults_jar = _android_tools_defaults_jar asar_import = _asar_import +fat_aar = _fat_aar +fat_aar_pom = _fat_aar_pom instrumented_app_info_aspect = _instrumented_app_info_aspect StarlarkApkInfo = _StarlarkApkInfo ApkInfo = _ApkInfo From df546ad44974775c68be7b3728cff20635750e6f Mon Sep 17 00:00:00 2001 From: Olivier Notteghem Date: Fri, 5 Jun 2026 17:43:01 +0200 Subject: [PATCH 2/3] Fix CI tests --- rules/fat_aar/aspect.bzl | 2 +- rules/fat_aar/rule.bzl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rules/fat_aar/aspect.bzl b/rules/fat_aar/aspect.bzl index 203c6c097..0ba61d97c 100644 --- a/rules/fat_aar/aspect.bzl +++ b/rules/fat_aar/aspect.bzl @@ -15,7 +15,7 @@ """Aspect for collecting transitive Android providers.""" load("@rules_java//java/common:proguard_spec_info.bzl", "ProguardSpecInfo") -load("//rules:providers.bzl", "AndroidAssetsInfo", "AndroidNativeLibsInfo", "AndroidResourcesInfo", "StarlarkAndroidResourcesInfo") +load("//providers:providers.bzl", "AndroidAssetsInfo", "AndroidNativeLibsInfo", "AndroidResourcesInfo", "StarlarkAndroidResourcesInfo") load("//rules:visibility.bzl", "PROJECT_VISIBILITY") visibility(PROJECT_VISIBILITY) diff --git a/rules/fat_aar/rule.bzl b/rules/fat_aar/rule.bzl index d26f92000..e7afefa8a 100644 --- a/rules/fat_aar/rule.bzl +++ b/rules/fat_aar/rule.bzl @@ -20,7 +20,7 @@ load("@rules_java//java/common:proguard_spec_info.bzl", "ProguardSpecInfo") load("//rules:busybox.bzl", _busybox = "busybox") load("//rules:common.bzl", _common = "common") load("//rules:java.bzl", "java") -load("//rules:providers.bzl", "AndroidAssetsInfo", "AndroidNativeLibsInfo", "AndroidResourcesInfo", "StarlarkAndroidResourcesInfo") +load("//providers:providers.bzl", "AndroidAssetsInfo", "AndroidNativeLibsInfo", "AndroidResourcesInfo", "StarlarkAndroidResourcesInfo") load("//rules:resources.bzl", _resources = "resources") load("//rules:utils.bzl", "get_android_sdk", "get_android_toolchain", "utils") load("//rules:visibility.bzl", "PROJECT_VISIBILITY") From e5a4dbd726c569881a2b6281ba46bb2d1b1a83a6 Mon Sep 17 00:00:00 2001 From: Olivier Notteghem Date: Fri, 5 Jun 2026 17:55:43 +0200 Subject: [PATCH 3/3] Fix CI tests --- rules/BUILD | 1 + 1 file changed, 1 insertion(+) diff --git a/rules/BUILD b/rules/BUILD index 4cb95be0b..d47e255aa 100644 --- a/rules/BUILD +++ b/rules/BUILD @@ -109,6 +109,7 @@ bzl_library( ], visibility = [ "//mobile_install:__pkg__", + "//rules/fat_aar:__pkg__", "//stardoc:__pkg__", "//test/rules/android_binary/r8_integration:__pkg__", "//test/rules/android_sdk_repository:__pkg__",