Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,26 @@ jobs:
bazel build -c opt --lockfile_mode=${{ env.BAZEL_LOCKFILE_MODE }} --repo_env=ANDROID_NDK_HOME="$ANDROID_NDK_HOME" --android_platforms=@rules_android//:arm64-v8a //:consumer_rules_r8
python3 ../../tools/ci/consumer_rules_check.py

- name: Plugin hub guard against private loads
run: python3 tools/ci/plugin_hub_boundary_guard.py --selftest

- name: Example plugin hub public-load guard
working-directory: examples/pub_plugins
run: python3 ../../tools/ci/plugin_hub_boundary_guard.py @flutter_plugins//:generated_plugin_registrant_src

- name: Consumer plugin hubs public-load guard
working-directory: tests/consumer
run: python3 ../../tools/ci/plugin_hub_boundary_guard.py @flutter_plugins//:generated_plugin_registrant_src @composed_plugins//:generated_plugin_registrant_src @composed_override_plugins//:generated_plugin_registrant_src

- name: Consumer hand-wired app mode and DEX checks
working-directory: tests/consumer
run: |
set -euo pipefail
apk="bazel-bin/hand_wired_app/hand_wired_app_unsigned.apk"
bazel build -c fastbuild --@rules_flutter_core//flutter:mode=debug --lockfile_mode=${{ env.BAZEL_LOCKFILE_MODE }} --repo_env=ANDROID_NDK_HOME="$ANDROID_NDK_HOME" --android_platforms=@rules_android//:arm64-v8a //hand_wired_app:hand_wired_app
python3 ../../tools/ci/hand_wired_dex_check.py debug "$apk"
bazel build -c opt --@rules_flutter_core//flutter:mode=release --lockfile_mode=${{ env.BAZEL_LOCKFILE_MODE }} --repo_env=ANDROID_NDK_HOME="$ANDROID_NDK_HOME" --android_platforms=@rules_android//:arm64-v8a //hand_wired_app:hand_wired_app
python3 ../../tools/ci/hand_wired_dex_check.py release "$apk"
# Composed app: the APK check fails the build on a planted mismatch.
- name: Consumer composed app release APK check
working-directory: tests/consumer
Expand Down Expand Up @@ -264,6 +284,10 @@ jobs:
set -o pipefail
env -u ANDROID_HOME -u ANDROID_NDK_HOME bazel test //... --lockfile_mode=${{ env.BAZEL_LOCKFILE_MODE }} 2>&1 | tee ../bazel_test_android.log

- name: Registrant parity tests in debug mode
working-directory: rules_flutter_android
run: bazel test --@rules_flutter_core//flutter:mode=debug --lockfile_mode=${{ env.BAZEL_LOCKFILE_MODE }} //tests/registrant_parity:all

# The registrant parity goldens, recaptured from this Flutter SDK, must
# still match: a template change in flutter_tools fails here.
- name: Registrant parity recapture
Expand Down
56 changes: 20 additions & 36 deletions QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ relative-import helpers:

```python
# integration_test/BUILD.bazel
load("@rules_flutter_core//flutter:defs.bzl", "flutter_debug_core")
load("@rules_flutter_core//flutter:composition.bzl", "flutter_debug_core")

flutter_debug_core(
name = "prebuilt_core",
Expand Down Expand Up @@ -618,41 +618,25 @@ shows recipe inputs and ABI-specific native contributions.

## Consumer-owned stock android_binary

A consumer may package core outputs with its own stock `android_binary`. The
four output groups are provided by `OutputGroupInfo` on a configured
`flutter_core` target; `FlutterArtifactInfo` is a separate typed provider, not
the output-group provider. Use `filegroup(output_group = ...)` to select the
outputs. `flutter_assets` is the asset tree, `flutter_engine` is the whole
selected engine-library set (which may contain multiple libraries for an ABI),
`flutter_libapp` is release AOT output and empty in debug, and
`flutter_embedding` is the selected mode's embedding jar. Use the mode-selecting
embedding output directly rather than a hard-coded Maven release label.

For example, one group can be selected from the configured core like this:

```python
filegroup(
name = "direct_engine",
srcs = [":core"],
output_group = "flutter_engine",
)
```

Select the other three groups with the same pattern and their respective
`output_group` names. The checked fixture below shows those groups flowing into
the stock binary together with the mode-selected Java embedding and raw
ABI-pinned native inputs.

The checked stock-binary fixture is
[`tests/consumer/composed_app/BUILD.bazel`](tests/consumer/composed_app/BUILD.bazel),
target `//composed_app:direct_apk`. It shows group selection, mode-selected
embedding import with the explicit AndroidX dependency closure in
[`tests/consumer/embedding_deps.bzl`](tests/consumer/embedding_deps.bzl), raw
ABI-pinned native inputs, and a stock `android_binary`. The fixture's
`direct_native` helper is consumer-owned demonstration code, not a public ruleset
API. Android packaging owns normal JNI and strip handling; use the raw
ABI-pinned strip path as appropriate for the consumer's native files. Do not
assume a single engine `.so` or a minimal embedding classpath.
Use the convenience rules from `defs.bzl` for standard apps. A consumer that
needs to own Android packaging can instead combine the public building blocks:
`flutter_core` exposes the `flutter_assets`, `flutter_engine`, and
`flutter_libapp` output groups. The engine group may contain several libraries
per ABI, and `flutter_libapp` is empty in debug mode. The Android composition
module provides `flutter_plugin_registrant`, `flutter_build_config`, and the
mode-selected `embedding_jar`; use `flutter_embedding_deps(maven_repo)` for the
embedding's explicit AndroidX classpath. Here `flutter_assets` is `flutter_core`'s
output group, not the `flutter_assets` rule. Stock `rules_android` rules then package those targets; for native
packaging, see [`tests/consumer/composed_app:direct_apk`](tests/consumer/composed_app/BUILD.bazel).

[`tests/consumer/hand_wired_app/BUILD.bazel`](tests/consumer/hand_wired_app/BUILD.bazel)
demonstrates plugin, Java, and Maven wiring without plugin extensions or
convenience macros; it is not a complete APK because it does not package the
Flutter native libraries. The registrant and build-config rules each produce
one Java source file suitable for `android_library.srcs`, and development
plugin code is included only in debug. Consumers using Kotlin Multiplatform
Maven artifacts must name the platform artifact (such as an `-jvm` artifact)
because `rules_jvm_external` does not resolve Gradle module metadata.

## ABI configuration and switching

Expand Down
49 changes: 30 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,31 +53,42 @@ declarations are needed.

### Composition and ABI selection

Declare one `flutter_core` from the core module per Bazel package; it owns that
package's app outputs and package/test outputs. Declare
`flutter_plugin_deps_guards` once in the package that owns the plugin graph to
check its committed Maven segment. `flutter_android_app` is the standard helper
entrypoint.
`flutter_android_inputs`, `flutter_apk_check`, `flutter_android_manifest`,
`flutter_assets_dir`, and `flutter_native_contribution` support custom
composition.
The convenience API is the default: load `flutter_core`, `flutter_package`,
`flutter_pubspec`, `flutter_test`, and `flutter_test_suite` from
`@rules_flutter_core//flutter:defs.bzl`, and `flutter_android_app` and related
Android helpers from `@rules_flutter_android//flutter:defs.bzl`. For deliberate
hand-wiring, the public `composition.bzl` modules expose lower-level providers,
rules, and targets. `flutter_pubspec` remains in core `defs.bzl` because
`flutter_package` requires it.

Core's Flutter SDK repository exposes `@flutter_sdk//:flutter.version.json` as
read-only version metadata. Android's embedding repositories derive their SDK
version from that file; consumers do not need to name those repository labels.

Select APK ABIs with ordinary `--android_platforms`. Use one APK label for each
existing app/mode variant and separate build commands for fat or single-ABI
configurations. Capture each APK before the next configuration because the
materialized APK path may be shared.

A consumer-owned stock `android_binary` may consume configured core targets'
`OutputGroupInfo`: `flutter_assets` is the bundle tree, `flutter_engine` is the
complete selected engine-library set, `flutter_libapp` is release AOT (empty in
debug), and `flutter_embedding` is the mode-selected embedding jar. The engine
group can contain multiple libraries for one ABI. `FlutterArtifactInfo` is a
separate typed provider. See the checked stock-binary example in
[`tests/consumer/composed_app/BUILD.bazel`](tests/consumer/composed_app/BUILD.bazel),
target `//composed_app:direct_apk`. Consumers own Android packaging and native
library handling, including strip behavior required by their packaging graph.

`flutter_debug_core` declares a debug artifact of an app's `flutter_core` built from another entrypoint, such as a test file under `integration_test/`, with its own `dart_defines`. Dart files outside `lib/` that the entrypoint imports go in `srcs`; an undeclared import fails the compile. The app's package inputs, mode-free bundle and debug engine are reused, so beyond the app's own outputs it adds only its kernel and a bundle copy. It is debug only: a release configuration reports it incompatible, and it is `manual` unless `tags` overrides that, so `bazel build //...` skips it under debug. Build it with `-c fastbuild --@rules_flutter_core//flutter:mode=debug` and an Android `--platforms`.
Core output groups remain available to hand-wired consumers: `flutter_assets`,
`flutter_engine` (possibly several libraries per ABI), and `flutter_libapp`
(empty in debug mode). Embedding is owned by the Android ruleset. Use
`@rules_flutter_android//flutter:embedding_jar` for the mode-selected jar;
`flutter_embedding_deps(maven_repo)` supplies its explicit AndroidX classpath.
The [`tests/consumer/hand_wired_app/BUILD.bazel`](tests/consumer/hand_wired_app/BUILD.bazel)
fixture demonstrates plugin, Java, and Maven wiring, but is not a complete APK.
For native packaging, see
[`tests/consumer/composed_app:direct_apk`](tests/consumer/composed_app/BUILD.bazel).

`flutter_debug_core` is a lower-level core composition rule. It declares a debug
artifact of an app's `flutter_core` built from another entrypoint, such as an
integration-test file, with its own `dart_defines`. Dart files outside `lib/`
that the entrypoint imports go in `srcs`; an undeclared import fails the
compile. The app's package inputs, mode-free bundle, and debug engine are reused.
It is debug-only and `manual` unless `tags` overrides that. Build it with
`-c fastbuild --@rules_flutter_core//flutter:mode=debug` and an Android
`--platforms`.


One measured warm four-command sequence took 1.500 seconds with the former suffixed labels and 16.165 seconds with one label under successive ABI configurations. These timings are scoped to one machine/workflow, not a general speed promise. Expensive Flutter/native actions remained reusable, while Android packaging reran. Disk/remote-cache switching speed was not measured; shared materialized paths do not inherently collide in action caches.

Expand Down
48 changes: 28 additions & 20 deletions examples/demo_app/MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading