From 5b8056cdc00cebac5078cab5612cba78b5ca78bd Mon Sep 17 00:00:00 2001 From: Mikhail Novoseltsev <51940183+Sameri11@users.noreply.github.com> Date: Wed, 11 Nov 2026 21:27:52 +0500 Subject: [PATCH 1/2] Move Android engine and toolchain ownership --- QUICKSTART.md | 18 +- README.md | 7 +- examples/demo_app/BUILD.bazel | 6 +- examples/demo_app/MODULE.bazel.lock | 147 +++++++----- examples/local_plugin/MODULE.bazel.lock | 147 +++++++----- .../packages/host_app/BUILD.bazel | 5 +- examples/no_plugins/BUILD.bazel | 5 +- examples/no_plugins/MODULE.bazel.lock | 143 +++++++----- examples/pub_plugins/BUILD.bazel | 6 +- examples/pub_plugins/MODULE.bazel.lock | 147 +++++++----- rules_flutter_android/MODULE.bazel | 24 +- rules_flutter_android/MODULE.bazel.lock | 159 ++++++++----- rules_flutter_android/flutter/BUILD.bazel | 101 +++++++++ rules_flutter_android/flutter/abis.bzl | 77 +++++++ .../flutter/android_core.bzl | 12 + rules_flutter_android/flutter/defs.bzl | 3 + .../flutter/private/abis.bzl | 69 ++---- .../flutter/private/android_inputs_rules.bzl | 21 +- .../flutter/private/apk_check.bzl | 2 +- .../flutter/private/embedding_repo.bzl | 49 +++- .../flutter/private/gen_snapshot_repo.bzl | 55 +++++ .../flutter/private/native_files.bzl | 2 +- .../flutter/private/native_strip.bzl | 4 +- .../flutter/private/target_toolchain_repo.bzl | 100 +++++++++ .../flutter/private/target_toolchains.bzl | 18 ++ rules_flutter_core/MODULE.bazel | 11 +- rules_flutter_core/MODULE.bazel.lock | 49 +--- rules_flutter_core/flutter/BUILD.bazel | 14 ++ rules_flutter_core/flutter/abis.bzl | 30 +-- .../flutter/private/BUILD.bazel | 97 ++------ rules_flutter_core/flutter/private/abis.bzl | 212 +----------------- rules_flutter_core/flutter/private/core.bzl | 119 ++++------ .../flutter/private/debug_core.bzl | 15 +- rules_flutter_core/flutter/private/repo.bzl | 108 +-------- rules_flutter_core/flutter/private/rules.bzl | 174 ++++++++++---- .../flutter/private/toolchain.bzl | 104 ++------- .../flutter/target_toolchain.bzl | 56 +++++ .../tools/ci/key_portability_test.py | 17 +- tests/consumer/BUILD.bazel | 8 +- tests/consumer/MODULE.bazel.lock | 147 +++++++----- tests/consumer/composed_app/BUILD.bazel | 7 +- tests/consumer/external_app/app/BUILD.bazel | 4 +- tests/consumer/hand_wired_app/BUILD.bazel | 4 +- tests/consumer/standard_layout/BUILD.bazel | 4 +- tests/core_consumer/MODULE.bazel.lock | 61 +---- tests/namespace_dynamic/MODULE.bazel.lock | 147 +++++++----- tools/ci/core_target_os_rejection.py | 31 +-- tools/ci/test_kernel_invariants.sh | 8 +- 48 files changed, 1499 insertions(+), 1255 deletions(-) create mode 100644 rules_flutter_android/flutter/abis.bzl create mode 100644 rules_flutter_android/flutter/android_core.bzl create mode 100644 rules_flutter_android/flutter/private/gen_snapshot_repo.bzl create mode 100644 rules_flutter_android/flutter/private/target_toolchain_repo.bzl create mode 100644 rules_flutter_android/flutter/private/target_toolchains.bzl create mode 100644 rules_flutter_core/flutter/target_toolchain.bzl diff --git a/QUICKSTART.md b/QUICKSTART.md index aa488269..d5e6ef46 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -620,14 +620,16 @@ shows recipe inputs and ABI-specific native contributions. 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). +`flutter_core` exposes the `flutter_assets` and `flutter_libapp` output groups; +`flutter_libapp` is empty in debug mode. Android engine libraries, possibly +several per ABI, come from `@rules_flutter_android//flutter:engine_libs`. 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 diff --git a/README.md b/README.md index 5e3f8a4e..340cb503 100644 --- a/README.md +++ b/README.md @@ -70,9 +70,10 @@ 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. -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 +Core output groups remain available to hand-wired consumers: `flutter_assets` +and `flutter_libapp` (empty in debug mode). Android engine libraries, possibly +several per ABI, are provided by `@rules_flutter_android//flutter:engine_libs`. +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) diff --git a/examples/demo_app/BUILD.bazel b/examples/demo_app/BUILD.bazel index b9f27e81..743792c4 100644 --- a/examples/demo_app/BUILD.bazel +++ b/examples/demo_app/BUILD.bazel @@ -1,14 +1,14 @@ # Dart targets for the flat demo app. The core owns the standard Flutter package # layout, and its outputs are composed into Android targets in android/app. -load("@rules_flutter_android//flutter:defs.bzl", "flutter_plugin_deps_guards") -load("@rules_flutter_core//flutter:defs.bzl", "flutter_core", "flutter_test", "flutter_test_suite") +load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_core", "flutter_plugin_deps_guards") +load("@rules_flutter_core//flutter:defs.bzl", "flutter_test", "flutter_test_suite") package(default_visibility = ["//visibility:public"]) flutter_plugin_deps_guards(plugins = "@flutter_plugins") -flutter_core( +flutter_android_core( name = "app", srcs = glob(["lib/**/*.dart"]) + [":lib/gen_info.dart"], dart_plugin_registrant = "lib/dart_plugin_registrant.dart", diff --git a/examples/demo_app/MODULE.bazel.lock b/examples/demo_app/MODULE.bazel.lock index 2018548d..8526fb56 100644 --- a/examples/demo_app/MODULE.bazel.lock +++ b/examples/demo_app/MODULE.bazel.lock @@ -409,13 +409,12 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%android_ndk": { "general": { - "bzlTransitiveDigest": "ChmCXXuZkbfT0XtrGZ3VW6ftH+vEyMOroOzRtbHokuY=", + "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", "usagesDigest": "HFu4wseopxonsvDUd/4S1JEEWdwTssTyzt3rsp61Cg8=", "recordedInputs": [ + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android rules_android+", - "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android_ndk rules_android_ndk+", - "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms" + "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android_ndk rules_android_ndk+" ], "generatedRepoSpecs": { "androidndk": { @@ -431,15 +430,15 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%flutter_plugins_ext": { "general": { - "bzlTransitiveDigest": "ChmCXXuZkbfT0XtrGZ3VW6ftH+vEyMOroOzRtbHokuY=", + "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", "usagesDigest": "NAsegc0NGgbnISf1WUvMP9+cZAMUJEHXC+unOFzUAKo=", "recordedInputs": [ "REPO_MAPPING:,flutter_maven rules_jvm_external++maven+flutter_maven", "REPO_MAPPING:,pub sameri11_rules_flutter_core++pub+pub", + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android rules_android+", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android_ndk rules_android_ndk+", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms", "REPO_MAPPING:sameri11_rules_flutter_android+,androidndk_cmake sameri11_rules_flutter_android++android_ndk+androidndk_cmake" ], "generatedRepoSpecs": { @@ -477,87 +476,131 @@ }, "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_embedding": { "general": { - "bzlTransitiveDigest": "Cc2IsXwVitTCSUMnh/0e+uh1l6r143p3g2wxU9QzBGg=", + "bzlTransitiveDigest": "3eqHOPboGukDJg90t58rNh9rmebP4fSAVaS1+Xd+5d4=", "usagesDigest": "VDubd1CC/3EsTr4xz8tz3uu65g6E5r8qQxmJ5I7keKE=", "recordedInputs": [ - "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk" + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", + "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+" ], "generatedRepoSpecs": { - "flutter_embedding_debug": { - "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_embedding_jar", - "attributes": { - "mode": "debug" - } - }, "flutter_embedding_release": { "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_embedding_jar", "attributes": { "mode": "release" } - } - } - } - }, - "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { - "general": { - "bzlTransitiveDigest": "AARGgu9wwVVVg2ko+pKNhSOGTtOh12+1knTwjHuxpjo=", - "usagesDigest": "PVRq/fIGaef1uAaEjTR0LCJbig9Z96itWnzuvhhPV74=", - "recordedInputs": [ - "REPO_MAPPING:sameri11_rules_flutter_core+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms" - ], - "generatedRepoSpecs": { - "flutter_sdk": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_sdk", + }, + "flutter_embedding_debug": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_embedding_jar", "attributes": { - "version": "3.47.3" + "mode": "debug" } }, - "flutter_tools_packages": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_tools_packages", - "attributes": {} - }, "flutter_engine_arm64_v8a_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "arm64_v8a_release", - "extract_libs": true + "artifact": "arm64_v8a_release" } }, "flutter_engine_arm64_v8a_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "arm64_v8a_debug", - "extract_libs": true + "artifact": "arm64_v8a_debug" } }, "flutter_engine_x86_64_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "x86_64_release", - "extract_libs": true + "artifact": "x86_64_release" } }, "flutter_engine_x86_64_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "x86_64_debug", - "extract_libs": true + "artifact": "x86_64_debug" } }, "flutter_engine_armeabi_v7a_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "armeabi_v7a_release", - "extract_libs": true + "artifact": "armeabi_v7a_release" } }, "flutter_engine_armeabi_v7a_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", + "attributes": { + "artifact": "armeabi_v7a_debug" + } + } + } + } + }, + "@@sameri11_rules_flutter_android+//flutter/private:target_toolchain_repo.bzl%flutter_android_toolchains": { + "general": { + "bzlTransitiveDigest": "0D9Q8Q4AQrFirqM6wLGtrv3QlasjcQ2c2i2OxhVYFfI=", + "usagesDigest": "2WhXsIhHLINViYFxmWfqIy1rlYxnWFK11LB9vSRc2M0=", + "recordedInputs": [ + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_gen_snapshot_arm64_v8a sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_arm64_v8a", + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_gen_snapshot_x86_64 sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_x86_64", + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_gen_snapshot_armeabi_v7a sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a", + "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+" + ], + "generatedRepoSpecs": { + "flutter_gen_snapshot_arm64_v8a": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-arm64-release" + } + }, + "flutter_gen_snapshot_x86_64": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-x64-release" + } + }, + "flutter_gen_snapshot_armeabi_v7a": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-arm-release" + } + }, + "flutter_android_target_toolchains": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:target_toolchain_repo.bzl%_android_toolchains", + "attributes": { + "aot_toolchain_targets": { + "arm64-v8a": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_arm64_v8a//:aot_toolchain'", + "x86_64": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_x86_64//:aot_toolchain'", + "armeabi-v7a": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a//:aot_toolchain'" + }, + "aot_toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:aot_toolchain_type'", + "toolchain_targets": { + "arm64-v8a": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_arm64_v8a'", + "x86_64": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_x86_64'", + "armeabi-v7a": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_armeabi_v7a'" + }, + "toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:target_toolchain_type'" + } + } + } + } + }, + "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { + "general": { + "bzlTransitiveDigest": "Et4GM1IW3LCV7ah8t1qEeCgh9cOh1ek26YeJOVdYI+0=", + "usagesDigest": "PVRq/fIGaef1uAaEjTR0LCJbig9Z96itWnzuvhhPV74=", + "recordedInputs": [], + "generatedRepoSpecs": { + "flutter_sdk": { + "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_sdk", "attributes": { - "artifact": "armeabi_v7a_debug", - "extract_libs": true + "version": "3.47.3" } + }, + "flutter_tools_packages": { + "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_tools_packages", + "attributes": {} } } } diff --git a/examples/local_plugin/MODULE.bazel.lock b/examples/local_plugin/MODULE.bazel.lock index d66596e9..85c44615 100644 --- a/examples/local_plugin/MODULE.bazel.lock +++ b/examples/local_plugin/MODULE.bazel.lock @@ -409,13 +409,12 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%android_ndk": { "general": { - "bzlTransitiveDigest": "ChmCXXuZkbfT0XtrGZ3VW6ftH+vEyMOroOzRtbHokuY=", + "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", "usagesDigest": "mXQ4Q6FohJ5fXR4FTAao+VwkF3Gfgb646BQodIC/hD8=", "recordedInputs": [ + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android rules_android+", - "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android_ndk rules_android_ndk+", - "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms" + "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android_ndk rules_android_ndk+" ], "generatedRepoSpecs": { "androidndk": { @@ -431,15 +430,15 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%flutter_plugins_ext": { "general": { - "bzlTransitiveDigest": "ChmCXXuZkbfT0XtrGZ3VW6ftH+vEyMOroOzRtbHokuY=", + "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", "usagesDigest": "G8kgLfLv/g4JO70/Fz65CA3gz9V9VxPrddSvVKtpw54=", "recordedInputs": [ "REPO_MAPPING:,flutter_maven rules_jvm_external++maven+flutter_maven", "REPO_MAPPING:,pub sameri11_rules_flutter_core++pub+pub", + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android rules_android+", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android_ndk rules_android_ndk+", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms", "REPO_MAPPING:sameri11_rules_flutter_android+,androidndk_cmake sameri11_rules_flutter_android++android_ndk+androidndk_cmake" ], "generatedRepoSpecs": { @@ -472,87 +471,131 @@ }, "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_embedding": { "general": { - "bzlTransitiveDigest": "Cc2IsXwVitTCSUMnh/0e+uh1l6r143p3g2wxU9QzBGg=", + "bzlTransitiveDigest": "3eqHOPboGukDJg90t58rNh9rmebP4fSAVaS1+Xd+5d4=", "usagesDigest": "VDubd1CC/3EsTr4xz8tz3uu65g6E5r8qQxmJ5I7keKE=", "recordedInputs": [ - "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk" + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", + "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+" ], "generatedRepoSpecs": { - "flutter_embedding_debug": { - "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_embedding_jar", - "attributes": { - "mode": "debug" - } - }, "flutter_embedding_release": { "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_embedding_jar", "attributes": { "mode": "release" } - } - } - } - }, - "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { - "general": { - "bzlTransitiveDigest": "AARGgu9wwVVVg2ko+pKNhSOGTtOh12+1knTwjHuxpjo=", - "usagesDigest": "mhGaagnJ1rxsIhLvM71ruzvKd+zmw0qH6YhJRGCfdjM=", - "recordedInputs": [ - "REPO_MAPPING:sameri11_rules_flutter_core+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms" - ], - "generatedRepoSpecs": { - "flutter_sdk": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_sdk", + }, + "flutter_embedding_debug": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_embedding_jar", "attributes": { - "version": "3.47.3" + "mode": "debug" } }, - "flutter_tools_packages": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_tools_packages", - "attributes": {} - }, "flutter_engine_arm64_v8a_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "arm64_v8a_release", - "extract_libs": true + "artifact": "arm64_v8a_release" } }, "flutter_engine_arm64_v8a_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "arm64_v8a_debug", - "extract_libs": true + "artifact": "arm64_v8a_debug" } }, "flutter_engine_x86_64_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "x86_64_release", - "extract_libs": true + "artifact": "x86_64_release" } }, "flutter_engine_x86_64_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "x86_64_debug", - "extract_libs": true + "artifact": "x86_64_debug" } }, "flutter_engine_armeabi_v7a_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "armeabi_v7a_release", - "extract_libs": true + "artifact": "armeabi_v7a_release" } }, "flutter_engine_armeabi_v7a_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", + "attributes": { + "artifact": "armeabi_v7a_debug" + } + } + } + } + }, + "@@sameri11_rules_flutter_android+//flutter/private:target_toolchain_repo.bzl%flutter_android_toolchains": { + "general": { + "bzlTransitiveDigest": "0D9Q8Q4AQrFirqM6wLGtrv3QlasjcQ2c2i2OxhVYFfI=", + "usagesDigest": "2WhXsIhHLINViYFxmWfqIy1rlYxnWFK11LB9vSRc2M0=", + "recordedInputs": [ + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_gen_snapshot_arm64_v8a sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_arm64_v8a", + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_gen_snapshot_x86_64 sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_x86_64", + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_gen_snapshot_armeabi_v7a sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a", + "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+" + ], + "generatedRepoSpecs": { + "flutter_gen_snapshot_arm64_v8a": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-arm64-release" + } + }, + "flutter_gen_snapshot_x86_64": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-x64-release" + } + }, + "flutter_gen_snapshot_armeabi_v7a": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-arm-release" + } + }, + "flutter_android_target_toolchains": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:target_toolchain_repo.bzl%_android_toolchains", + "attributes": { + "aot_toolchain_targets": { + "arm64-v8a": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_arm64_v8a//:aot_toolchain'", + "x86_64": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_x86_64//:aot_toolchain'", + "armeabi-v7a": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a//:aot_toolchain'" + }, + "aot_toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:aot_toolchain_type'", + "toolchain_targets": { + "arm64-v8a": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_arm64_v8a'", + "x86_64": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_x86_64'", + "armeabi-v7a": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_armeabi_v7a'" + }, + "toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:target_toolchain_type'" + } + } + } + } + }, + "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { + "general": { + "bzlTransitiveDigest": "Et4GM1IW3LCV7ah8t1qEeCgh9cOh1ek26YeJOVdYI+0=", + "usagesDigest": "mhGaagnJ1rxsIhLvM71ruzvKd+zmw0qH6YhJRGCfdjM=", + "recordedInputs": [], + "generatedRepoSpecs": { + "flutter_sdk": { + "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_sdk", "attributes": { - "artifact": "armeabi_v7a_debug", - "extract_libs": true + "version": "3.47.3" } + }, + "flutter_tools_packages": { + "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_tools_packages", + "attributes": {} } } } diff --git a/examples/local_plugin/packages/host_app/BUILD.bazel b/examples/local_plugin/packages/host_app/BUILD.bazel index 1093d3dd..8e728d55 100644 --- a/examples/local_plugin/packages/host_app/BUILD.bazel +++ b/examples/local_plugin/packages/host_app/BUILD.bazel @@ -1,8 +1,9 @@ -load("@rules_flutter_core//flutter:defs.bzl", "flutter_core", "flutter_test_suite") +load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_core") +load("@rules_flutter_core//flutter:defs.bzl", "flutter_test_suite") package(default_visibility = ["//visibility:public"]) -flutter_core( +flutter_android_core( name = "app", assets = [], dart_plugin_registrant = "lib/dart_plugin_registrant.dart", diff --git a/examples/no_plugins/BUILD.bazel b/examples/no_plugins/BUILD.bazel index ea737076..1052ff4b 100644 --- a/examples/no_plugins/BUILD.bazel +++ b/examples/no_plugins/BUILD.bazel @@ -1,10 +1,11 @@ # Dart targets for a plugin-free app. -load("@rules_flutter_core//flutter:defs.bzl", "flutter_core", "flutter_integration_test", "flutter_test_suite") +load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_core") +load("@rules_flutter_core//flutter:defs.bzl", "flutter_integration_test", "flutter_test_suite") package(default_visibility = ["//visibility:public"]) -flutter_core( +flutter_android_core( name = "core", srcs = glob(["lib/**/*.dart"]), # flutter create declares no assets. diff --git a/examples/no_plugins/MODULE.bazel.lock b/examples/no_plugins/MODULE.bazel.lock index 623972bb..c97362ec 100644 --- a/examples/no_plugins/MODULE.bazel.lock +++ b/examples/no_plugins/MODULE.bazel.lock @@ -409,13 +409,12 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%android_ndk": { "general": { - "bzlTransitiveDigest": "ChmCXXuZkbfT0XtrGZ3VW6ftH+vEyMOroOzRtbHokuY=", + "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", "usagesDigest": "gihfLsx8YMM+v3GPdKHQB+XDY0mBmcT3vsEGUwsp5YE=", "recordedInputs": [ + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android rules_android+", - "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android_ndk rules_android_ndk+", - "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms" + "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android_ndk rules_android_ndk+" ], "generatedRepoSpecs": { "androidndk": { @@ -431,87 +430,131 @@ }, "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_embedding": { "general": { - "bzlTransitiveDigest": "Cc2IsXwVitTCSUMnh/0e+uh1l6r143p3g2wxU9QzBGg=", + "bzlTransitiveDigest": "3eqHOPboGukDJg90t58rNh9rmebP4fSAVaS1+Xd+5d4=", "usagesDigest": "VDubd1CC/3EsTr4xz8tz3uu65g6E5r8qQxmJ5I7keKE=", "recordedInputs": [ - "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk" + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", + "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+" ], "generatedRepoSpecs": { - "flutter_embedding_debug": { - "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_embedding_jar", - "attributes": { - "mode": "debug" - } - }, "flutter_embedding_release": { "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_embedding_jar", "attributes": { "mode": "release" } - } - } - } - }, - "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { - "general": { - "bzlTransitiveDigest": "AARGgu9wwVVVg2ko+pKNhSOGTtOh12+1knTwjHuxpjo=", - "usagesDigest": "qgOPHTIJehHE7C5edcqknU9pqU6zUuSV2IGyzjt0tf8=", - "recordedInputs": [ - "REPO_MAPPING:sameri11_rules_flutter_core+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms" - ], - "generatedRepoSpecs": { - "flutter_sdk": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_sdk", + }, + "flutter_embedding_debug": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_embedding_jar", "attributes": { - "version": "3.47.3" + "mode": "debug" } }, - "flutter_tools_packages": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_tools_packages", - "attributes": {} - }, "flutter_engine_arm64_v8a_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "arm64_v8a_release", - "extract_libs": true + "artifact": "arm64_v8a_release" } }, "flutter_engine_arm64_v8a_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "arm64_v8a_debug", - "extract_libs": true + "artifact": "arm64_v8a_debug" } }, "flutter_engine_x86_64_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "x86_64_release", - "extract_libs": true + "artifact": "x86_64_release" } }, "flutter_engine_x86_64_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "x86_64_debug", - "extract_libs": true + "artifact": "x86_64_debug" } }, "flutter_engine_armeabi_v7a_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "armeabi_v7a_release", - "extract_libs": true + "artifact": "armeabi_v7a_release" } }, "flutter_engine_armeabi_v7a_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", + "attributes": { + "artifact": "armeabi_v7a_debug" + } + } + } + } + }, + "@@sameri11_rules_flutter_android+//flutter/private:target_toolchain_repo.bzl%flutter_android_toolchains": { + "general": { + "bzlTransitiveDigest": "0D9Q8Q4AQrFirqM6wLGtrv3QlasjcQ2c2i2OxhVYFfI=", + "usagesDigest": "2WhXsIhHLINViYFxmWfqIy1rlYxnWFK11LB9vSRc2M0=", + "recordedInputs": [ + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_gen_snapshot_arm64_v8a sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_arm64_v8a", + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_gen_snapshot_x86_64 sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_x86_64", + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_gen_snapshot_armeabi_v7a sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a", + "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+" + ], + "generatedRepoSpecs": { + "flutter_gen_snapshot_arm64_v8a": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", "attributes": { - "artifact": "armeabi_v7a_debug", - "extract_libs": true + "engine_dir": "android-arm64-release" } + }, + "flutter_gen_snapshot_x86_64": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-x64-release" + } + }, + "flutter_gen_snapshot_armeabi_v7a": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-arm-release" + } + }, + "flutter_android_target_toolchains": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:target_toolchain_repo.bzl%_android_toolchains", + "attributes": { + "aot_toolchain_targets": { + "arm64-v8a": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_arm64_v8a//:aot_toolchain'", + "x86_64": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_x86_64//:aot_toolchain'", + "armeabi-v7a": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a//:aot_toolchain'" + }, + "aot_toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:aot_toolchain_type'", + "toolchain_targets": { + "arm64-v8a": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_arm64_v8a'", + "x86_64": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_x86_64'", + "armeabi-v7a": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_armeabi_v7a'" + }, + "toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:target_toolchain_type'" + } + } + } + } + }, + "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { + "general": { + "bzlTransitiveDigest": "Et4GM1IW3LCV7ah8t1qEeCgh9cOh1ek26YeJOVdYI+0=", + "usagesDigest": "qgOPHTIJehHE7C5edcqknU9pqU6zUuSV2IGyzjt0tf8=", + "recordedInputs": [], + "generatedRepoSpecs": { + "flutter_sdk": { + "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_sdk", + "attributes": { + "version": "3.47.3" + } + }, + "flutter_tools_packages": { + "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_tools_packages", + "attributes": {} } } } diff --git a/examples/pub_plugins/BUILD.bazel b/examples/pub_plugins/BUILD.bazel index 612df57c..cad015e2 100644 --- a/examples/pub_plugins/BUILD.bazel +++ b/examples/pub_plugins/BUILD.bazel @@ -1,13 +1,13 @@ # Dart targets for the flat pub-plugin example. -load("@rules_flutter_android//flutter:defs.bzl", "flutter_plugin_deps_guards") -load("@rules_flutter_core//flutter:defs.bzl", "flutter_core", "flutter_integration_test", "flutter_test_suite") +load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_core", "flutter_plugin_deps_guards") +load("@rules_flutter_core//flutter:defs.bzl", "flutter_integration_test", "flutter_test_suite") package(default_visibility = ["//visibility:public"]) flutter_plugin_deps_guards(plugins = "@flutter_plugins") -flutter_core( +flutter_android_core( name = "app", assets = [], dart_plugin_registrant = "lib/dart_plugin_registrant.dart", diff --git a/examples/pub_plugins/MODULE.bazel.lock b/examples/pub_plugins/MODULE.bazel.lock index 75b3b663..a60cd2aa 100644 --- a/examples/pub_plugins/MODULE.bazel.lock +++ b/examples/pub_plugins/MODULE.bazel.lock @@ -409,13 +409,12 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%android_ndk": { "general": { - "bzlTransitiveDigest": "ChmCXXuZkbfT0XtrGZ3VW6ftH+vEyMOroOzRtbHokuY=", + "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", "usagesDigest": "CWMaitCzriuNa8mQm9VlXo9SoC7z68RHk02zkINH09A=", "recordedInputs": [ + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android rules_android+", - "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android_ndk rules_android_ndk+", - "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms" + "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android_ndk rules_android_ndk+" ], "generatedRepoSpecs": { "androidndk": { @@ -431,15 +430,15 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%flutter_plugins_ext": { "general": { - "bzlTransitiveDigest": "ChmCXXuZkbfT0XtrGZ3VW6ftH+vEyMOroOzRtbHokuY=", + "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", "usagesDigest": "mW8ky7T5PDBsdwo6w2Q5gOopJ+4T57XXK8sGug9bSvc=", "recordedInputs": [ "REPO_MAPPING:,flutter_maven rules_jvm_external++maven+flutter_maven", "REPO_MAPPING:,pub sameri11_rules_flutter_core++pub+pub", + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android rules_android+", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android_ndk rules_android_ndk+", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms", "REPO_MAPPING:sameri11_rules_flutter_android+,androidndk_cmake sameri11_rules_flutter_android++android_ndk+androidndk_cmake" ], "generatedRepoSpecs": { @@ -472,87 +471,131 @@ }, "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_embedding": { "general": { - "bzlTransitiveDigest": "Cc2IsXwVitTCSUMnh/0e+uh1l6r143p3g2wxU9QzBGg=", + "bzlTransitiveDigest": "3eqHOPboGukDJg90t58rNh9rmebP4fSAVaS1+Xd+5d4=", "usagesDigest": "VDubd1CC/3EsTr4xz8tz3uu65g6E5r8qQxmJ5I7keKE=", "recordedInputs": [ - "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk" + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", + "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+" ], "generatedRepoSpecs": { - "flutter_embedding_debug": { - "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_embedding_jar", - "attributes": { - "mode": "debug" - } - }, "flutter_embedding_release": { "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_embedding_jar", "attributes": { "mode": "release" } - } - } - } - }, - "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { - "general": { - "bzlTransitiveDigest": "AARGgu9wwVVVg2ko+pKNhSOGTtOh12+1knTwjHuxpjo=", - "usagesDigest": "7g5j5szAjo79jAswLX0VYMWhMYvsF2r2WEYs8ccrN/k=", - "recordedInputs": [ - "REPO_MAPPING:sameri11_rules_flutter_core+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms" - ], - "generatedRepoSpecs": { - "flutter_sdk": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_sdk", + }, + "flutter_embedding_debug": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_embedding_jar", "attributes": { - "version": "3.47.3" + "mode": "debug" } }, - "flutter_tools_packages": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_tools_packages", - "attributes": {} - }, "flutter_engine_arm64_v8a_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "arm64_v8a_release", - "extract_libs": true + "artifact": "arm64_v8a_release" } }, "flutter_engine_arm64_v8a_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "arm64_v8a_debug", - "extract_libs": true + "artifact": "arm64_v8a_debug" } }, "flutter_engine_x86_64_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "x86_64_release", - "extract_libs": true + "artifact": "x86_64_release" } }, "flutter_engine_x86_64_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "x86_64_debug", - "extract_libs": true + "artifact": "x86_64_debug" } }, "flutter_engine_armeabi_v7a_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "armeabi_v7a_release", - "extract_libs": true + "artifact": "armeabi_v7a_release" } }, "flutter_engine_armeabi_v7a_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", + "attributes": { + "artifact": "armeabi_v7a_debug" + } + } + } + } + }, + "@@sameri11_rules_flutter_android+//flutter/private:target_toolchain_repo.bzl%flutter_android_toolchains": { + "general": { + "bzlTransitiveDigest": "0D9Q8Q4AQrFirqM6wLGtrv3QlasjcQ2c2i2OxhVYFfI=", + "usagesDigest": "2WhXsIhHLINViYFxmWfqIy1rlYxnWFK11LB9vSRc2M0=", + "recordedInputs": [ + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_gen_snapshot_arm64_v8a sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_arm64_v8a", + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_gen_snapshot_x86_64 sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_x86_64", + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_gen_snapshot_armeabi_v7a sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a", + "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+" + ], + "generatedRepoSpecs": { + "flutter_gen_snapshot_arm64_v8a": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-arm64-release" + } + }, + "flutter_gen_snapshot_x86_64": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-x64-release" + } + }, + "flutter_gen_snapshot_armeabi_v7a": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-arm-release" + } + }, + "flutter_android_target_toolchains": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:target_toolchain_repo.bzl%_android_toolchains", + "attributes": { + "aot_toolchain_targets": { + "arm64-v8a": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_arm64_v8a//:aot_toolchain'", + "x86_64": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_x86_64//:aot_toolchain'", + "armeabi-v7a": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a//:aot_toolchain'" + }, + "aot_toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:aot_toolchain_type'", + "toolchain_targets": { + "arm64-v8a": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_arm64_v8a'", + "x86_64": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_x86_64'", + "armeabi-v7a": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_armeabi_v7a'" + }, + "toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:target_toolchain_type'" + } + } + } + } + }, + "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { + "general": { + "bzlTransitiveDigest": "Et4GM1IW3LCV7ah8t1qEeCgh9cOh1ek26YeJOVdYI+0=", + "usagesDigest": "7g5j5szAjo79jAswLX0VYMWhMYvsF2r2WEYs8ccrN/k=", + "recordedInputs": [], + "generatedRepoSpecs": { + "flutter_sdk": { + "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_sdk", "attributes": { - "artifact": "armeabi_v7a_debug", - "extract_libs": true + "version": "3.47.3" } + }, + "flutter_tools_packages": { + "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_tools_packages", + "attributes": {} } } } diff --git a/rules_flutter_android/MODULE.bazel b/rules_flutter_android/MODULE.bazel index e68965ad..e8b8e69d 100644 --- a/rules_flutter_android/MODULE.bazel +++ b/rules_flutter_android/MODULE.bazel @@ -15,7 +15,29 @@ flutter = use_extension("@rules_flutter_core//flutter:extensions.bzl", "flutter" use_repo(flutter, "flutter_sdk") flutter_android_embedding = use_extension("//flutter/private:embedding_repo.bzl", "flutter_android_embedding") -use_repo(flutter_android_embedding, "flutter_embedding_debug", "flutter_embedding_release") +use_repo( + flutter_android_embedding, + "flutter_embedding_debug", + "flutter_embedding_release", + "flutter_engine_arm64_v8a_debug", + "flutter_engine_arm64_v8a_release", + "flutter_engine_armeabi_v7a_debug", + "flutter_engine_armeabi_v7a_release", + "flutter_engine_x86_64_debug", + "flutter_engine_x86_64_release", +) + +flutter_android_toolchains = use_extension("//flutter/private:target_toolchain_repo.bzl", "flutter_android_toolchains") +use_repo(flutter_android_toolchains, "flutter_android_target_toolchains") + +register_toolchains("@flutter_android_target_toolchains//:all") + +use_repo( + flutter_android_toolchains, + "flutter_gen_snapshot_arm64_v8a", + "flutter_gen_snapshot_armeabi_v7a", + "flutter_gen_snapshot_x86_64", +) # Pin for developing this repository as the root module. A dev dependency, so # consumers ignore it, as they ignore core's own pin. diff --git a/rules_flutter_android/MODULE.bazel.lock b/rules_flutter_android/MODULE.bazel.lock index 393edd5a..18380dea 100644 --- a/rules_flutter_android/MODULE.bazel.lock +++ b/rules_flutter_android/MODULE.bazel.lock @@ -300,13 +300,12 @@ "moduleExtensions": { "//flutter:extensions.bzl%android_ndk": { "general": { - "bzlTransitiveDigest": "ChmCXXuZkbfT0XtrGZ3VW6ftH+vEyMOroOzRtbHokuY=", + "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", "usagesDigest": "BSiKVNTHy3EXCSxrVE2lcJp20b5h/Z0f4xjsXF45WZ0=", "recordedInputs": [ + "REPO_MAPPING:,platforms platforms", "REPO_MAPPING:,rules_android rules_android+", - "REPO_MAPPING:,rules_android_ndk rules_android_ndk+", - "REPO_MAPPING:,rules_flutter_core sameri11_rules_flutter_core+", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms" + "REPO_MAPPING:,rules_android_ndk rules_android_ndk+" ], "generatedRepoSpecs": { "androidndk": { @@ -322,10 +321,11 @@ }, "//flutter:extensions.bzl%flutter_plugins_ext": { "general": { - "bzlTransitiveDigest": "ChmCXXuZkbfT0XtrGZ3VW6ftH+vEyMOroOzRtbHokuY=", + "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", "usagesDigest": "9owEpU8N8w2F4asd++mkiTNLiSzbTw8JI0PGfMv4MyM=", "recordedInputs": [ "REPO_MAPPING:,parity_maven rules_jvm_external++maven+parity_maven", + "REPO_MAPPING:,platforms platforms", "REPO_MAPPING:,registrant_parity_federated_default sameri11_rules_flutter_core++pub+registrant_parity_federated_default", "REPO_MAPPING:,registrant_parity_federated_dev_override sameri11_rules_flutter_core++pub+registrant_parity_federated_dev_override", "REPO_MAPPING:,registrant_parity_federated_direct sameri11_rules_flutter_core++pub+registrant_parity_federated_direct", @@ -334,7 +334,6 @@ "REPO_MAPPING:,rules_android rules_android+", "REPO_MAPPING:,rules_android_ndk rules_android_ndk+", "REPO_MAPPING:,rules_flutter_core sameri11_rules_flutter_core+", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms", "REPO_MAPPING:,androidndk_cmake +android_ndk+androidndk_cmake" ], "generatedRepoSpecs": { @@ -463,22 +462,111 @@ }, "//flutter/private:embedding_repo.bzl%flutter_android_embedding": { "general": { - "bzlTransitiveDigest": "Cc2IsXwVitTCSUMnh/0e+uh1l6r143p3g2wxU9QzBGg=", + "bzlTransitiveDigest": "3eqHOPboGukDJg90t58rNh9rmebP4fSAVaS1+Xd+5d4=", "usagesDigest": "JpFePUNenAgojvGWTEykSWOR1tW+yKsbSwtNkwh8Zis=", "recordedInputs": [ - "REPO_MAPPING:,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk" + "REPO_MAPPING:,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", + "REPO_MAPPING:,platforms platforms", + "REPO_MAPPING:,rules_flutter_core sameri11_rules_flutter_core+" ], "generatedRepoSpecs": { + "flutter_embedding_release": { + "repoRuleId": "@@//flutter/private:embedding_repo.bzl%flutter_embedding_jar", + "attributes": { + "mode": "release" + } + }, "flutter_embedding_debug": { "repoRuleId": "@@//flutter/private:embedding_repo.bzl%flutter_embedding_jar", "attributes": { "mode": "debug" } }, - "flutter_embedding_release": { - "repoRuleId": "@@//flutter/private:embedding_repo.bzl%flutter_embedding_jar", + "flutter_engine_arm64_v8a_release": { + "repoRuleId": "@@//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "mode": "release" + "artifact": "arm64_v8a_release" + } + }, + "flutter_engine_arm64_v8a_debug": { + "repoRuleId": "@@//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", + "attributes": { + "artifact": "arm64_v8a_debug" + } + }, + "flutter_engine_x86_64_release": { + "repoRuleId": "@@//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", + "attributes": { + "artifact": "x86_64_release" + } + }, + "flutter_engine_x86_64_debug": { + "repoRuleId": "@@//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", + "attributes": { + "artifact": "x86_64_debug" + } + }, + "flutter_engine_armeabi_v7a_release": { + "repoRuleId": "@@//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", + "attributes": { + "artifact": "armeabi_v7a_release" + } + }, + "flutter_engine_armeabi_v7a_debug": { + "repoRuleId": "@@//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", + "attributes": { + "artifact": "armeabi_v7a_debug" + } + } + } + } + }, + "//flutter/private:target_toolchain_repo.bzl%flutter_android_toolchains": { + "general": { + "bzlTransitiveDigest": "0D9Q8Q4AQrFirqM6wLGtrv3QlasjcQ2c2i2OxhVYFfI=", + "usagesDigest": "X0W+UJfnyCDYkAN+wssgobE3kRonFYUkhzx27KR7fCI=", + "recordedInputs": [ + "REPO_MAPPING:,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", + "REPO_MAPPING:,platforms platforms", + "REPO_MAPPING:,flutter_gen_snapshot_arm64_v8a +flutter_android_toolchains+flutter_gen_snapshot_arm64_v8a", + "REPO_MAPPING:,flutter_gen_snapshot_x86_64 +flutter_android_toolchains+flutter_gen_snapshot_x86_64", + "REPO_MAPPING:,flutter_gen_snapshot_armeabi_v7a +flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a", + "REPO_MAPPING:,rules_flutter_core sameri11_rules_flutter_core+" + ], + "generatedRepoSpecs": { + "flutter_gen_snapshot_arm64_v8a": { + "repoRuleId": "@@//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-arm64-release" + } + }, + "flutter_gen_snapshot_x86_64": { + "repoRuleId": "@@//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-x64-release" + } + }, + "flutter_gen_snapshot_armeabi_v7a": { + "repoRuleId": "@@//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-arm-release" + } + }, + "flutter_android_target_toolchains": { + "repoRuleId": "@@//flutter/private:target_toolchain_repo.bzl%_android_toolchains", + "attributes": { + "aot_toolchain_targets": { + "arm64-v8a": "'@@+flutter_android_toolchains+flutter_gen_snapshot_arm64_v8a//:aot_toolchain'", + "x86_64": "'@@+flutter_android_toolchains+flutter_gen_snapshot_x86_64//:aot_toolchain'", + "armeabi-v7a": "'@@+flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a//:aot_toolchain'" + }, + "aot_toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:aot_toolchain_type'", + "toolchain_targets": { + "arm64-v8a": "'@@//flutter:target_toolchain_arm64_v8a'", + "x86_64": "'@@//flutter:target_toolchain_x86_64'", + "armeabi-v7a": "'@@//flutter:target_toolchain_armeabi_v7a'" + }, + "toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:target_toolchain_type'" } } } @@ -593,12 +681,9 @@ }, "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { "general": { - "bzlTransitiveDigest": "AARGgu9wwVVVg2ko+pKNhSOGTtOh12+1knTwjHuxpjo=", + "bzlTransitiveDigest": "Et4GM1IW3LCV7ah8t1qEeCgh9cOh1ek26YeJOVdYI+0=", "usagesDigest": "yrcrs3q9AyVzH00WG98mD1OpGXB4f6VMs6fbGNdHd28=", - "recordedInputs": [ - "REPO_MAPPING:sameri11_rules_flutter_core+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms" - ], + "recordedInputs": [], "generatedRepoSpecs": { "flutter_sdk": { "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_sdk", @@ -609,48 +694,6 @@ "flutter_tools_packages": { "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_tools_packages", "attributes": {} - }, - "flutter_engine_arm64_v8a_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "arm64_v8a_release", - "extract_libs": true - } - }, - "flutter_engine_arm64_v8a_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "arm64_v8a_debug", - "extract_libs": true - } - }, - "flutter_engine_x86_64_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "x86_64_release", - "extract_libs": true - } - }, - "flutter_engine_x86_64_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "x86_64_debug", - "extract_libs": true - } - }, - "flutter_engine_armeabi_v7a_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "armeabi_v7a_release", - "extract_libs": true - } - }, - "flutter_engine_armeabi_v7a_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "armeabi_v7a_debug", - "extract_libs": true - } } } } diff --git a/rules_flutter_android/flutter/BUILD.bazel b/rules_flutter_android/flutter/BUILD.bazel index 7190b29b..073f6829 100644 --- a/rules_flutter_android/flutter/BUILD.bazel +++ b/rules_flutter_android/flutter/BUILD.bazel @@ -1,10 +1,12 @@ load("//flutter:native_build_env.bzl", "native_build_env") +load("//flutter/private:target_toolchains.bzl", "flutter_android_target_toolchains") package(default_visibility = ["//visibility:public"]) exports_files([ "native_build_env.bzl", "composition.bzl", + "android_core.bzl", ]) filegroup( @@ -32,6 +34,105 @@ filegroup( }), ) +config_setting( + name = "engine_split_arm64_v8a_release", + constraint_values = [ + "@platforms//os:android", + "@platforms//cpu:arm64", + ], + flag_values = {"@rules_flutter_core//flutter:mode": "release"}, +) + +config_setting( + name = "engine_split_arm64_v8a_debug", + constraint_values = [ + "@platforms//os:android", + "@platforms//cpu:arm64", + ], + flag_values = {"@rules_flutter_core//flutter:mode": "debug"}, +) + +config_setting( + name = "engine_split_x86_64_release", + constraint_values = [ + "@platforms//os:android", + "@platforms//cpu:x86_64", + ], + flag_values = {"@rules_flutter_core//flutter:mode": "release"}, +) + +config_setting( + name = "engine_split_x86_64_debug", + constraint_values = [ + "@platforms//os:android", + "@platforms//cpu:x86_64", + ], + flag_values = {"@rules_flutter_core//flutter:mode": "debug"}, +) + +config_setting( + name = "engine_split_armeabi_v7a_release", + constraint_values = [ + "@platforms//os:android", + "@platforms//cpu:armv7", + ], + flag_values = {"@rules_flutter_core//flutter:mode": "release"}, +) + +config_setting( + name = "engine_split_armeabi_v7a_debug", + constraint_values = [ + "@platforms//os:android", + "@platforms//cpu:armv7", + ], + flag_values = {"@rules_flutter_core//flutter:mode": "debug"}, +) + +filegroup( + name = "engine_libs_arm64_v8a_release", + srcs = ["@flutter_engine_arm64_v8a_release//jar:libs"], +) + +filegroup( + name = "engine_libs_arm64_v8a_debug", + srcs = ["@flutter_engine_arm64_v8a_debug//jar:libs"], +) + +filegroup( + name = "engine_libs_x86_64_release", + srcs = ["@flutter_engine_x86_64_release//jar:libs"], +) + +filegroup( + name = "engine_libs_x86_64_debug", + srcs = ["@flutter_engine_x86_64_debug//jar:libs"], +) + +filegroup( + name = "engine_libs_armeabi_v7a_release", + srcs = ["@flutter_engine_armeabi_v7a_release//jar:libs"], +) + +filegroup( + name = "engine_libs_armeabi_v7a_debug", + srcs = ["@flutter_engine_armeabi_v7a_debug//jar:libs"], +) + +filegroup( + name = "engine_libs", + srcs = select({ + ":engine_split_arm64_v8a_release": [":engine_libs_arm64_v8a_release"], + ":engine_split_arm64_v8a_debug": [":engine_libs_arm64_v8a_debug"], + ":engine_split_x86_64_release": [":engine_libs_x86_64_release"], + ":engine_split_x86_64_debug": [":engine_libs_x86_64_debug"], + ":engine_split_armeabi_v7a_release": [":engine_libs_armeabi_v7a_release"], + ":engine_split_armeabi_v7a_debug": [":engine_libs_armeabi_v7a_debug"], + "//conditions:default": [], + }), +) + # The Android SDK a native build hook reads. Pass this to a flutter_core call # explicitly when package hooks need the SDK. native_build_env(name = "native_build_env") + +flutter_android_target_toolchains() diff --git a/rules_flutter_android/flutter/abis.bzl b/rules_flutter_android/flutter/abis.bzl new file mode 100644 index 00000000..464b7e62 --- /dev/null +++ b/rules_flutter_android/flutter/abis.bzl @@ -0,0 +1,77 @@ +"""Public Android ABI names and the target data mapped from each one. + +The ABI spelling is the name consumers use and the name that appears in APKs. +""" + +visibility("public") + +# Android CPUs recognized but not supported by Flutter's cached engine. +_UNSUPPORTED_ANDROID_CPU_CONSTRAINTS = { + Label("@platforms//cpu:x86_32"): "x86", + Label("@platforms//cpu:riscv64"): "riscv64", +} + +def _abi(target_platform, engine_dir, maven_artifact, manifest_key, cpu_constraint, snapshot_flags = []): + return struct( + target_platform = target_platform, + engine_dir = engine_dir, + maven_artifact = maven_artifact, + manifest_key = manifest_key, + cpu_constraint = cpu_constraint, + snapshot_flags = snapshot_flags, + ) + +ABIS = { + "arm64-v8a": _abi( + target_platform = "android-arm64", + engine_dir = {"release": "android-arm64-release"}, + maven_artifact = {"release": "arm64_v8a_release", "debug": "arm64_v8a_debug"}, + manifest_key = "android_arm64", + cpu_constraint = Label("@platforms//cpu:arm64"), + ), + "x86_64": _abi( + target_platform = "android-x64", + engine_dir = {"release": "android-x64-release"}, + maven_artifact = {"release": "x86_64_release", "debug": "x86_64_debug"}, + manifest_key = "android_x64", + cpu_constraint = Label("@platforms//cpu:x86_64"), + ), + "armeabi-v7a": _abi( + target_platform = "android-arm", + engine_dir = {"release": "android-arm-release"}, + maven_artifact = {"release": "armeabi_v7a_release", "debug": "armeabi_v7a_debug"}, + manifest_key = "android_arm", + cpu_constraint = Label("@platforms//cpu:armv7"), + snapshot_flags = ["--no-sim-use-hardfp", "--no-use-integer-division"], + ), +} + +def _abi_cpu_constraints(): + constraints = {entry.cpu_constraint: abi for abi, entry in ABIS.items()} + for constraint, name in _UNSUPPORTED_ANDROID_CPU_CONSTRAINTS.items(): + if constraint in constraints: + fail("ABIS and unsupported Android CPUs both name {}.".format(constraint)) + constraints[constraint] = name + return constraints + +ABI_PLATFORM_ATTRS = { + "_cpu_constraints": attr.label_keyed_string_dict(default = _abi_cpu_constraints()), + "_android_os": attr.label(default = Label("@platforms//os:android")), +} + +def abi_from_platform(ctx): + """Return the supported/unsupported Android ABI name or None for other OSes. + + Args: + ctx: The rule context whose target platform is being inspected. + + Returns: + The ABI name, or None when the platform is not a supported Android ABI. + """ + android = ctx.attr._android_os[platform_common.ConstraintValueInfo] + if not ctx.target_platform_has_constraint(android): + return None + for target, name in ctx.attr._cpu_constraints.items(): + if ctx.target_platform_has_constraint(target[platform_common.ConstraintValueInfo]): + return name + return None diff --git a/rules_flutter_android/flutter/android_core.bzl b/rules_flutter_android/flutter/android_core.bzl new file mode 100644 index 00000000..c48d224f --- /dev/null +++ b/rules_flutter_android/flutter/android_core.bzl @@ -0,0 +1,12 @@ +"""Android facade that supplies ABI data to the platform-neutral core macro.""" + +load("@rules_flutter_core//flutter:defs.bzl", _flutter_core = "flutter_core") +load("//flutter:abis.bzl", "ABIS") +load("//flutter/private:abis.bzl", "bazel_platform") + +def flutter_android_core(**kwargs): + _flutter_core( + android_target_platforms = [bazel_platform(abi) for abi in ABIS], + aot_compatible_with = [Label("@platforms//os:android")], + **kwargs + ) diff --git a/rules_flutter_android/flutter/defs.bzl b/rules_flutter_android/flutter/defs.bzl index 4df154bc..3f8c7cd5 100644 --- a/rules_flutter_android/flutter/defs.bzl +++ b/rules_flutter_android/flutter/defs.bzl @@ -1,6 +1,7 @@ """Supported convenience BUILD API for Flutter Android consumer modules.""" load("@rules_flutter_core//flutter:composition.bzl", _core_flutter_assets = "flutter_assets") +load("//flutter:android_core.bzl", _flutter_android_core = "flutter_android_core") load("//flutter/private:android_app.bzl", _FLUTTER_DEBUG_UNCOMPRESSED_ASSETS = "FLUTTER_DEBUG_UNCOMPRESSED_ASSETS", _flutter_android_app = "flutter_android_app") load("//flutter/private:plugin_deps_guards.bzl", _flutter_plugin_deps_guards = "flutter_plugin_deps_guards") load("//flutter/private:recipe.bzl", _flutter_native_contribution = "flutter_native_contribution") @@ -12,6 +13,8 @@ def flutter_assets(native_build_env = _ANDROID_NATIVE_BUILD_ENV, **kwargs): _core_flutter_assets(native_build_env = native_build_env, **kwargs) flutter_android_app = _flutter_android_app + +flutter_android_core = _flutter_android_core flutter_plugin_deps_guards = _flutter_plugin_deps_guards flutter_native_contribution = _flutter_native_contribution FLUTTER_DEBUG_UNCOMPRESSED_ASSETS = _FLUTTER_DEBUG_UNCOMPRESSED_ASSETS diff --git a/rules_flutter_android/flutter/private/abis.bzl b/rules_flutter_android/flutter/private/abis.bzl index c8f0acb0..a5b7e577 100644 --- a/rules_flutter_android/flutter/private/abis.bzl +++ b/rules_flutter_android/flutter/private/abis.bzl @@ -1,18 +1,9 @@ -"""Android-only facts for each ABI, keyed by the names in the public ABI table. +"""Android-only toolchain and SDK facts.""" -The table itself is public in `@rules_flutter_core//flutter:abis.bzl`. What only -the Android rules need lives here: the rules_android platform that selects an -ABI's NDK toolchain, the minimum SDK, and the names of the plugin repository's -per-ABI aggregates. -""" - -load("@rules_flutter_core//flutter:abis.bzl", "ABIS", "abi_from_platform") +load("//flutter:abis.bzl", "ABIS", "abi_from_platform") visibility(["//flutter/...", "//tests/..."]) -# The platform rules_android packages an ABI's native libraries under. The name -# must equal the ABI: rules_android writes them to lib//, so a -# mismatch would place this ABI's libraries under another ABI's directory. _BAZEL_PLATFORMS = { "arm64-v8a": "@rules_android//:arm64-v8a", "x86_64": "@rules_android//:x86_64", @@ -42,65 +33,45 @@ _ = _check_bazel_platforms() def bazel_platform(abi): """The rules_android platform label that selects one ABI's toolchain.""" - return _BAZEL_PLATFORMS[abi] + return Label(_BAZEL_PLATFORMS[abi]) -# The floor these rules ask for. rules_android applies its own floor during -# resource processing and clamps manifest_values against it, so an APK can -# declare a higher minimum than this. MIN_SDK = 21 def check_abis(abis, where): - """Fails with the supported set listed, rather than a KeyError. + """Validate a non-empty ABI list against the supported Android ABI set. Args: - abis: the ABI names a caller asked for. - where: what to name in the failure, usually the calling target. + abis: ABI names requested by the caller. + where: Context label used to identify validation failures. """ if not abis: - fail("{}: `abis` must name at least one ABI, one of {}.".format( - where, - sorted(ABIS), - )) + fail("{}: `abis` must name at least one ABI, one of {}.".format(where, sorted(ABIS))) for abi in abis: if abi not in ABIS: - fail("{}: unknown ABI '{}'. Supported: {}.".format( - where, - abi, - sorted(ABIS), - )) + fail("{}: unknown ABI '{}'. Supported: {}.".format(where, abi, sorted(ABIS))) def check_platform_abi(ctx): - """Verify that a platform transition selected the platform for `ctx.attr.abi`. - - Only valid on rules that transition `--platforms` from `abi`; returns - `ctx.attr.abi`. + """Check a rule's declared ABI against its configured Android platform. Args: - ctx: the rule context; must carry ABI_PLATFORM_ATTRS and an `abi` attribute. + ctx: The rule context with the declared platform and ABI. Returns: - `ctx.attr.abi`, after verifying it against the target platform. + The verified ABI name. """ found = abi_from_platform(ctx) if found == None: - fail(( - "{}: target platform '{}' is not Android, or names no CPU " + - "constraint this ruleset recognizes, so it cannot be checked " + - "against abi = '{}'. This rule transitions --platforms from its " + - "own `abi` attribute; if that transition is ever removed, this " + - "check should go with it." - ).format(ctx.label, ctx.attr.platform, ctx.attr.abi)) + fail("{}: target platform '{}' is not Android, or names no CPU.".format(ctx.label, ctx.attr.platform)) if found not in ABIS: - fail(( - "{}: target platform '{}' is Android/{}, which this ruleset does " + - "not support (abi = '{}'). Supported: {}." - ).format(ctx.label, ctx.attr.platform, found, ctx.attr.abi, sorted(ABIS))) + fail("{}: target platform '{}' is Android/{}, which this ruleset does not support (abi = '{}'). Supported: {}.".format( + ctx.label, + ctx.attr.platform, + found, + ctx.attr.abi, + sorted(ABIS), + )) if found != ctx.attr.abi: - fail(( - "{}: abi = '{}' but target platform '{}' carries the CPU " + - "constraint for '{}'. ABIS['{}'].cpu_constraint = {} names the " + - "wrong ABI." - ).format( + fail("{}: abi = '{}' but target platform '{}' carries the CPU constraint for '{}'. ABIS['{}'].cpu_constraint = {} names the wrong ABI.".format( ctx.label, ctx.attr.abi, ctx.attr.platform, diff --git a/rules_flutter_android/flutter/private/android_inputs_rules.bzl b/rules_flutter_android/flutter/private/android_inputs_rules.bzl index cb975ab7..8d4aabad 100644 --- a/rules_flutter_android/flutter/private/android_inputs_rules.bzl +++ b/rules_flutter_android/flutter/private/android_inputs_rules.bzl @@ -1,14 +1,10 @@ """The Android inputs of a flutter_core artifact, one split per Android ABI. `flutter_android_inputs` declares the android_library a consumer lists in its -android_binary deps. Each configured split reads its ABI from the Bazel platform -it is built for, takes that ABI's engine and AOT libraries from the core, and -takes the plugin repository's libraries for that ABI. The libraries reach the -APK through CcInfo, not through a jar the app wraps. - -A split built without an Android platform still analyses. It links a placeholder -library whose action fails, so the failure comes when an APK asks for the split, -not when the build graph is loaded. +android_binary deps. Each configured split reads its ABI from the Bazel platform, +takes the AOT library from core, the engine libraries from this ruleset's +Android-owned selection, and the plugin repository's libraries for that ABI. +The libraries reach the APK through CcInfo, not through a jar the app wraps. """ load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") @@ -16,9 +12,9 @@ load("@rules_android//rules:rules.bzl", "android_library") load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cpp_toolchain", "use_cc_toolchain") load("@rules_cc//cc/common:cc_common.bzl", "cc_common") load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") -load("@rules_flutter_core//flutter:abis.bzl", "ABIS", "ABI_PLATFORM_ATTRS", "abi_from_platform") load("@rules_flutter_core//flutter:composition.bzl", "FlutterArtifactInfo") load("@rules_java//java:defs.bzl", "java_import") +load("//flutter:abis.bzl", "ABIS", "ABI_PLATFORM_ATTRS", "abi_from_platform") load(":android.bzl", "flutter_android_assets") load(":embedding.bzl", "ANNOTATION_JVM", "flutter_embedding_deps") load(":maven_identity.bzl", "check_flutter_maven_repository", "flutter_maven_repository") @@ -113,7 +109,7 @@ def _split_native_impl(ctx): # The engine is stripped here, as the engine jar was, so the APK carries # the same bytes the jar carried. - for f in info.engine_libs: + for f in ctx.files.engine_libs: stripped = ctx.actions.declare_file("{}/{}".format(ctx.label.name, f.basename)) ctx.actions.run( executable = cc_toolchain.strip_executable, @@ -167,6 +163,11 @@ platform. The ABI comes from that platform.""", providers = [FlutterArtifactInfo], doc = "The flutter_core target, configured for this split.", ), + "engine_libs": attr.label_list( + allow_files = True, + default = [Label("//flutter:engine_libs")], + doc = "The Android ruleset's ABI-and-mode-selected engine libraries.", + ), "native_files": attr.label( providers = [FlutterNativeFilesByAbiInfo], doc = "The plugin repository's flutter_native_files_by_abi. None for an app without plugins.", diff --git a/rules_flutter_android/flutter/private/apk_check.bzl b/rules_flutter_android/flutter/private/apk_check.bzl index f7cc946d..46e3f24f 100644 --- a/rules_flutter_android/flutter/private/apk_check.bzl +++ b/rules_flutter_android/flutter/private/apk_check.bzl @@ -9,7 +9,7 @@ entries of the AARs and library archives the android_binary packages for that sp load("@rules_android//providers:providers.bzl", "AndroidBinaryNativeLibsInfo") load("@rules_android//rules:android_split_transition.bzl", "android_split_transition") -load("@rules_flutter_core//flutter:abis.bzl", "ABIS") +load("//flutter:abis.bzl", "ABIS") load(":android_inputs_rules.bzl", "FlutterSplitNativeInfo") _CHECKER = "//flutter/private:apk_check_tool" diff --git a/rules_flutter_android/flutter/private/embedding_repo.bzl b/rules_flutter_android/flutter/private/embedding_repo.bzl index fadc7268..3a293cb3 100644 --- a/rules_flutter_android/flutter/private/embedding_repo.bzl +++ b/rules_flutter_android/flutter/private/embedding_repo.bzl @@ -1,4 +1,7 @@ -"""Repositories for the Java embedding used by Android consumers.""" +"""Repositories for the Java embedding and Android engine artifacts.""" + +load("@rules_flutter_core//flutter:abis.bzl", "MODES") +load("//flutter:abis.bzl", "ABIS") visibility(["//:__pkg__"]) @@ -33,8 +36,50 @@ flutter_embedding_jar = repository_rule( }, ) +def _engine_impl(ctx): + version = json.decode(ctx.read(ctx.path(ctx.attr.version_file))) + artifact = ctx.attr.artifact + url = "{}/{}/1.0.0-{}/{}-1.0.0-{}.jar".format( + _BASE, + artifact, + version["engineRevision"], + artifact, + version["engineContentHash"], + ) + ctx.download(url = url, output = "jar/{}.jar".format(artifact), canonical_id = url) + ctx.extract(archive = ctx.path("jar/{}.jar".format(artifact)), output = "jar") + ctx.file("jar/BUILD.bazel", """filegroup( + name = "file", + srcs = ["{}.jar"], + visibility = ["//visibility:public"], +) + +filegroup( + name = "libs", + srcs = glob(["lib/**/*.so"], allow_empty = True), + visibility = ["//visibility:public"], +) +""".format(artifact)) + +flutter_android_engine_jar = repository_rule( + implementation = _engine_impl, + attrs = { + "artifact": attr.string(mandatory = True), + "version_file": attr.label( + allow_single_file = True, + default = Label("@flutter_sdk//:flutter.version.json"), + ), + }, +) + def _embedding_extension_impl(_ctx): - for mode in ["debug", "release"]: + for mode in MODES: flutter_embedding_jar(name = "flutter_embedding_" + mode, mode = mode) + for abi, info in ABIS.items(): + for mode in MODES: + flutter_android_engine_jar( + name = "flutter_engine_{}_{}".format(abi.replace("-", "_"), mode), + artifact = info.maven_artifact[mode], + ) flutter_android_embedding = module_extension(implementation = _embedding_extension_impl) diff --git a/rules_flutter_android/flutter/private/gen_snapshot_repo.bzl b/rules_flutter_android/flutter/private/gen_snapshot_repo.bzl new file mode 100644 index 00000000..064bec06 --- /dev/null +++ b/rules_flutter_android/flutter/private/gen_snapshot_repo.bzl @@ -0,0 +1,55 @@ +"""Repositories exposing cached Android gen_snapshot payloads per ABI.""" + +visibility(["//:__pkg__"]) + +def _snapshot_path(ctx): + version_file = ctx.path(ctx.attr.version_file).realpath + sdk_root = version_file.dirname.dirname.dirname + host_os = ctx.os.name.lower() + host_arch = { + "aarch64": "arm64", + "amd64": "x64", + "arm64": "arm64", + "riscv64": "riscv64", + "x86_64": "x64", + }.get(ctx.os.arch.lower()) + if host_arch == None: + fail("Unsupported host architecture for a Flutter build: '{}'.".format(ctx.os.arch)) + if host_os.startswith("mac os"): + host = "darwin-x64" + elif host_os.startswith("linux"): + host = "linux-" + host_arch + elif host_os.startswith("windows"): + host = "windows-" + host_arch + else: + fail("Unsupported host OS for a Flutter build: '{}'.".format(ctx.os.name)) + + # Flutter currently stores Android gen_snapshot in the x64 engine directory on macOS. + return sdk_root.get_child("bin/cache/artifacts/engine").get_child(ctx.attr.engine_dir).get_child(host).get_child("gen_snapshot") + +def _gen_snapshot_impl(ctx): + path = _snapshot_path(ctx) + if path.exists: + ctx.symlink(path, "gen_snapshot") + ctx.file("BUILD.bazel", """load("@rules_flutter_core//flutter:target_toolchain.bzl", "flutter_aot_toolchain") + +package(default_visibility = ["//visibility:public"]) + +exports_files(["gen_snapshot"]) + +flutter_aot_toolchain( + name = "aot_toolchain", + gen_snapshot_release = ":gen_snapshot", +) +""") + else: + fail("No cached Android gen_snapshot for {}. Run `flutter precache --android` to install the missing payload.".format(ctx.attr.engine_dir)) + +flutter_gen_snapshot = repository_rule( + implementation = _gen_snapshot_impl, + attrs = { + "engine_dir": attr.string(mandatory = True), + "version_file": attr.label(allow_single_file = True, default = Label("@flutter_sdk//:flutter.version.json")), + }, + local = True, +) diff --git a/rules_flutter_android/flutter/private/native_files.bzl b/rules_flutter_android/flutter/private/native_files.bzl index 35270969..d4c2c7e0 100644 --- a/rules_flutter_android/flutter/private/native_files.bzl +++ b/rules_flutter_android/flutter/private/native_files.bzl @@ -1,6 +1,6 @@ """Plugin native libraries as raw Files, one aggregate per Android ABI.""" -load("@rules_flutter_core//flutter:abis.bzl", "ABIS") +load("//flutter:abis.bzl", "ABIS") load(":recipe.bzl", "FlutterNativeInfo") visibility("public") diff --git a/rules_flutter_android/flutter/private/native_strip.bzl b/rules_flutter_android/flutter/private/native_strip.bzl index f99b799a..074a6447 100644 --- a/rules_flutter_android/flutter/private/native_strip.bzl +++ b/rules_flutter_android/flutter/private/native_strip.bzl @@ -1,7 +1,7 @@ """ABI-pinned raw native library stripping for consumer packaging.""" load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cpp_toolchain", "use_cc_toolchain") -load("@rules_flutter_core//flutter:abis.bzl", "ABI_PLATFORM_ATTRS") +load("//flutter:abis.bzl", "ABI_PLATFORM_ATTRS") load(":abis.bzl", "bazel_platform", "check_abis", "check_platform_abi") visibility("public") @@ -58,6 +58,6 @@ def android_native_files(name, src, abi, **kwargs): name = name, src = src, abi = abi, - platform = bazel_platform(abi), + platform = str(bazel_platform(abi)), **kwargs ) diff --git a/rules_flutter_android/flutter/private/target_toolchain_repo.bzl b/rules_flutter_android/flutter/private/target_toolchain_repo.bzl new file mode 100644 index 00000000..bbaaf0a6 --- /dev/null +++ b/rules_flutter_android/flutter/private/target_toolchain_repo.bzl @@ -0,0 +1,100 @@ +"""Registers Android target toolchains for the current execution host.""" + +load("//flutter:abis.bzl", "ABIS") +load(":gen_snapshot_repo.bzl", "flutter_gen_snapshot") + +visibility(["//"]) + +def _host_constraints(ctx): + name = ctx.os.name.lower() + if name.startswith("mac os"): + host_os = "macos" + elif name.startswith("linux"): + host_os = "linux" + elif name.startswith("windows"): + host_os = "windows" + else: + return None + + host_cpu = { + "aarch64": "arm64", + "amd64": "x86_64", + "arm64": "arm64", + "riscv64": "riscv64", + "x86_64": "x86_64", + }.get(ctx.os.arch.lower()) + if host_cpu == None: + return None + return ["@platforms//os:" + host_os, "@platforms//cpu:" + host_cpu] + +def _android_toolchains_impl(ctx): + host = _host_constraints(ctx) + if host == None: + ctx.file("BUILD.bazel", "# No Android Flutter target toolchains for this host.\n") + return + + lines = ['package(default_visibility = ["//visibility:public"])', ""] + cpu_constraints = { + "arm64-v8a": "arm64", + "armeabi-v7a": "armv7", + "x86_64": "x86_64", + } + + for abi in sorted(ctx.attr.toolchain_targets.keys()): + lines.extend([ + "toolchain(", + ' name = "flutter_android_' + abi.replace("-", "_") + '",', + ' toolchain = "' + ctx.attr.toolchain_targets[abi] + '",', + ' toolchain_type = "' + ctx.attr.toolchain_type + '",', + " exec_compatible_with = " + repr(host) + ",", + ' target_compatible_with = ["@platforms//os:android", "@platforms//cpu:' + cpu_constraints[abi] + '"],', + ")", + "", + ]) + + for abi in sorted(ctx.attr.aot_toolchain_targets.keys()): + lines.extend([ + "toolchain(", + ' name = "flutter_android_aot_' + abi.replace("-", "_") + '",', + ' toolchain = "' + ctx.attr.aot_toolchain_targets[abi] + '",', + ' toolchain_type = "' + ctx.attr.aot_toolchain_type + '",', + " exec_compatible_with = " + repr(host) + ",", + ' target_compatible_with = ["@platforms//os:android", "@platforms//cpu:' + cpu_constraints[abi] + '"],', + ")", + "", + ]) + ctx.file("BUILD.bazel", "\n".join(lines)) + +_android_toolchains = repository_rule( + implementation = _android_toolchains_impl, + attrs = { + "aot_toolchain_targets": attr.string_dict(mandatory = True), + "aot_toolchain_type": attr.string(mandatory = True), + "toolchain_targets": attr.string_dict(mandatory = True), + "toolchain_type": attr.string(mandatory = True), + }, +) + +def _android_toolchains_extension_impl(_ctx): + toolchain_targets = {} + aot_toolchain_targets = {} + for abi, info in ABIS.items(): + suffix = abi.replace("-", "_") + flutter_gen_snapshot( + name = "flutter_gen_snapshot_" + suffix, + engine_dir = info.engine_dir["release"], + ) + toolchain_targets[abi] = str(Label("//flutter:target_toolchain_" + suffix)) + aot_toolchain_targets[abi] = str(Label("@flutter_gen_snapshot_" + suffix + "//:aot_toolchain")) + + _android_toolchains( + name = "flutter_android_target_toolchains", + aot_toolchain_targets = aot_toolchain_targets, + aot_toolchain_type = str(Label("@rules_flutter_core//flutter:aot_toolchain_type")), + toolchain_targets = toolchain_targets, + toolchain_type = str(Label("@rules_flutter_core//flutter:target_toolchain_type")), + ) + +flutter_android_toolchains = module_extension( + implementation = _android_toolchains_extension_impl, +) diff --git a/rules_flutter_android/flutter/private/target_toolchains.bzl b/rules_flutter_android/flutter/private/target_toolchains.bzl new file mode 100644 index 00000000..c28a98e9 --- /dev/null +++ b/rules_flutter_android/flutter/private/target_toolchains.bzl @@ -0,0 +1,18 @@ +"""Declares the Android target toolchain implementations.""" + +load("@rules_flutter_core//flutter:target_toolchain.bzl", "flutter_target_toolchain") +load("//flutter:abis.bzl", "ABIS") + +visibility(["//flutter:__pkg__"]) + +def flutter_android_target_toolchains(): + for abi, info in ABIS.items(): + suffix = abi.replace("-", "_") + flutter_target_toolchain( + name = "target_toolchain_" + suffix, + target_os = "android", + snapshot_flags = info.snapshot_flags, + native_assets_key = info.manifest_key, + target_platform = info.target_platform, + slice_id = abi, + ) diff --git a/rules_flutter_core/MODULE.bazel b/rules_flutter_core/MODULE.bazel index 19d771f5..0d792364 100644 --- a/rules_flutter_core/MODULE.bazel +++ b/rules_flutter_core/MODULE.bazel @@ -2,9 +2,8 @@ module( name = "sameri11_rules_flutter_core", ) -# The rules' own .bzl files name @flutter_sdk and the engine repositories, so -# this module imports them. Which SDK version they hold is the root module's -# choice: the extension reads flutter.sdk only from the root module. +# The rules' own .bzl files name @flutter_sdk, so this module imports it. Which +# SDK version they hold is the root module's choice: the extension reads flutter.sdk only from the root module. flutter = use_extension("//flutter:extensions.bzl", "flutter") # Pin for developing this repository as the root module. A dev dependency, so @@ -33,12 +32,6 @@ test_metadata_parser_src( use_repo( flutter, - "flutter_engine_arm64_v8a_debug", - "flutter_engine_arm64_v8a_release", - "flutter_engine_armeabi_v7a_debug", - "flutter_engine_armeabi_v7a_release", - "flutter_engine_x86_64_debug", - "flutter_engine_x86_64_release", "flutter_sdk", "flutter_tools_packages", ) diff --git a/rules_flutter_core/MODULE.bazel.lock b/rules_flutter_core/MODULE.bazel.lock index 01e2bfdd..393cf1a9 100644 --- a/rules_flutter_core/MODULE.bazel.lock +++ b/rules_flutter_core/MODULE.bazel.lock @@ -193,12 +193,9 @@ "moduleExtensions": { "//flutter:extensions.bzl%flutter": { "general": { - "bzlTransitiveDigest": "AARGgu9wwVVVg2ko+pKNhSOGTtOh12+1knTwjHuxpjo=", + "bzlTransitiveDigest": "Et4GM1IW3LCV7ah8t1qEeCgh9cOh1ek26YeJOVdYI+0=", "usagesDigest": "3eQ7CSujwGo7HWistYKJNKsA65Je6fZw/a3upweGNLo=", - "recordedInputs": [ - "REPO_MAPPING:,flutter_sdk +flutter+flutter_sdk", - "REPO_MAPPING:,platforms platforms" - ], + "recordedInputs": [], "generatedRepoSpecs": { "flutter_sdk": { "repoRuleId": "@@//flutter/private:repo.bzl%flutter_sdk", @@ -209,48 +206,6 @@ "flutter_tools_packages": { "repoRuleId": "@@//flutter/private:repo.bzl%flutter_tools_packages", "attributes": {} - }, - "flutter_engine_arm64_v8a_release": { - "repoRuleId": "@@//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "arm64_v8a_release", - "extract_libs": true - } - }, - "flutter_engine_arm64_v8a_debug": { - "repoRuleId": "@@//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "arm64_v8a_debug", - "extract_libs": true - } - }, - "flutter_engine_x86_64_release": { - "repoRuleId": "@@//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "x86_64_release", - "extract_libs": true - } - }, - "flutter_engine_x86_64_debug": { - "repoRuleId": "@@//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "x86_64_debug", - "extract_libs": true - } - }, - "flutter_engine_armeabi_v7a_release": { - "repoRuleId": "@@//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "armeabi_v7a_release", - "extract_libs": true - } - }, - "flutter_engine_armeabi_v7a_debug": { - "repoRuleId": "@@//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "armeabi_v7a_debug", - "extract_libs": true - } } } } diff --git a/rules_flutter_core/flutter/BUILD.bazel b/rules_flutter_core/flutter/BUILD.bazel index ae4b4415..2cbf2d0a 100644 --- a/rules_flutter_core/flutter/BUILD.bazel +++ b/rules_flutter_core/flutter/BUILD.bazel @@ -17,6 +17,20 @@ alias( visibility = ["//visibility:public"], ) +alias( + name = "target_toolchain_type", + actual = "//flutter/private:flutter_target_toolchain_type", + visibility = ["//visibility:public"], +) + +alias( + name = "aot_toolchain_type", + actual = "//flutter/private:flutter_aot_toolchain_type", + visibility = ["//visibility:public"], +) + +exports_files(["target_toolchain.bzl"]) + # Flutter runtime mode, independent of --compilation_mode. string_flag( name = "mode", diff --git a/rules_flutter_core/flutter/abis.bzl b/rules_flutter_core/flutter/abis.bzl index 0cab67d0..6bdc999a 100644 --- a/rules_flutter_core/flutter/abis.bzl +++ b/rules_flutter_core/flutter/abis.bzl @@ -1,33 +1,7 @@ -"""The public names of the Android ABI table. +"""Neutral Flutter build-mode names.""" -The table itself is in flutter/private/abis.bzl. Its ABI names are the public -spelling of an Android ABI; these are the names the Android module and consumers -read. Engine and embedding repository names, CPU constraints and AOT modes stay -core-private. -""" - -load( - "//flutter/private:abis.bzl", - _ABIS = "ABIS", - _ABI_PLATFORM_ATTRS = "ABI_PLATFORM_ATTRS", - _MODES = "MODES", - _abi_from_platform = "abi_from_platform", - _engine_jar_label = "engine_jar_label", -) +load("//flutter/private:abis.bzl", _MODES = "MODES") visibility("public") -# The build modes, `release` and `debug`. MODES = _MODES - -# ABI name -> the ABI's per-architecture fields. The keys are the public spelling. -ABIS = _ABIS - -# Implicit attributes a rule carries to name its target platform's ABI. -ABI_PLATFORM_ATTRS = _ABI_PLATFORM_ATTRS - -# The ABI a rule's target platform selects, or None when it is not a known Android ABI. -abi_from_platform = _abi_from_platform - -# The prebuilt engine jar for one ABI and mode, as a Label. -engine_jar_label = _engine_jar_label diff --git a/rules_flutter_core/flutter/private/BUILD.bazel b/rules_flutter_core/flutter/private/BUILD.bazel index f9a0ac2b..e8899868 100644 --- a/rules_flutter_core/flutter/private/BUILD.bazel +++ b/rules_flutter_core/flutter/private/BUILD.bazel @@ -21,10 +21,9 @@ Two mechanical exceptions need a label, and only those: host-compatible registration points at the public static toolchain target. """ -load("//tools/ci:assertions.bzl", "expect_equal", "expect_label_equal") -load(":abis.bzl", "ABIS") load(":hook_packages_test.bzl", "staged_hook_packages_test") load(":pub_lock_parser_test.bzl", "pub_lock_parser_test_suite") +load(":rules.bzl", "flutter_target_slice") load(":toolchain.bzl", "flutter_toolchain") package(default_visibility = ["//visibility:private"]) @@ -87,34 +86,20 @@ toolchain_type( visibility = ["//visibility:public"], ) -# The ABI table is implementation, so these assertions live beside it rather -# than in the Consumer Module fixture: a wrong snapshot flag or CPU constraint -# mislabels a platform, and nothing else in the build would notice. -expect_equal( - ABIS["armeabi-v7a"].snapshot_flags, - [ - "--no-sim-use-hardfp", - "--no-use-integer-division", - ], - "armeabi-v7a snapshot_flags", -) - -expect_label_equal( - ABIS["arm64-v8a"].cpu_constraint, - "@platforms//cpu:arm64", - "arm64-v8a cpu_constraint", +toolchain_type( + name = "flutter_target_toolchain_type", + visibility = ["//visibility:public"], ) -expect_label_equal( - ABIS["x86_64"].cpu_constraint, - "@platforms//cpu:x86_64", - "x86_64 cpu_constraint", +toolchain_type( + name = "flutter_aot_toolchain_type", + no_match_error = "No Flutter AOT toolchain is registered for this platform. For Android, run `flutter precache --android` on this host to fetch the missing gen_snapshot payload.", + visibility = ["//visibility:public"], ) -expect_label_equal( - ABIS["armeabi-v7a"].cpu_constraint, - "@platforms//cpu:armv7", - "armeabi-v7a cpu_constraint", +flutter_target_slice( + name = "target_slice", + visibility = ["//visibility:public"], ) # What the pub lock reader accepts and, as importantly, refuses. @@ -122,10 +107,7 @@ pub_lock_parser_test_suite(name = "pub_lock_parser_test") staged_hook_packages_test(name = "staged_hook_packages_test") -# One split per ABI and mode. The core selects its per-ABI libraries on these, -# so a 3-ABI build gets exactly one split per ABI rather than one per platform. # Public because the core's select() is expanded in the consumer's package. -# Spelled out: Bazel does not allow `for` in BUILD files. config_setting( name = "target_os_android", constraint_values = ["@platforms//os:android"], @@ -133,61 +115,8 @@ config_setting( ) config_setting( - name = "split_arm64_v8a_release", - constraint_values = [ - "@platforms//os:android", - "@platforms//cpu:arm64", - ], - flag_values = {"//flutter:mode": "release"}, - visibility = ["//visibility:public"], -) - -config_setting( - name = "split_arm64_v8a_debug", - constraint_values = [ - "@platforms//os:android", - "@platforms//cpu:arm64", - ], - flag_values = {"//flutter:mode": "debug"}, - visibility = ["//visibility:public"], -) - -config_setting( - name = "split_x86_64_release", - constraint_values = [ - "@platforms//os:android", - "@platforms//cpu:x86_64", - ], - flag_values = {"//flutter:mode": "release"}, - visibility = ["//visibility:public"], -) - -config_setting( - name = "split_x86_64_debug", - constraint_values = [ - "@platforms//os:android", - "@platforms//cpu:x86_64", - ], - flag_values = {"//flutter:mode": "debug"}, - visibility = ["//visibility:public"], -) - -config_setting( - name = "split_armeabi_v7a_release", - constraint_values = [ - "@platforms//os:android", - "@platforms//cpu:armv7", - ], + name = "target_os_android_release", + constraint_values = ["@platforms//os:android"], flag_values = {"//flutter:mode": "release"}, visibility = ["//visibility:public"], ) - -config_setting( - name = "split_armeabi_v7a_debug", - constraint_values = [ - "@platforms//os:android", - "@platforms//cpu:armv7", - ], - flag_values = {"//flutter:mode": "debug"}, - visibility = ["//visibility:public"], -) diff --git a/rules_flutter_core/flutter/private/abis.bzl b/rules_flutter_core/flutter/private/abis.bzl index e595f78d..bac5a61e 100644 --- a/rules_flutter_core/flutter/private/abis.bzl +++ b/rules_flutter_core/flutter/private/abis.bzl @@ -1,194 +1,15 @@ -"""One table mapping an Android ABI to every name the build needs for it. - -The same architecture is spelled differently by the NDK, by `flutter build -bundle`, by the engine cache, by Maven and by the -native-assets manifest. **The ABI name is the public spelling** -- it is what -appears in the APK and what a consumer writes; the rest are derived here and -never typed by hand. - -This is the Android half of the slice table the roadmap's convergences describe. -Apple slices belong beside it rather than in a second file: two tables would -have to agree about the same architectures forever. - -`x86` is absent deliberately. The SDK precaches no `android-x86` engine, so -there is no `gen_snapshot` to pair with an x86 library however many a package -publishes. -""" +"""Mode values and compatibility selects for core Flutter rules.""" visibility(["//flutter", "//flutter/private"]) MODES = ["release", "debug"] - -# Modes that require gen_snapshot. AOT_MODES = ["release"] -def _abi(target_platform, engine_dir, maven_artifact, manifest_key, cpu_constraint, snapshot_flags = []): - return struct( - # `flutter build bundle --target-platform`. - target_platform = target_platform, - engine_dir = engine_dir, - maven_artifact = maven_artifact, - # The key the engine reads in NativeAssetsManifest.json. Baked in at - # engine-compile time, so it is the engine that decides, not the build. - manifest_key = manifest_key, - # CPU constraint used to identify the ABI after a platform transition. - cpu_constraint = cpu_constraint, - # Extra gen_snapshot flags. Empty for everything but armv7. - snapshot_flags = snapshot_flags, - ) - -ABIS = { - "arm64-v8a": _abi( - target_platform = "android-arm64", - engine_dir = {"release": "android-arm64-release"}, - maven_artifact = { - "release": "arm64_v8a_release", - "debug": "arm64_v8a_debug", - }, - manifest_key = "android_arm64", - cpu_constraint = Label("@platforms//cpu:arm64"), - ), - "x86_64": _abi( - target_platform = "android-x64", - engine_dir = {"release": "android-x64-release"}, - maven_artifact = { - "release": "x86_64_release", - "debug": "x86_64_debug", - }, - manifest_key = "android_x64", - cpu_constraint = Label("@platforms//cpu:x86_64"), - ), - "armeabi-v7a": _abi( - target_platform = "android-arm", - engine_dir = {"release": "android-arm-release"}, - maven_artifact = { - "release": "armeabi_v7a_release", - "debug": "armeabi_v7a_debug", - }, - manifest_key = "android_arm", - cpu_constraint = Label("@platforms//cpu:armv7"), - # The only ABI needing flags, and the reason they live in a table rather - # than a rule body. flutter_tools passes both for armv7 (softfp, and no - # integer division on 32-bit Pixels). Omitting them yields a snapshot - # that builds, links and installs, then executes an unsupported - # instruction on the affected device. - snapshot_flags = [ - "--no-sim-use-hardfp", - "--no-use-integer-division", - ], - ), -} - -# Android CPU constraints recognized as unsupported, so diagnostics distinguish -# unsupported Android CPUs from non-Android platforms. -_UNSUPPORTED_ANDROID_CPU_CONSTRAINTS = { - Label("@platforms//cpu:x86_32"): "x86", - Label("@platforms//cpu:riscv64"): "riscv64", -} - -def abi_cpu_constraints(): - """Build and validate the CPU-constraint-to-ABI mapping. - - Rejects a CPU constraint that names both an ABI and an unsupported CPU. - - Returns: - A dict from CPU constraint Label to ABI or unsupported-CPU name. - """ - constraints = {} - for abi, entry in ABIS.items(): - constraints[entry.cpu_constraint] = abi - for constraint, name in _UNSUPPORTED_ANDROID_CPU_CONSTRAINTS.items(): - if constraint in constraints: - fail("abis.bzl: ABIS and _UNSUPPORTED_ANDROID_CPU_CONSTRAINTS both name {}.".format( - constraint, - )) - constraints[constraint] = name - return constraints - -# Implicit attrs used to identify the ABI from the target platform. -ABI_PLATFORM_ATTRS = { - "_cpu_constraints": attr.label_keyed_string_dict(default = abi_cpu_constraints()), - # CPU constraints are shared with non-Android platforms; gate matching on OS. - "_android_os": attr.label(default = Label("@platforms//os:android")), -} - -def abi_from_platform(ctx): - """Return the matching Android ABI or unsupported CPU name, or None. - - CPU constraints are shared with non-Android platforms, so require Android - before matching the CPU. - - Args: - ctx: the rule or aspect context; must carry ABI_PLATFORM_ATTRS. - - Returns: - An ABI name, an unsupported-CPU name, or None if not Android. - """ - android = ctx.attr._android_os[platform_common.ConstraintValueInfo] - if not ctx.target_platform_has_constraint(android): - return None - for target, name in ctx.attr._cpu_constraints.items(): - constraint = target[platform_common.ConstraintValueInfo] - if ctx.target_platform_has_constraint(constraint): - return name - return None - -def engine_repo(abi, mode): - """Name of the repository holding one ABI's prebuilt engine jar in one mode. - - Args: - abi: an Android ABI name, a key of ABIS. - mode: "release" or "debug". - - Returns: - A repository name; `-` is not valid in one, so it becomes `_`. - """ - return "flutter_engine_{}_{}".format(abi.replace("-", "_"), mode) - -def engine_jar_label(abi, mode): - """The prebuilt engine jar for one ABI in one mode. - - A `Label`, not a string: a string handed to an attribute from inside a macro - resolves in the *caller's* repo mapping, and these repositories are our - extension's. Resolving here is what keeps the engine out of consumer BUILD - files. - - Args: - abi: an Android ABI name, a key of ABIS. - mode: "release" or "debug". - - Returns: - A Label for the unstripped engine jar. - """ - return Label("@{}//jar:file".format(engine_repo(abi, mode))) - -def engine_libs_label(abi, mode): - """The native libraries extracted from one ABI's engine jar in one mode. - - Same repository-mapping reason as engine_jar_label: resolve here, not in - the caller's package. - - Args: - abi: an Android ABI name, a key of ABIS. - mode: "release" or "debug". - - Returns: - A Label for the glob of the jar's lib/**/*.so files. - """ - return Label("@{}//jar:libs".format(engine_repo(abi, mode))) - def aot_target_compatible_with(): """target_compatible_with for a rule that only runs under an AOT-capable mode. - Built from AOT_MODES rather than naming "debug" directly, so the list - controls which modes are compatible with an AOT rule. Adding another AOT - mode also requires a matching build-setting value and per-ABI engine - directory mapping. Every omitted mode reports "target incompatible with - the current configuration" during analysis instead of reaching gen_snapshot - with a kernel it rejects -- see docs_internal/build-modes-plan.md. - Returns: - A select() value for a `target_compatible_with` attribute. + A select expression that is compatible in AOT-capable modes. """ conditions = {"//conditions:default": [Label("@platforms//:incompatible")]} for mode in AOT_MODES: @@ -196,35 +17,8 @@ def aot_target_compatible_with(): return select(conditions) def debug_target_compatible_with(): - """target_compatible_with for a rule that exists only in debug mode. - - A debug-only artifact reads the debug engine and the JIT kernel, so a - release configuration must skip it rather than mix the two. - - Returns: - A select() value for a `target_compatible_with` attribute. - """ + """target_compatible_with for a rule that exists only in debug mode.""" return select({ Label("//flutter:mode_debug"): [], "//conditions:default": [Label("@platforms//:incompatible")], }) - -def check_abis(abis, where): - """Fails with the supported set listed, rather than a KeyError. - - Args: - abis: the ABI names a caller asked for. - where: what to name in the failure, usually the calling target. - """ - if not abis: - fail("{}: `abis` must name at least one ABI, one of {}.".format( - where, - sorted(ABIS), - )) - for abi in abis: - if abi not in ABIS: - fail("{}: unknown ABI '{}'. Supported: {}.".format( - where, - abi, - sorted(ABIS), - )) diff --git a/rules_flutter_core/flutter/private/core.bzl b/rules_flutter_core/flutter/private/core.bzl index 13982876..3b9942aa 100644 --- a/rules_flutter_core/flutter/private/core.bzl +++ b/rules_flutter_core/flutter/private/core.bzl @@ -1,24 +1,24 @@ -"""The core artifact: an app's Dart outputs and engine libraries, with no APK packaging. +"""The core artifact: an app's Dart outputs and host-test inputs. -`flutter_core` declares the platform-neutral inputs once. The kernel and the -asset bundle are built under a host platform transition, so an android_binary -with N splits runs each action once rather than once per split. The per-ABI -snapshot and engine libraries are chosen by `select()` on the split's -config_settings, so each split names only its own ABI. - -The android_binary side takes the artifact through `FlutterArtifactInfo`. See -`flutter_android_inputs` for the packaging half. +`flutter_core` declares an app's platform-neutral inputs once. Its AOT outputs +are selected by `select()` on target-platform config settings; Android engine +libraries belong to `rules_flutter_android`. """ load("@bazel_skylib//lib:shell.bzl", "shell") load("@bazel_skylib//rules:build_test.bzl", "build_test") load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") -load(":abis.bzl", "ABIS", "ABI_PLATFORM_ATTRS", "MODES", "abi_from_platform", "aot_target_compatible_with", "engine_libs_label") +load(":abis.bzl", "aot_target_compatible_with") load(":pubspec.bzl", "FlutterPubspecInfo", "flutter_pubspec") load(":rules.bzl", "dart_aot_elf", "dart_aot_kernel", "dart_jit_kernel", "flutter_assets", "flutter_package", "flutter_test_assets", "lib_root", "library_path", "pub_path_deps_check", "pub_plugins_check") visibility(["//flutter/...", "//tests/..."]) +_TARGET_TOOLCHAIN_LABEL = "//flutter/private:flutter_target_toolchain_type" +_TARGET_TOOLCHAIN = config_common.toolchain_type( + Label("//flutter/private:flutter_target_toolchain_type"), + mandatory = False, +) _TOOLCHAIN = "//flutter/private:flutter_toolchain_type" def _internal_tags(kwargs): @@ -53,7 +53,6 @@ FlutterArtifactInfo = provider( "mode": "`release` or `debug`.", "abi": "The Android ABI of this split, or None on a host build.", "libapp": "File: the libapp.so of this ABI, or None under debug.", - "engine_libs": "list of File: this ABI's engine .so files, unstripped.", "assets": "Directory: the flutter_assets bundle, with the debug overlay under debug.", }, ) @@ -72,11 +71,6 @@ FlutterCoreInfo = provider( }, ) -def _split_label(abi, mode): - # A Label, not a string: select() keys in a macro resolve in the caller's - # repo mapping, and these config_settings belong to this ruleset. - return Label("//flutter/private:split_{}_{}".format(abi.replace("-", "_"), mode)) - _ANDROID_SPLIT_OUTPUTS = [ # The options the Android split transition writes. The kernel and the bundle # must not inherit any of them from whichever split asked. rules_android's @@ -188,33 +182,27 @@ assets_bundle = rule( def _flutter_core_impl(ctx): mode = ctx.attr._mode[BuildSettingInfo].value - abi = abi_from_platform(ctx) + abi = ctx.toolchains[_TARGET_TOOLCHAIN_LABEL].flutter_target.slice_id if ctx.toolchains[_TARGET_TOOLCHAIN_LABEL] != None else None libapp = ctx.files.libapp - engine_libs = ctx.files.engine_libs assets = ctx.attr.assets[DefaultInfo].files.to_list() (mode_free_assets,) = ctx.files.mode_free_assets if len(assets) != 1: fail("{}: expected one assets directory, got {}".format(ctx.label, len(assets))) if len(libapp) > 1: fail("{}: expected at most one libapp.so, got {}".format(ctx.label, len(libapp))) - if abi != None: - if not engine_libs: - fail("{}: ABI '{}' has no engine libraries".format(ctx.label, abi)) - if mode == "release" and len(libapp) != 1: - fail("{}: release ABI '{}' needs one libapp.so".format(ctx.label, abi)) + if abi != None and mode == "release" and len(libapp) != 1: + fail("{}: release ABI '{}' needs one libapp.so".format(ctx.label, abi)) return [ - DefaultInfo(files = depset(libapp + engine_libs + [assets[0]])), + DefaultInfo(files = depset(libapp + [assets[0]])), OutputGroupInfo( flutter_assets = depset([assets[0]]), - flutter_engine = depset(engine_libs), flutter_libapp = depset(libapp), ), FlutterArtifactInfo( mode = mode, abi = abi, libapp = libapp[0] if libapp else None, - engine_libs = engine_libs, assets = assets[0], ), FlutterCoreInfo( @@ -233,22 +221,19 @@ def _flutter_core_impl(ctx): _flutter_core = rule( implementation = _flutter_core_impl, doc = "Collects one split's view of a flutter_core into FlutterArtifactInfo.", - attrs = dict( - { - "libapp": attr.label_list(allow_files = True, doc = "The dart_aot_elf of this split's ABI; empty under debug and on a host build."), - "engine_libs": attr.label_list(allow_files = True, doc = "This split's engine .so files; empty on a host build."), - "assets": attr.label(mandatory = True, doc = "The platform-neutral bundle."), - "mode_free_assets": attr.label(allow_files = True, mandatory = True, cfg = _platform_neutral_transition, doc = "The flutter_assets tree before any debug overlay, built under the host platform. A flutter_debug_core overlays its own kernel on it, so the app's assets label is never named outside this package."), - "pubspec": attr.label(mandatory = True, providers = [FlutterPubspecInfo]), - "package_config": attr.label(allow_single_file = True, mandatory = True, doc = "The pub hub's package_config.json, read by a flutter_debug_core's kernel."), - "pub_srcs": attr.label_list(allow_files = True, allow_empty = False, mandatory = True, doc = "The pub hub's :lib, read by a flutter_debug_core's kernel."), - "srcs": attr.label_list(allow_files = [".dart"], doc = "Dart sources for change detection in a flutter_debug_core's kernel."), - "path_deps": attr.label_list(allow_files = True, doc = "Sources of path dependencies, as the kernels take them."), - "debug_registrant": attr.label(allow_single_file = [".dart"], doc = "The debug registrant, as dart_jit_kernel takes it."), - "_mode": attr.label(default = Label("//flutter:mode"), providers = [BuildSettingInfo]), - }, - **ABI_PLATFORM_ATTRS - ), + attrs = { + "libapp": attr.label_list(allow_files = True, doc = "The dart_aot_elf of this split; empty under debug and on a host build."), + "assets": attr.label(mandatory = True, doc = "The platform-neutral bundle."), + "mode_free_assets": attr.label(allow_files = True, mandatory = True, cfg = _platform_neutral_transition, doc = "The flutter_assets tree before any debug overlay, built under the host platform. A flutter_debug_core overlays its own kernel on it, so the app's assets label is never named outside this package."), + "pubspec": attr.label(mandatory = True, providers = [FlutterPubspecInfo]), + "package_config": attr.label(allow_single_file = True, mandatory = True, doc = "The pub hub's package_config.json, read by a flutter_debug_core's kernel."), + "pub_srcs": attr.label_list(allow_files = True, allow_empty = False, mandatory = True, doc = "The pub hub's :lib, read by a flutter_debug_core's kernel."), + "srcs": attr.label_list(allow_files = [".dart"], doc = "Dart sources for change detection in a flutter_debug_core's kernel."), + "path_deps": attr.label_list(allow_files = True, doc = "Sources of path dependencies, as the kernels take them."), + "debug_registrant": attr.label(allow_single_file = [".dart"], doc = "The debug registrant, as dart_jit_kernel takes it."), + "_mode": attr.label(default = Label("//flutter:mode"), providers = [BuildSettingInfo]), + }, + toolchains = [_TARGET_TOOLCHAIN], ) def _pub_labels(pub, who): @@ -269,6 +254,8 @@ def flutter_core( dart_defines = {}, native_build_env = None, dart_plugin_registrant = None, + android_target_platforms = None, + aot_compatible_with = [], **kwargs): """Declares an app's Flutter artifacts and host-test inputs. @@ -285,9 +272,11 @@ def flutter_core( entrypoint: the app's entrypoint under lib/. srcs: Dart sources for change detection; defaults to `glob(["lib/**/*.dart"])`. path_deps: sources of `path:` dependencies. - dart_defines: `-D` constants the kernels compile with. + dart_defines: values forwarded to frontend_server as `-D=` options. native_build_env: the native build environment for asset bundle hooks, or None. - dart_plugin_registrant: the Dart plugin registrant under lib/, or None. + dart_plugin_registrant: an optional plugin registrant Dart file to compile. + android_target_platforms: platform labels used to configure target toolchains. + aot_compatible_with: additional compatibility constraints for platform-specific AOT artifacts. **kwargs: visibility and tags passed to declared targets. """ if srcs == None: @@ -297,6 +286,9 @@ def flutter_core( if name == "pubspec": fail("flutter_core: `name` may not be \"pubspec\", which the core declares.") + if android_target_platforms == None: + fail("flutter_core: Android asset bundling requires target platforms; use flutter_android_core from rules_flutter_android.") + package_config, project_package_config, pub_lib, pub_all = _pub_labels(pub, "flutter_core " + name) aot_registrant = None @@ -320,12 +312,10 @@ def flutter_core( path_deps = path_deps, dart_plugin_registrant = aot_registrant, dart_defines = dart_defines, - target_os = "android", + target_platforms = android_target_platforms, target_compatible_with = aot_target_compatible_with(), ) - # Keep the OS-specific kernel shared across ABI splits through the existing - # host transition. platform_neutral_files( name = name + "_kernel_neutral", src = ":" + name + "_kernel", @@ -350,7 +340,7 @@ def flutter_core( flutter_assets( name = name + "_assets", - abis = list(ABIS), + target_platforms = android_target_platforms, native_build_env = native_build_env, assets = assets, pubspec = ":pubspec", @@ -395,19 +385,12 @@ def flutter_core( src = ":" + name + "_bundle_action", ) - for abi in ABIS: - dart_aot_elf( - name = "{}_libapp_{}".format(name, abi), - dill = select({ - Label("//flutter/private:target_os_android"): ":" + name + "_kernel_neutral", - "//conditions:default": None, - }), - abi = abi, - snapshot_flags = ABIS[abi].snapshot_flags, - strip = True, - # Only the matching Android ABI has a libapp implementation. - target_compatible_with = [Label("@platforms//os:android"), ABIS[abi].cpu_constraint] + aot_target_compatible_with(), - ) + dart_aot_elf( + name = name + "_libapp", + dill = ":" + name + "_kernel_neutral", + strip = True, + target_compatible_with = aot_compatible_with + aot_target_compatible_with(), + ) guards = [":path_deps_check"] pub_path_deps_check( @@ -438,18 +421,12 @@ def flutter_core( **kwargs ) - # Each split names only its own ABI's libapp and engine libraries. The - # host build and any unmatched split get none, and fail downstream. _flutter_core( name = name, - libapp = select(dict( - [(_split_label(abi, "release"), [":{}_libapp_{}".format(name, abi)]) for abi in ABIS] + - [("//conditions:default", [])], - )), - engine_libs = select(dict( - [(_split_label(abi, mode), [engine_libs_label(abi, mode)]) for abi in ABIS for mode in MODES] + - [("//conditions:default", [])], - )), + libapp = select({ + Label("//flutter/private:target_os_android_release"): [":" + name + "_libapp"], + "//conditions:default": [], + }), assets = ":" + name + "_bundle", mode_free_assets = ":" + name + "_assets", pubspec = ":pubspec", diff --git a/rules_flutter_core/flutter/private/debug_core.bzl b/rules_flutter_core/flutter/private/debug_core.bzl index 4677a3db..666e6a43 100644 --- a/rules_flutter_core/flutter/private/debug_core.bzl +++ b/rules_flutter_core/flutter/private/debug_core.bzl @@ -2,9 +2,9 @@ `flutter_debug_core` compiles its own `entrypoint` with its own `dart_defines`, against the app's package inputs, and pairs that kernel with the app's -mode-free bundle and debug engine. The result is a debug FlutterArtifactInfo -with the same shape as the app's, so packaging consumes it the same way. Both -kernels and both bundles come from the shared actions in rules.bzl and core.bzl. +mode-free bundle. The result is a debug FlutterArtifactInfo with the same shape +as the app's, so packaging consumes it the same way. Both kernels and both +bundles come from the shared actions in rules.bzl and core.bzl. The app is reached only through its FlutterCoreInfo provider, so it may live in another package, behind a restrictive visibility, or be an alias. @@ -102,20 +102,17 @@ def _flutter_debug_core_impl(ctx): artifact = ctx.attr.app[FlutterArtifactInfo] (assets,) = ctx.files.bundle - # No libapp: a debug artifact runs its kernel from the bundle. The engine is - # selected by the same configuration as the app. + # No libapp: a debug artifact runs its kernel from the bundle. return [ - DefaultInfo(files = depset(artifact.engine_libs + [assets])), + DefaultInfo(files = depset([assets])), OutputGroupInfo( flutter_assets = depset([assets]), - flutter_engine = depset(artifact.engine_libs), flutter_libapp = depset([]), ), FlutterArtifactInfo( mode = "debug", abi = artifact.abi, libapp = None, - engine_libs = artifact.engine_libs, assets = assets, ), ctx.attr.app[FlutterPubspecInfo], @@ -123,7 +120,7 @@ def _flutter_debug_core_impl(ctx): _flutter_debug_core = rule( implementation = _flutter_debug_core_impl, - doc = "Collects a debug FlutterArtifactInfo from the app's engine and the test bundle.", + doc = "Collects a debug FlutterArtifactInfo from the app and the test bundle.", attrs = { "app": attr.label( mandatory = True, diff --git a/rules_flutter_core/flutter/private/repo.bzl b/rules_flutter_core/flutter/private/repo.bzl index 22acc042..78033f02 100644 --- a/rules_flutter_core/flutter/private/repo.bzl +++ b/rules_flutter_core/flutter/private/repo.bzl @@ -7,7 +7,6 @@ artifacts are fetched lazily by `flutter precache`, so a fully hermetic download rule is a separate concern. See README for that tradeoff. """ -load(":abis.bzl", "ABIS", "AOT_MODES", "MODES", "engine_repo") load(":sdk_packages.bzl", "sdk_package_root") visibility(["//flutter"]) @@ -31,7 +30,7 @@ exports_files([ "flutter_tester", "icudtl.dat", "flutter.version.json", -] + {gen_snapshots}) +]) # Patched SDK containing platform_strong.dill. `--sdk-root` wants the directory, # so the whole tree is exposed as one filegroup. @@ -91,15 +90,6 @@ def _host_engine_dir(ctx): host = _host_platform_name(ctx) return "darwin-x64" if host.startswith("darwin-") else host -def _gen_snapshot_host_dir(ctx): - """Host directory of the Android gen_snapshot executables.""" - - # TODO(flutter/flutter#152281): delete the collapse and return - # _host_platform_name(ctx) directly, once the engine ships an arm64-native - # Android gen_snapshot. - host = _host_platform_name(ctx) - return "darwin-x64" if host.startswith("darwin-") else host - def _resolve_flutter_root(ctx): """Locate the SDK from FLUTTER_ROOT, else from `flutter` on PATH.""" root = ctx.os.environ.get("FLUTTER_ROOT", "").strip() @@ -278,18 +268,7 @@ def _flutter_sdk_impl(ctx): "flutter_patched_sdk", ) - # Create a snapshot symlink for each ABI and AOT mode. - host = _gen_snapshot_host_dir(ctx) - for abi, info in ABIS.items(): - for mode in AOT_MODES: - gen_snapshot = "{}/{}/{}/gen_snapshot".format(engine, info.engine_dir[mode], host) - - if not ctx.path(gen_snapshot).exists: - fail( - "gen_snapshot for {} missing at {}.\n".format(abi, gen_snapshot) + - "Run `flutter precache --android` on this host.", - ) - ctx.symlink(gen_snapshot, "gen_snapshot_{}_{}".format(abi, mode)) + # Android owns its per-ABI gen_snapshot repositories. # The host-engine files: the debug snapshots the debug overlay ships as # vm_snapshot_data and isolate_snapshot_data, and the test runtime @@ -322,22 +301,14 @@ def _flutter_sdk_impl(ctx): sdk_globs = repr( ["{}/lib/**".format(r) for r in sdk_package_roots] + ["{}/pubspec.yaml".format(r) for r in sdk_package_roots] + - # The bundle action's license collector reads `/NOTICES` or - # `/LICENSE` of every package (sky_engine's is 1.3 MB of engine - # third-party licenses). Absent, NOTICES.Z silently loses them. ["{}/LICENSE*".format(r) for r in sdk_package_roots] + ["{}/NOTICES*".format(r) for r in sdk_package_roots], ), - gen_snapshots = repr([ - "gen_snapshot_{}_{}".format(abi, mode) - for abi in ABIS - for mode in AOT_MODES - ]), )) flutter_sdk = repository_rule( implementation = _flutter_sdk_impl, - doc = "Exposes the declared Flutter SDK's compiler + snapshotter to Bazel.", + doc = "Exposes the declared Flutter SDK's host tools to Bazel.", attrs = { "version": attr.string(mandatory = True), }, @@ -345,71 +316,6 @@ flutter_sdk = repository_rule( # PATH is needed when FLUTTER_ROOT is unset. HOME and PUB_CACHE are not read. environ = ["FLUTTER_ROOT", "PATH"], ) -# The Android embedding is not in bin/cache -- Flutter fetches it from Maven at -# Gradle time. The URLs are derivable from flutter.version.json, with one trap: -# the *directory* is versioned by engineRevision while the *filename* uses -# engineContentHash, and the artifacts are .jar, not .aar. - -_ENGINE_BASE = "https://storage.googleapis.com/download.flutter.io/io/flutter" - -# Builds Maven artifact URLs for engine and embedding jars. - -def _engine_url(artifact, revision, content_hash): - return "{base}/{a}/1.0.0-{rev}/{a}-1.0.0-{hash}.jar".format( - base = _ENGINE_BASE, - a = artifact, - rev = revision, - hash = content_hash, - ) - -def _flutter_engine_jar_impl(ctx): - version = json.decode(ctx.read(ctx.path(ctx.attr.version_file))) - url = _engine_url( - ctx.attr.artifact, - version["engineRevision"], - version["engineContentHash"], - ) - ctx.download( - url = url, - output = "jar/{}.jar".format(ctx.attr.artifact), - canonical_id = url, - ) - - # Only an ABI's engine jar carries the .so files that :libs globs; the embedding - # jar stays a file. - if ctx.attr.extract_libs: - ctx.extract( - archive = ctx.path("jar/{}.jar".format(ctx.attr.artifact)), - output = "jar", - ) - - # The jar's members are unknown until it is extracted, so the core takes each - # ABI's .so files through this glob rather than a list it would have to hardcode. - # The embedding jar carries no .so files, hence allow_empty. - ctx.file("jar/BUILD.bazel", """filegroup( - name = "file", - srcs = ["{}.jar"], - visibility = ["//visibility:public"], -) - -filegroup( - name = "libs", - srcs = glob(["lib/**/*.so"], allow_empty = True), - visibility = ["//visibility:public"], -) -""".format(ctx.attr.artifact)) - -flutter_engine_jar = repository_rule( - implementation = _flutter_engine_jar_impl, - attrs = { - "artifact": attr.string(mandatory = True), - "extract_libs": attr.bool(default = False), - "version_file": attr.label( - allow_single_file = True, - default = Label("@flutter_sdk//:flutter.version.json"), - ), - }, -) def _flutter_impl(ctx): declared_version = "" @@ -428,14 +334,6 @@ def _flutter_impl(ctx): flutter_tools_packages(name = "flutter_tools_packages") - for abi, info in ABIS.items(): - for mode in MODES: - flutter_engine_jar( - name = engine_repo(abi, mode), - artifact = info.maven_artifact[mode], - extract_libs = True, - ) - _flutter_sdk_tag = tag_class( attrs = { "version": attr.string(mandatory = True), diff --git a/rules_flutter_core/flutter/private/rules.bzl b/rules_flutter_core/flutter/private/rules.bzl index d6911582..68414e46 100644 --- a/rules_flutter_core/flutter/private/rules.bzl +++ b/rules_flutter_core/flutter/private/rules.bzl @@ -26,10 +26,69 @@ tool the assets action runs is still a host input (see `_ASSETS_EXEC`). """ load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") -load(":abis.bzl", "ABIS") load(":hook_packages.bzl", "FlutterHookPackagesInfo", "staged_hook_packages") load(":pubspec.bzl", "FlutterPubspecInfo") +FlutterTargetSliceInfo = provider( + doc = "Platform metadata resolved for a configured Flutter target slice.", + fields = [ + "target_platform", + "slice_id", + "target_os", + ], +) + +_ANDROID_SPLIT_OUTPUTS = [ + "//command_line_option:Android configuration distinguisher", + "//command_line_option:android_compiler", + "//command_line_option:android_platforms", + "//command_line_option:compiler", + "//command_line_option:dynamic_mode", + "//command_line_option:platforms", +] + +def _target_platform_split_impl(_settings, attr): + return { + str(platform): { + "//command_line_option:Android configuration distinguisher": "main", + "//command_line_option:android_compiler": None, + "//command_line_option:android_platforms": [], + "//command_line_option:compiler": None, + "//command_line_option:dynamic_mode": "default", + "//command_line_option:platforms": [str(platform)], + } + for platform in attr.target_platforms + } + +_target_platform_split = transition( + implementation = _target_platform_split_impl, + inputs = [], + outputs = _ANDROID_SPLIT_OUTPUTS, +) + +def _flutter_target_slice_impl(ctx): + target = ctx.toolchains["//flutter/private:flutter_target_toolchain_type"] + if target == None: + return [FlutterTargetSliceInfo( + target_platform = "", + slice_id = "", + target_os = "", + )] + target = target.flutter_target + return [FlutterTargetSliceInfo( + target_platform = target.target_platform, + slice_id = target.slice_id, + target_os = target.target_os, + )] + +flutter_target_slice = rule( + implementation = _flutter_target_slice_impl, + toolchains = [config_common.toolchain_type( + Label("//flutter/private:flutter_target_toolchain_type"), + mandatory = False, + )], +) + visibility(["//flutter"]) # The bundle action runs `flutter build bundle`, and the flutter tool is still a @@ -159,6 +218,12 @@ _REGISTRANT_SOURCE_ARGS = '--source "{uri}" --source "package:flutter/src/dart_p def _flutter_toolchain(ctx): return ctx.toolchains["//flutter/private:flutter_toolchain_type"].flutter +def _flutter_target_toolchain(ctx): + return ctx.toolchains["//flutter/private:flutter_target_toolchain_type"].flutter_target + +def _flutter_aot_toolchain(ctx): + return ctx.toolchains["//flutter/private:flutter_aot_toolchain_type"].flutter_aot + def _dart_aot_kernel_impl(ctx): dill = ctx.actions.declare_file(ctx.label.name + ".dill") @@ -192,7 +257,23 @@ def _dart_aot_kernel_impl(ctx): args.add("--tfa") args.add("-Ddart.vm.product=true") - args.add("--target-os", ctx.attr.target_os) + target_os = ctx.attr.target_os + if not target_os: + target_slices = ctx.split_attr.target_slice + target_os_values = { + target[FlutterTargetSliceInfo].target_os: True + for target in target_slices.values() + if target[FlutterTargetSliceInfo].target_os + } + if not target_os_values: + fail("dart_aot_kernel has no Flutter target toolchain for {}; set target_os or register a platform ruleset".format( + ctx.attr.target_platforms[0] if ctx.attr.target_platforms else ctx.label, + )) + if len(target_os_values) != 1: + fail("dart_aot_kernel target platforms must agree on target_os; got {}".format(sorted(target_os_values.keys()))) + target_os = target_os_values.keys()[0] + args.add("--target-os", target_os) + args.add_all(_dart_define_flags(ctx.attr.dart_defines)) # The package config is the pub hub's: every rootUri is relative, so it is @@ -354,11 +435,6 @@ Point this at a filegroup in the dependency's own package, e.g. `path_deps = ["//packages/mylib:srcs"]`. Bazel globs cannot cross package boundaries, so the dependency needs its own BUILD file.""", ), - "target_os": attr.string( - mandatory = True, - values = ["android", "ios", "macos", "linux", "windows", "fuchsia"], - doc = "Target OS for `--target-os`.", - ), "dart_defines": attr.string_dict( doc = """Compile-time constants, passed as `-D=`. @@ -368,6 +444,18 @@ not get these values, and returns the default. They are in the action key, so changing a value recompiles the kernel; an empty dict adds no flags, so existing keys are unchanged.""", ), + "target_platforms": attr.label_list( + doc = "Platforms whose target toolchains provide kernel target-OS metadata.", + ), + "target_os": attr.string( + doc = "Target OS override; otherwise derived from the registered platform toolchain.", + ), + "target_slice": attr.label( + default = Label("//flutter/private:target_slice"), + cfg = _target_platform_split, + providers = [FlutterTargetSliceInfo], + doc = "Internal per-platform target-toolchain metadata.", + ), }, toolchains = ["//flutter/private:flutter_toolchain_type"], ) @@ -992,17 +1080,14 @@ flags, so existing keys are unchanged.""", def _dart_aot_elf_impl(ctx): # Under the target name, not at the package root. The basename is fixed -- - # the engine's Dart_LoadELF is given "libapp.so" by the embedder, and - # jni_lib_jar packages whatever basename it is handed -- so the target name - # is the only thing left to disambiguate with. Two targets in one package - # (two ABIs, or Android beside iOS) both declaring `libapp.so` at the root - # is a duplicate-output analysis error. + # Android splits compile this same label against distinct target toolchains; + # configuration-specific output roots keep the resulting libapp.so files apart. so = ctx.actions.declare_file(ctx.label.name + "/libapp.so") if ctx.file.dill == None: fail( - "Target OS selection failed for label {} (ABI {}): no AOT kernel is configured.".format( + "Target OS selection failed for label {} (slice {}): no AOT kernel is configured.".format( ctx.label, - ctx.attr.abi, + _flutter_target_toolchain(ctx).slice_id, ), ) @@ -1012,7 +1097,7 @@ def _dart_aot_elf_impl(ctx): # therefore safe to cache. Verified: identical sha256 over repeat builds. args.add("--deterministic") args.add("--snapshot_kind=app-aot-elf") - args.add_all(ctx.attr.snapshot_flags) + args.add_all(_flutter_target_toolchain(ctx).snapshot_flags) # gen_snapshot only accepts the --flag=value form here; a space-separated # pair makes it print usage and exit non-zero. @@ -1027,10 +1112,7 @@ def _dart_aot_elf_impl(ctx): args.add(ctx.file.dill) ctx.actions.run( - executable = _flutter_toolchain(ctx).gen_snapshots["{}_{}".format( - ctx.attr.abi, - ctx.attr._mode[BuildSettingInfo].value, - )], + executable = _flutter_aot_toolchain(ctx).gen_snapshot_release, arguments = [args], inputs = [ctx.file.dill], outputs = [so], @@ -1051,23 +1133,15 @@ dart_aot_elf = rule( default = True, doc = "Drop DWARF debug info from the ELF.", ), - "abi": attr.string( - mandatory = True, - values = sorted(ABIS.keys()), - ), "_mode": attr.label( default = "//flutter:mode", providers = [BuildSettingInfo], ), - "snapshot_flags": attr.string_list( - doc = """Extra gen_snapshot flags for this ABI. - -Table-driven rather than derived here: armv7 is the only ABI that needs any, -and omitting them yields a snapshot that installs and then executes an -unsupported instruction.""", - ), }, - toolchains = ["//flutter/private:flutter_toolchain_type"], + toolchains = [ + "//flutter/private:flutter_target_toolchain_type", + "//flutter/private:flutter_aot_toolchain_type", + ], ) def _pub_hub_labels(pub, who): @@ -1305,7 +1379,7 @@ def _bundle_dir(out, abi, index): return "$EXECROOT/{}".format(out.path) return "$STAGE/bundles/{}".format(abi) -def _bundle_command(out, abi, index): +def _bundle_command(out, abi, index, target_platform): # `flutter_tool` (defined in the action preamble) runs the tool's snapshot # directly: bin/flutter writes engine.stamp into the SDK, which a sandbox # forbids. `--no-version-check` skips the update check, which runs `git fetch` @@ -1323,8 +1397,8 @@ def _bundle_command(out, abi, index): --suppress-analytics >/dev/null rm -f "{dir}/.last_build_id" rm -rf "{dir}/native_assets" -""".format( - platform = ABIS[abi].target_platform, + """.format( + platform = target_platform, dir = _bundle_dir(out, abi, index), ) @@ -1474,10 +1548,21 @@ python3 "$EXECROOT/{merger}" {merge_args} """.format( snapshot = toolchain.tools_snapshot.path, merger = ctx.file._merger.path, - bundles = "\n".join([_bundle_command(out, abi, i) for i, abi in enumerate(ctx.attr.abis)]), + bundles = "\n".join([ + _bundle_command( + out, + ctx.split_attr.target_slice[str(platform.label)][FlutterTargetSliceInfo].slice_id, + i, + ctx.split_attr.target_slice[str(platform.label)][FlutterTargetSliceInfo].target_platform, + ) + for i, platform in enumerate(ctx.attr.target_platforms) + ]), merge_args = " ".join([ - '--bundle "{}={}"'.format(abi, _bundle_dir(out, abi, i)) - for i, abi in enumerate(ctx.attr.abis) + '--bundle "{}={}"'.format( + ctx.split_attr.target_slice[str(platform.label)][FlutterTargetSliceInfo].slice_id, + _bundle_dir(out, ctx.split_attr.target_slice[str(platform.label)][FlutterTargetSliceInfo].slice_id, i), + ) + for i, platform in enumerate(ctx.attr.target_platforms) ]), ) @@ -1543,14 +1628,13 @@ than a fact read out of it.""", allow_files = True, doc = "Sources of `path:` dependencies, staged and declared. See dart_aot_kernel.path_deps.", ), - "abis": attr.string_list( - mandatory = True, - doc = """ABIs to bundle for. Required, always a list. - -`flutter build bundle` runs once per ABI inside this one action, because -exactly one file it produces -- NativeAssetsManifest.json -- is keyed by -architecture. The manifests are merged and everything else is compared, so a -bundle that started varying by architecture fails here rather than shipping.""", + "target_platforms": attr.label_list( + doc = "Platforms whose target toolchains provide asset-bundle metadata.", + ), + "target_slice": attr.label( + default = Label("//flutter/private:target_slice"), + cfg = _target_platform_split, + doc = "Internal per-platform target-toolchain metadata.", ), "native_build_env": attr.label( providers = [FlutterNativeBuildEnvInfo], diff --git a/rules_flutter_core/flutter/private/toolchain.bzl b/rules_flutter_core/flutter/private/toolchain.bzl index 17732ceb..653b3475 100644 --- a/rules_flutter_core/flutter/private/toolchain.bzl +++ b/rules_flutter_core/flutter/private/toolchain.bzl @@ -1,7 +1,5 @@ """Provider and implementation target for the host Flutter SDK toolchain.""" -load(":abis.bzl", "ABIS", "AOT_MODES") - visibility(["//flutter"]) FlutterToolchainInfo = provider( @@ -19,22 +17,13 @@ FlutterToolchainInfo = provider( "platform_product": "Product patched SDK files.", "platform_debug": "Debug patched SDK files.", "sdk_packages": "Depset of the SDK's Dart package files: every `packages/` package plus sky_engine, as lib/, pubspec.yaml and license files.", - "gen_snapshots": "ABI and AOT mode to gen_snapshot executable file mapping.", "dart": "Dart SDK executable file, used to run the test-kernel driver.", "vm_platform_strong": "VM platform dill the test-kernel driver is compiled against.", "dart_sdk_revision": "Dart SDK revision file; the driver's sdk_hash comes from it.", }, ) -def _gen_snapshot_attr_name(abi, mode): - return "_gen_snapshot_{}_{}".format(abi.replace("-", "_"), mode) - def _flutter_toolchain_impl(ctx): - gen_snapshots = {} - for abi in sorted(ABIS.keys()): - for mode in AOT_MODES: - attr_name = _gen_snapshot_attr_name(abi, mode) - gen_snapshots["{}_{}".format(abi, mode)] = getattr(ctx.executable, attr_name) return [platform_common.ToolchainInfo(flutter = FlutterToolchainInfo( flutter = ctx.file._flutter, tools_snapshot = ctx.file._tools_snapshot, @@ -50,88 +39,27 @@ def _flutter_toolchain_impl(ctx): sdk_version = ctx.file._sdk_version, platform_product = ctx.files._platform_product, platform_debug = ctx.files._platform_debug, - gen_snapshots = gen_snapshots, sdk_packages = depset(ctx.files._sdk_packages), ))] def _toolchain_attrs(): - attrs = { - "_flutter": attr.label( - default = "@flutter_sdk//:flutter", - allow_single_file = True, - ), - "_tools_snapshot": attr.label( - default = "@flutter_sdk//:flutter_tools.snapshot", - allow_single_file = True, - ), - "_dartaotruntime": attr.label( - default = "@flutter_sdk//:dartaotruntime", - executable = True, - cfg = "exec", - allow_single_file = True, - ), - "_dart": attr.label( - default = "@flutter_sdk//:dart", - executable = True, - cfg = "exec", - allow_single_file = True, - ), - "_vm_platform_strong": attr.label( - default = "@flutter_sdk//:vm_platform_strong.dill", - allow_single_file = True, - ), - "_dart_sdk_revision": attr.label( - default = "@flutter_sdk//:dart_sdk_revision", - allow_single_file = True, - ), - "_frontend_server": attr.label( - default = "@flutter_sdk//:frontend_server.snapshot", - allow_single_file = True, - ), - "_vm_isolate_snapshot": attr.label( - default = "@flutter_sdk//:vm_isolate_snapshot.bin", - allow_single_file = True, - ), - "_isolate_snapshot": attr.label( - default = "@flutter_sdk//:isolate_snapshot.bin", - allow_single_file = True, - ), - "_flutter_tester": attr.label( - default = "@flutter_sdk//:flutter_tester", - executable = True, - cfg = "exec", - allow_single_file = True, - ), - "_icu_data": attr.label( - default = "@flutter_sdk//:icudtl.dat", - allow_single_file = True, - ), - "_sdk_version": attr.label( - default = "@flutter_sdk//:flutter.version.json", - allow_single_file = True, - ), - "_platform_product": attr.label( - default = "@flutter_sdk//:platform_product", - allow_files = True, - ), - "_platform_debug": attr.label( - default = "@flutter_sdk//:platform_debug", - allow_files = True, - ), - "_sdk_packages": attr.label( - default = "@flutter_sdk//:sdk_packages", - allow_files = True, - ), + return { + "_flutter": attr.label(default = "@flutter_sdk//:flutter", allow_single_file = True), + "_tools_snapshot": attr.label(default = "@flutter_sdk//:flutter_tools.snapshot", allow_single_file = True), + "_dartaotruntime": attr.label(default = "@flutter_sdk//:dartaotruntime", executable = True, cfg = "exec", allow_single_file = True), + "_dart": attr.label(default = "@flutter_sdk//:dart", executable = True, cfg = "exec", allow_single_file = True), + "_vm_platform_strong": attr.label(default = "@flutter_sdk//:vm_platform_strong.dill", allow_single_file = True), + "_dart_sdk_revision": attr.label(default = "@flutter_sdk//:dart_sdk_revision", allow_single_file = True), + "_frontend_server": attr.label(default = "@flutter_sdk//:frontend_server.snapshot", allow_single_file = True), + "_vm_isolate_snapshot": attr.label(default = "@flutter_sdk//:vm_isolate_snapshot.bin", allow_single_file = True), + "_isolate_snapshot": attr.label(default = "@flutter_sdk//:isolate_snapshot.bin", allow_single_file = True), + "_flutter_tester": attr.label(default = "@flutter_sdk//:flutter_tester", executable = True, cfg = "exec", allow_single_file = True), + "_icu_data": attr.label(default = "@flutter_sdk//:icudtl.dat", allow_single_file = True), + "_sdk_version": attr.label(default = "@flutter_sdk//:flutter.version.json", allow_single_file = True), + "_platform_product": attr.label(default = "@flutter_sdk//:platform_product", allow_files = True), + "_platform_debug": attr.label(default = "@flutter_sdk//:platform_debug", allow_files = True), + "_sdk_packages": attr.label(default = "@flutter_sdk//:sdk_packages", allow_files = True), } - for abi in sorted(ABIS.keys()): - for mode in AOT_MODES: - attrs[_gen_snapshot_attr_name(abi, mode)] = attr.label( - default = "@flutter_sdk//:gen_snapshot_{}_{}".format(abi, mode), - executable = True, - cfg = "exec", - allow_single_file = True, - ) - return attrs flutter_toolchain = rule( implementation = _flutter_toolchain_impl, diff --git a/rules_flutter_core/flutter/target_toolchain.bzl b/rules_flutter_core/flutter/target_toolchain.bzl new file mode 100644 index 00000000..a05646fa --- /dev/null +++ b/rules_flutter_core/flutter/target_toolchain.bzl @@ -0,0 +1,56 @@ +"""Public rules for platform-owned Flutter target toolchains.""" + +FlutterTargetToolchainInfo = provider( + doc = "Target-platform data used by Flutter compilation rules.", + fields = { + "target_os": "Target OS passed to the AOT kernel compiler.", + "snapshot_flags": "Target-specific gen_snapshot flags.", + "native_assets_key": "Flutter native-assets manifest key.", + "target_platform": "Flutter bundle target-platform spelling.", + "slice_id": "Stable identifier for this target slice.", + }, +) + +FlutterAotToolchainInfo = provider( + doc = "AOT compiler executable for a target platform.", + fields = { + "gen_snapshot_release": "Host executable used to produce the release AOT ELF.", + }, +) + +def _flutter_target_toolchain_impl(ctx): + return [platform_common.ToolchainInfo(flutter_target = FlutterTargetToolchainInfo( + target_os = ctx.attr.target_os, + snapshot_flags = ctx.attr.snapshot_flags, + native_assets_key = ctx.attr.native_assets_key, + target_platform = ctx.attr.target_platform, + slice_id = ctx.attr.slice_id, + ))] + +def _flutter_aot_toolchain_impl(ctx): + return [platform_common.ToolchainInfo(flutter_aot = FlutterAotToolchainInfo( + gen_snapshot_release = ctx.executable.gen_snapshot_release, + ))] + +flutter_target_toolchain = rule( + implementation = _flutter_target_toolchain_impl, + attrs = { + "target_os": attr.string(mandatory = True), + "snapshot_flags": attr.string_list(), + "native_assets_key": attr.string(mandatory = True), + "target_platform": attr.string(mandatory = True), + "slice_id": attr.string(mandatory = True), + }, +) + +flutter_aot_toolchain = rule( + implementation = _flutter_aot_toolchain_impl, + attrs = { + "gen_snapshot_release": attr.label( + mandatory = True, + executable = True, + cfg = "exec", + allow_single_file = True, + ), + }, +) diff --git a/rules_flutter_core/tools/ci/key_portability_test.py b/rules_flutter_core/tools/ci/key_portability_test.py index f47da373..c78d0a0c 100755 --- a/rules_flutter_core/tools/ci/key_portability_test.py +++ b/rules_flutter_core/tools/ci/key_portability_test.py @@ -301,8 +301,21 @@ def check_c(self): aot_action = _action_call(aot_function, "run") if aot_action is None: self.fail("C", "_dart_aot_elf_impl must create its AOT action with ctx.actions.run") - if not _rule_uses_toolchain(tree, "dart_aot_elf"): - self.fail("C", "dart_aot_elf must declare the Flutter toolchain type") + else: + executable = _keyword(aot_action, "executable") + if ( + not isinstance(executable, ast.Attribute) + or executable.attr != "gen_snapshot_release" + or _call_path(executable.value) != ["_flutter_aot_toolchain"] + ): + self.fail("C", "DartAotElf executable must come from its declared AOT toolchain") + aot_toolchain = _function(tree, "_flutter_aot_toolchain") + if aot_toolchain is None or not _contains_string(aot_toolchain, "//flutter/private:flutter_aot_toolchain_type"): + self.fail("C", "DartAotElf executable helper must read its declared AOT toolchain") + if not _rule_uses_toolchain(tree, "dart_aot_elf", "//flutter/private:flutter_target_toolchain_type"): + self.fail("C", "dart_aot_elf must declare the Flutter target toolchain type") + if not _rule_uses_toolchain(tree, "dart_aot_elf", "//flutter/private:flutter_aot_toolchain_type"): + self.fail("C", "dart_aot_elf must declare the Flutter AOT toolchain type") for target in ( "_dartaotruntime", diff --git a/tests/consumer/BUILD.bazel b/tests/consumer/BUILD.bazel index 9daa89a4..ed57df58 100644 --- a/tests/consumer/BUILD.bazel +++ b/tests/consumer/BUILD.bazel @@ -7,9 +7,9 @@ targets may also load composition.bzl; Android macros load from load("@bazel_skylib//rules:build_test.bzl", "build_test") load("@rules_flutter_android//flutter:composition.bzl", "flutter_android_inputs", "flutter_embedding_deps") -load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_app", "flutter_assets", "flutter_native_contribution", "flutter_plugin_deps_guards") +load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_app", "flutter_android_core", "flutter_assets", "flutter_native_contribution", "flutter_plugin_deps_guards") load("@rules_flutter_core//flutter:composition.bzl", "pub_path_deps_check") -load("@rules_flutter_core//flutter:defs.bzl", "flutter_core", "flutter_package", "flutter_pubspec", "flutter_test") +load("@rules_flutter_core//flutter:defs.bzl", "flutter_package", "flutter_pubspec", "flutter_test") load("@rules_java//java:defs.bzl", "java_binary", "java_import") load( "@rules_kotlin//kotlin:android.bzl", @@ -97,7 +97,7 @@ flutter_test( # The macro, with every named parameter supplied -- it is the public entry point # most consumers use, and its signature is the thing most likely to drift. -flutter_core( +flutter_android_core( name = "app_core", srcs = ["lib/main.dart"], dart_plugin_registrant = "lib/dart_plugin_registrant.dart", @@ -112,11 +112,11 @@ flutter_core( flutter_assets( name = "assets", - abis = ["arm64-v8a"], assets = [], package_config = "@pub//:project_package_config.json", pub_srcs = ["@pub//:all"], pubspec = ":fixture_pubspec", + target_platforms = ["@rules_android//:arm64-v8a"], ) pub_path_deps_check( diff --git a/tests/consumer/MODULE.bazel.lock b/tests/consumer/MODULE.bazel.lock index dba0728b..c04ab18a 100644 --- a/tests/consumer/MODULE.bazel.lock +++ b/tests/consumer/MODULE.bazel.lock @@ -408,13 +408,12 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%android_ndk": { "general": { - "bzlTransitiveDigest": "ChmCXXuZkbfT0XtrGZ3VW6ftH+vEyMOroOzRtbHokuY=", + "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", "usagesDigest": "AFQI5PN97mnks2yWgIX361TV8306rJ4VPuWmhG1yFNA=", "recordedInputs": [ + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android rules_android+", - "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android_ndk rules_android_ndk+", - "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms" + "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android_ndk rules_android_ndk+" ], "generatedRepoSpecs": { "androidndk": { @@ -430,17 +429,17 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%flutter_plugins_ext": { "general": { - "bzlTransitiveDigest": "ChmCXXuZkbfT0XtrGZ3VW6ftH+vEyMOroOzRtbHokuY=", + "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", "usagesDigest": "MIZKH8xcfb1pscqq81jQhHF4ylGO08PRxzsEQhM4UcY=", "recordedInputs": [ "REPO_MAPPING:,composed_pub sameri11_rules_flutter_core++pub+composed_pub", "REPO_MAPPING:,flutter_maven rules_jvm_external++maven+flutter_maven", "REPO_MAPPING:,namespace_plugin_metadata +namespace_plugin_metadata+namespace_plugin_metadata", "REPO_MAPPING:,plugin_maven rules_jvm_external++maven+plugin_maven", + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android rules_android+", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android_ndk rules_android_ndk+", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms", "REPO_MAPPING:sameri11_rules_flutter_android+,androidndk_cmake sameri11_rules_flutter_android++android_ndk+androidndk_cmake" ], "generatedRepoSpecs": { @@ -541,87 +540,131 @@ }, "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_embedding": { "general": { - "bzlTransitiveDigest": "Cc2IsXwVitTCSUMnh/0e+uh1l6r143p3g2wxU9QzBGg=", + "bzlTransitiveDigest": "3eqHOPboGukDJg90t58rNh9rmebP4fSAVaS1+Xd+5d4=", "usagesDigest": "VDubd1CC/3EsTr4xz8tz3uu65g6E5r8qQxmJ5I7keKE=", "recordedInputs": [ - "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk" + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", + "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+" ], "generatedRepoSpecs": { - "flutter_embedding_debug": { - "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_embedding_jar", - "attributes": { - "mode": "debug" - } - }, "flutter_embedding_release": { "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_embedding_jar", "attributes": { "mode": "release" } - } - } - } - }, - "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { - "general": { - "bzlTransitiveDigest": "AARGgu9wwVVVg2ko+pKNhSOGTtOh12+1knTwjHuxpjo=", - "usagesDigest": "3YgLOQ5KctGblzmwvkmzbmE3OR8fATY6yGn0owwy7n8=", - "recordedInputs": [ - "REPO_MAPPING:sameri11_rules_flutter_core+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms" - ], - "generatedRepoSpecs": { - "flutter_sdk": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_sdk", + }, + "flutter_embedding_debug": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_embedding_jar", "attributes": { - "version": "3.47.3" + "mode": "debug" } }, - "flutter_tools_packages": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_tools_packages", - "attributes": {} - }, "flutter_engine_arm64_v8a_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "arm64_v8a_release", - "extract_libs": true + "artifact": "arm64_v8a_release" } }, "flutter_engine_arm64_v8a_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "arm64_v8a_debug", - "extract_libs": true + "artifact": "arm64_v8a_debug" } }, "flutter_engine_x86_64_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "x86_64_release", - "extract_libs": true + "artifact": "x86_64_release" } }, "flutter_engine_x86_64_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "x86_64_debug", - "extract_libs": true + "artifact": "x86_64_debug" } }, "flutter_engine_armeabi_v7a_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "armeabi_v7a_release", - "extract_libs": true + "artifact": "armeabi_v7a_release" } }, "flutter_engine_armeabi_v7a_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", + "attributes": { + "artifact": "armeabi_v7a_debug" + } + } + } + } + }, + "@@sameri11_rules_flutter_android+//flutter/private:target_toolchain_repo.bzl%flutter_android_toolchains": { + "general": { + "bzlTransitiveDigest": "0D9Q8Q4AQrFirqM6wLGtrv3QlasjcQ2c2i2OxhVYFfI=", + "usagesDigest": "2WhXsIhHLINViYFxmWfqIy1rlYxnWFK11LB9vSRc2M0=", + "recordedInputs": [ + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_gen_snapshot_arm64_v8a sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_arm64_v8a", + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_gen_snapshot_x86_64 sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_x86_64", + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_gen_snapshot_armeabi_v7a sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a", + "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+" + ], + "generatedRepoSpecs": { + "flutter_gen_snapshot_arm64_v8a": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-arm64-release" + } + }, + "flutter_gen_snapshot_x86_64": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-x64-release" + } + }, + "flutter_gen_snapshot_armeabi_v7a": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-arm-release" + } + }, + "flutter_android_target_toolchains": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:target_toolchain_repo.bzl%_android_toolchains", + "attributes": { + "aot_toolchain_targets": { + "arm64-v8a": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_arm64_v8a//:aot_toolchain'", + "x86_64": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_x86_64//:aot_toolchain'", + "armeabi-v7a": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a//:aot_toolchain'" + }, + "aot_toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:aot_toolchain_type'", + "toolchain_targets": { + "arm64-v8a": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_arm64_v8a'", + "x86_64": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_x86_64'", + "armeabi-v7a": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_armeabi_v7a'" + }, + "toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:target_toolchain_type'" + } + } + } + } + }, + "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { + "general": { + "bzlTransitiveDigest": "Et4GM1IW3LCV7ah8t1qEeCgh9cOh1ek26YeJOVdYI+0=", + "usagesDigest": "3YgLOQ5KctGblzmwvkmzbmE3OR8fATY6yGn0owwy7n8=", + "recordedInputs": [], + "generatedRepoSpecs": { + "flutter_sdk": { + "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_sdk", "attributes": { - "artifact": "armeabi_v7a_debug", - "extract_libs": true + "version": "3.47.3" } + }, + "flutter_tools_packages": { + "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_tools_packages", + "attributes": {} } } } diff --git a/tests/consumer/composed_app/BUILD.bazel b/tests/consumer/composed_app/BUILD.bazel index ebcf07a2..599380b7 100644 --- a/tests/consumer/composed_app/BUILD.bazel +++ b/tests/consumer/composed_app/BUILD.bazel @@ -15,8 +15,8 @@ load( "@rules_flutter_android//flutter:defs.bzl", "FLUTTER_DEBUG_UNCOMPRESSED_ASSETS", "flutter_android_app", + "flutter_android_core", ) -load("@rules_flutter_core//flutter:defs.bzl", "flutter_core") load("@rules_java//java:defs.bzl", "java_import", "java_library") load("@rules_java//java:java_single_jar.bzl", "java_single_jar") load("@rules_kotlin//kotlin:android.bzl", "kt_android_library") @@ -30,7 +30,7 @@ exports_files([ "recipe_package.bzl", ]) -flutter_core( +flutter_android_core( name = "core", srcs = glob(["lib/**/*.dart"]), assets = ["assets/composed-marker.txt"], @@ -295,8 +295,7 @@ filegroup( filegroup( name = "direct_engine", - srcs = [":core"], - output_group = "flutter_engine", + srcs = ["@rules_flutter_android//flutter:engine_libs"], tags = ["manual"], ) diff --git a/tests/consumer/external_app/app/BUILD.bazel b/tests/consumer/external_app/app/BUILD.bazel index 8cc740c8..4324b401 100644 --- a/tests/consumer/external_app/app/BUILD.bazel +++ b/tests/consumer/external_app/app/BUILD.bazel @@ -1,9 +1,9 @@ load("@rules_flutter_android//flutter:composition.bzl", "flutter_android_inputs") -load("@rules_flutter_core//flutter:defs.bzl", "flutter_core") +load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_core") package(default_visibility = ["//visibility:public"]) -flutter_core( +flutter_android_core( name = "app", entrypoint = "lib/alternate$release.dart", native_build_env = "@rules_flutter_android//flutter:native_build_env", diff --git a/tests/consumer/hand_wired_app/BUILD.bazel b/tests/consumer/hand_wired_app/BUILD.bazel index a193c48b..d7e14efd 100644 --- a/tests/consumer/hand_wired_app/BUILD.bazel +++ b/tests/consumer/hand_wired_app/BUILD.bazel @@ -1,11 +1,11 @@ load("@rules_android//rules:rules.bzl", "android_binary", "android_library") load("@rules_flutter_android//flutter:composition.bzl", "flutter_build_config", "flutter_embedding_deps", "flutter_plugin_registrant") -load("@rules_flutter_core//flutter:defs.bzl", "flutter_core") +load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_core") load("@rules_java//java:defs.bzl", "java_import") package(default_visibility = ["//visibility:private"]) -flutter_core( +flutter_android_core( name = "core", srcs = glob(["lib/**/*.dart"]), path_deps = [ diff --git a/tests/consumer/standard_layout/BUILD.bazel b/tests/consumer/standard_layout/BUILD.bazel index ab0f6b37..575cb1b7 100644 --- a/tests/consumer/standard_layout/BUILD.bazel +++ b/tests/consumer/standard_layout/BUILD.bazel @@ -1,9 +1,9 @@ # A standard Flutter package layout with a nondefault entrypoint. -load("@rules_flutter_core//flutter:defs.bzl", "flutter_core") +load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_core") package(default_visibility = ["//visibility:public"]) -flutter_core( +flutter_android_core( name = "core", entrypoint = "lib/alternate$release.dart", native_build_env = "@rules_flutter_android//flutter:native_build_env", diff --git a/tests/core_consumer/MODULE.bazel.lock b/tests/core_consumer/MODULE.bazel.lock index 6ba05dbd..32c7b48b 100644 --- a/tests/core_consumer/MODULE.bazel.lock +++ b/tests/core_consumer/MODULE.bazel.lock @@ -467,12 +467,9 @@ }, "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { "general": { - "bzlTransitiveDigest": "IQKKhDRQ2nv9172jyBf+8QrvfO6OoJlhXoR8YS+5fA8=", + "bzlTransitiveDigest": "Et4GM1IW3LCV7ah8t1qEeCgh9cOh1ek26YeJOVdYI+0=", "usagesDigest": "M9zv5xCGzmNUd7ho3lam884NDO9+c0DK/RoIPwdODYA=", - "recordedInputs": [ - "REPO_MAPPING:sameri11_rules_flutter_core+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms" - ], + "recordedInputs": [], "generatedRepoSpecs": { "flutter_sdk": { "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_sdk", @@ -483,60 +480,6 @@ "flutter_tools_packages": { "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_tools_packages", "attributes": {} - }, - "flutter_embedding_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "flutter_embedding_release" - } - }, - "flutter_embedding_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "flutter_embedding_debug" - } - }, - "flutter_engine_arm64_v8a_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "arm64_v8a_release", - "extract_libs": true - } - }, - "flutter_engine_arm64_v8a_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "arm64_v8a_debug", - "extract_libs": true - } - }, - "flutter_engine_x86_64_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "x86_64_release", - "extract_libs": true - } - }, - "flutter_engine_x86_64_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "x86_64_debug", - "extract_libs": true - } - }, - "flutter_engine_armeabi_v7a_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "armeabi_v7a_release", - "extract_libs": true - } - }, - "flutter_engine_armeabi_v7a_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", - "attributes": { - "artifact": "armeabi_v7a_debug", - "extract_libs": true - } } } } diff --git a/tests/namespace_dynamic/MODULE.bazel.lock b/tests/namespace_dynamic/MODULE.bazel.lock index 87796720..b29207f5 100644 --- a/tests/namespace_dynamic/MODULE.bazel.lock +++ b/tests/namespace_dynamic/MODULE.bazel.lock @@ -404,13 +404,12 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%android_ndk": { "general": { - "bzlTransitiveDigest": "ChmCXXuZkbfT0XtrGZ3VW6ftH+vEyMOroOzRtbHokuY=", + "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", "usagesDigest": "EZM/Ob3Auw0v6B0CQI9nhTMvFryd8ZLaCRIMfYFeY0s=", "recordedInputs": [ + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android rules_android+", - "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android_ndk rules_android_ndk+", - "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms" + "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android_ndk rules_android_ndk+" ], "generatedRepoSpecs": { "androidndk": { @@ -426,14 +425,14 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%flutter_plugins_ext": { "general": { - "bzlTransitiveDigest": "ChmCXXuZkbfT0XtrGZ3VW6ftH+vEyMOroOzRtbHokuY=", + "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", "usagesDigest": "RqVa86t+EWrrGg+0ZGJnQnjPe2QKakZHbi5XlHpeEh8=", "recordedInputs": [ "REPO_MAPPING:,dynamic_namespace_metadata +dynamic_namespace_metadata+dynamic_namespace_metadata", + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android rules_android+", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_android_ndk rules_android_ndk+", "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms", "REPO_MAPPING:sameri11_rules_flutter_android+,androidndk_cmake sameri11_rules_flutter_android++android_ndk+androidndk_cmake" ], "generatedRepoSpecs": { @@ -466,87 +465,131 @@ }, "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_embedding": { "general": { - "bzlTransitiveDigest": "Cc2IsXwVitTCSUMnh/0e+uh1l6r143p3g2wxU9QzBGg=", + "bzlTransitiveDigest": "3eqHOPboGukDJg90t58rNh9rmebP4fSAVaS1+Xd+5d4=", "usagesDigest": "VDubd1CC/3EsTr4xz8tz3uu65g6E5r8qQxmJ5I7keKE=", "recordedInputs": [ - "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk" + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", + "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+" ], "generatedRepoSpecs": { - "flutter_embedding_debug": { - "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_embedding_jar", - "attributes": { - "mode": "debug" - } - }, "flutter_embedding_release": { "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_embedding_jar", "attributes": { "mode": "release" } - } - } - } - }, - "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { - "general": { - "bzlTransitiveDigest": "AARGgu9wwVVVg2ko+pKNhSOGTtOh12+1knTwjHuxpjo=", - "usagesDigest": "4778tf6hjNJFXpm3IbdSjOP2Pn3Qa43Y+4cT+Z0KOnk=", - "recordedInputs": [ - "REPO_MAPPING:sameri11_rules_flutter_core+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", - "REPO_MAPPING:sameri11_rules_flutter_core+,platforms platforms" - ], - "generatedRepoSpecs": { - "flutter_sdk": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_sdk", + }, + "flutter_embedding_debug": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_embedding_jar", "attributes": { - "version": "" + "mode": "debug" } }, - "flutter_tools_packages": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_tools_packages", - "attributes": {} - }, "flutter_engine_arm64_v8a_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "arm64_v8a_release", - "extract_libs": true + "artifact": "arm64_v8a_release" } }, "flutter_engine_arm64_v8a_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "arm64_v8a_debug", - "extract_libs": true + "artifact": "arm64_v8a_debug" } }, "flutter_engine_x86_64_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "x86_64_release", - "extract_libs": true + "artifact": "x86_64_release" } }, "flutter_engine_x86_64_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "x86_64_debug", - "extract_libs": true + "artifact": "x86_64_debug" } }, "flutter_engine_armeabi_v7a_release": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", "attributes": { - "artifact": "armeabi_v7a_release", - "extract_libs": true + "artifact": "armeabi_v7a_release" } }, "flutter_engine_armeabi_v7a_debug": { - "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_engine_jar", + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_engine_jar", + "attributes": { + "artifact": "armeabi_v7a_debug" + } + } + } + } + }, + "@@sameri11_rules_flutter_android+//flutter/private:target_toolchain_repo.bzl%flutter_android_toolchains": { + "general": { + "bzlTransitiveDigest": "0D9Q8Q4AQrFirqM6wLGtrv3QlasjcQ2c2i2OxhVYFfI=", + "usagesDigest": "2WhXsIhHLINViYFxmWfqIy1rlYxnWFK11LB9vSRc2M0=", + "recordedInputs": [ + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", + "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_gen_snapshot_arm64_v8a sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_arm64_v8a", + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_gen_snapshot_x86_64 sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_x86_64", + "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_gen_snapshot_armeabi_v7a sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a", + "REPO_MAPPING:sameri11_rules_flutter_android+,rules_flutter_core sameri11_rules_flutter_core+" + ], + "generatedRepoSpecs": { + "flutter_gen_snapshot_arm64_v8a": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-arm64-release" + } + }, + "flutter_gen_snapshot_x86_64": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-x64-release" + } + }, + "flutter_gen_snapshot_armeabi_v7a": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:gen_snapshot_repo.bzl%flutter_gen_snapshot", + "attributes": { + "engine_dir": "android-arm-release" + } + }, + "flutter_android_target_toolchains": { + "repoRuleId": "@@sameri11_rules_flutter_android+//flutter/private:target_toolchain_repo.bzl%_android_toolchains", + "attributes": { + "aot_toolchain_targets": { + "arm64-v8a": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_arm64_v8a//:aot_toolchain'", + "x86_64": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_x86_64//:aot_toolchain'", + "armeabi-v7a": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a//:aot_toolchain'" + }, + "aot_toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:aot_toolchain_type'", + "toolchain_targets": { + "arm64-v8a": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_arm64_v8a'", + "x86_64": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_x86_64'", + "armeabi-v7a": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_armeabi_v7a'" + }, + "toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:target_toolchain_type'" + } + } + } + } + }, + "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { + "general": { + "bzlTransitiveDigest": "Et4GM1IW3LCV7ah8t1qEeCgh9cOh1ek26YeJOVdYI+0=", + "usagesDigest": "4778tf6hjNJFXpm3IbdSjOP2Pn3Qa43Y+4cT+Z0KOnk=", + "recordedInputs": [], + "generatedRepoSpecs": { + "flutter_sdk": { + "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_sdk", "attributes": { - "artifact": "armeabi_v7a_debug", - "extract_libs": true + "version": "" } + }, + "flutter_tools_packages": { + "repoRuleId": "@@sameri11_rules_flutter_core+//flutter/private:repo.bzl%flutter_tools_packages", + "attributes": {} } } } diff --git a/tools/ci/core_target_os_rejection.py b/tools/ci/core_target_os_rejection.py index f8842724..64eb3943 100755 --- a/tools/ci/core_target_os_rejection.py +++ b/tools/ci/core_target_os_rejection.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Probe configured libapp rejection and guard against an Android fallback.""" +"""Probe that a host-configured macro-generated libapp rejects as Android-incompatible.""" import os import re @@ -22,7 +22,7 @@ def run(workspace, output_user_root): "--nobuild", "--lockfile_mode=update", "--platforms=@platforms//host:host", - "//:app_core_libapp_arm64-v8a", + "//:app_core_libapp", ], cwd=workspace, stdout=subprocess.PIPE, @@ -40,7 +40,6 @@ def main(): core_copy, ignore=shutil.ignore_patterns("bazel-*", "MODULE.bazel.lock", "*.checked"), ) - core_rules_copy = core_copy / "flutter/private/core.bzl" workspace = tmp / "consumer" shutil.copytree( ROOT / "tests/consumer", @@ -65,12 +64,12 @@ def main(): if rejected.returncode == 0: sys.exit("FAIL: host-configured macro-generated libapp unexpectedly analyzed") explicit_error = ( - "Target //:app_core_libapp_arm64-v8a is incompatible and cannot be built, " + "Target //:app_core_libapp is incompatible and cannot be built, " "but was explicitly requested" ) if explicit_error not in rejected.stdout: sys.exit("FAIL: rejection lacked {!r}:\n{}".format(explicit_error, rejected.stdout)) - target_line = "//:app_core_libapp_arm64-v8a" + target_line = "//:app_core_libapp" dependency_chain = rejected.stdout.partition("Dependency chain:")[2] has_target_os_rejection = ( target_line in dependency_chain @@ -85,27 +84,7 @@ def main(): target_line, rejected.stdout ) ) - original = core_rules_copy.read_text() - fallback = '"//conditions:default": None,' - planted = '"//conditions:default": ":" + name + "_kernel_neutral",' - compatibility = 'target_compatible_with = [Label("@platforms//os:android"), ABIS[abi].cpu_constraint] + aot_target_compatible_with(),' - if original.count(fallback) != 1 or original.count(compatibility) != 1: - sys.exit("FAIL: could not uniquely locate rejection and compatibility declarations") - try: - core_rules_copy.write_text( - original.replace(fallback, planted, 1).replace( - compatibility, - "target_compatible_with = [],", - 1, - ) - ) - accepted = run(workspace, output_user_root) - finally: - core_rules_copy.write_text(original) - if accepted.returncode != 0: - sys.exit("FAIL: planted Android fallback was not detected by the probe:\n" + accepted.stdout) - - print("PASS: host libapp rejects as incompatible; planted fallback builds") + print("PASS: host libapp rejects as incompatible with the Android OS constraint") finally: subprocess.run( [BAZEL, "--output_user_root=" + str(output_user_root), "shutdown"], diff --git a/tools/ci/test_kernel_invariants.sh b/tools/ci/test_kernel_invariants.sh index 46f6299b..4cdfc8d1 100755 --- a/tools/ci/test_kernel_invariants.sh +++ b/tools/ci/test_kernel_invariants.sh @@ -100,15 +100,15 @@ fi # its BUILD.bazel is restored on exit. find_build() { find . -name BUILD.bazel -not -path './bazel-*' -print | LC_ALL=C sort | while read -r f; do - if grep -q "$1" "$f"; then echo "$f"; fi + if grep -E -q "$1" "$f"; then echo "$f"; fi done | sed -n 1p } -app_build=$(find_build '^flutter_core(') +app_build=$(find_build '^flutter_(android_)?core\(') if [ -z "$app_build" ]; then - echo "FAIL: no flutter_core package for the undeclared-import probe" >&2 + echo "FAIL: no flutter_core or flutter_android_core package for the undeclared-import probe" >&2 exit 1 fi -core_name=$(awk '/^flutter_core\(/ { f = 1 } f && /name = "/ { match($0, /"[^"]*"/); print substr($0, RSTART + 1, RLENGTH - 2); exit }' "$app_build") +core_name=$(awk '/^flutter_(android_)?core\(/ { f = 1 } f && /name = "/ { match($0, /"[^"]*"/); print substr($0, RSTART + 1, RLENGTH - 2); exit }' "$app_build") app_target=":${core_name}_test_package" probe_dir=$(dirname "$app_build") app_pkg=${probe_dir#.} From 7d7cda647fada19418cfaed8ad08e59a04ad6919 Mon Sep 17 00:00:00 2001 From: Mikhail Novoseltsev <51940183+Sameri11@users.noreply.github.com> Date: Wed, 11 Nov 2026 21:35:10 +0500 Subject: [PATCH 2/2] Add a neutral platform composition contract --- .github/workflows/ci.yml | 6 + QUICKSTART.md | 52 +++- README.md | 38 ++- examples/demo_app/BUILD.bazel | 7 +- examples/demo_app/MODULE.bazel.lock | 15 +- examples/local_plugin/MODULE.bazel.lock | 15 +- .../packages/host_app/BUILD.bazel | 7 +- examples/no_plugins/BUILD.bazel | 13 +- examples/no_plugins/MODULE.bazel.lock | 13 +- .../no_plugins/integration_test/BUILD.bazel | 2 + examples/pub_plugins/BUILD.bazel | 8 +- examples/pub_plugins/MODULE.bazel.lock | 15 +- rules_flutter_android/MODULE.bazel.lock | 15 +- rules_flutter_android/flutter/BUILD.bazel | 7 +- rules_flutter_android/flutter/abis.bzl | 7 +- .../flutter/android_core.bzl | 12 - rules_flutter_android/flutter/defs.bzl | 89 +++++- .../flutter/private/android.bzl | 19 +- .../flutter/private/android_app.bzl | 23 +- .../flutter/private/android_inputs_rules.bzl | 7 +- .../flutter/private/embedding_repo.bzl | 2 +- .../flutter/private/gen_snapshot_repo.bzl | 5 + .../flutter/private/target_toolchain_repo.bzl | 14 +- .../flutter/private/target_toolchains.bzl | 1 - rules_flutter_core/MODULE.bazel | 6 +- rules_flutter_core/MODULE.bazel.lock | 2 +- rules_flutter_core/flutter/BUILD.bazel | 6 +- rules_flutter_core/flutter/composition.bzl | 3 +- rules_flutter_core/flutter/defs.bzl | 32 +- rules_flutter_core/flutter/extensions.bzl | 3 +- .../flutter/{abis.bzl => modes.bzl} | 2 +- .../flutter/private/BUILD.bazel | 16 +- rules_flutter_core/flutter/private/core.bzl | 285 ++++++++++-------- .../flutter/private/debug_core.bzl | 149 +++++---- .../flutter/private/integration_test.bzl | 4 +- .../flutter/private/merge_native_assets.py | 55 ++-- .../flutter/private/{abis.bzl => modes.bzl} | 0 .../flutter/private/pub_lock.bzl | 2 +- .../flutter/private/pubspec.bzl | 3 +- rules_flutter_core/flutter/private/repo.bzl | 8 +- rules_flutter_core/flutter/private/rules.bzl | 246 ++++++++------- .../flutter/target_toolchain.bzl | 22 +- .../tools/ci/key_portability_test.py | 28 +- tests/consumer/BUILD.bazel | 7 +- tests/consumer/MODULE.bazel | 11 +- tests/consumer/MODULE.bazel.lock | 15 +- tests/consumer/composed_app/BUILD.bazel | 15 +- tests/consumer/external_app/app/BUILD.bazel | 6 +- tests/consumer/fake_platform/BUILD.bazel | 49 +++ tests/consumer/fake_platform/defs.bzl | 49 +++ tests/consumer/fake_platform/fake_app.bzl | 61 ++++ .../fake_platform/fake_gen_snapshot.bzl | 26 ++ tests/consumer/hand_wired_app/BUILD.bazel | 10 +- .../mixed_platform/AndroidManifest.xml | 4 + tests/consumer/mixed_platform/BUILD.bazel | 48 +++ .../consumer/mixed_platform/assets/marker.txt | 1 + tests/consumer/mixed_platform/lib/main.dart | 5 + tests/consumer/mixed_platform/pubspec.lock | 55 ++++ tests/consumer/mixed_platform/pubspec.yaml | 9 + tests/consumer/standard_layout/BUILD.bazel | 6 +- tests/core_consumer/MODULE.bazel.lock | 2 +- tests/namespace_dynamic/MODULE.bazel.lock | 15 +- tools/ci/core_target_os_rejection.py | 6 +- tools/ci/native_hook_env_analysis.py | 2 +- tools/ci/test_apk_kernel_check.py | 9 +- tools/ci/test_kernel_invariants.sh | 6 +- 66 files changed, 1159 insertions(+), 522 deletions(-) delete mode 100644 rules_flutter_android/flutter/android_core.bzl rename rules_flutter_core/flutter/{abis.bzl => modes.bzl} (59%) rename rules_flutter_core/flutter/private/{abis.bzl => modes.bzl} (100%) create mode 100644 tests/consumer/fake_platform/BUILD.bazel create mode 100644 tests/consumer/fake_platform/defs.bzl create mode 100644 tests/consumer/fake_platform/fake_app.bzl create mode 100644 tests/consumer/fake_platform/fake_gen_snapshot.bzl create mode 100644 tests/consumer/mixed_platform/AndroidManifest.xml create mode 100644 tests/consumer/mixed_platform/BUILD.bazel create mode 100644 tests/consumer/mixed_platform/assets/marker.txt create mode 100644 tests/consumer/mixed_platform/lib/main.dart create mode 100644 tests/consumer/mixed_platform/pubspec.lock create mode 100644 tests/consumer/mixed_platform/pubspec.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55d81d05..66c0242b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -215,6 +215,12 @@ jobs: 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" + + - name: Consumer mixed-platform debug and Android asset selection + working-directory: tests/consumer + run: | + set -euo pipefail + 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 //mixed_platform:fake_debug_app //mixed_platform:android_app_mixed_debug //mixed_platform:android_app # Composed app: the APK check fails the build on a planted mismatch. - name: Consumer composed app release APK check working-directory: tests/consumer diff --git a/QUICKSTART.md b/QUICKSTART.md index d5e6ef46..cba91121 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -227,23 +227,27 @@ Start from a Flutter Android project with `pubspec.yaml`, current assets, set `assets = []` explicitly. Create root `BUILD.bazel`: ```python +load("@rules_flutter_android//flutter:defs.bzl", "FLUTTER_ANDROID") load("@rules_flutter_core//flutter:defs.bzl", "flutter_core") package(default_visibility = ["//visibility:public"]) flutter_core( name = "core", + platforms = [FLUTTER_ANDROID], srcs = glob(["lib/**/*.dart"]), assets = [], - native_build_env = "@rules_flutter_android//flutter:native_build_env", pub = "@pub", ) ``` If a production package (including the app package) contains a staged `hook/build.dart`, `flutter_core` requires `native_build_env` during analysis. -The Android environment shown above supplies the SDK needed by Flutter's native -build-hook runner. Dev-only dependencies do not trigger this requirement. +`FLUTTER_ANDROID` does not supply an environment by default. Set +`native_build_env = "@rules_flutter_android//flutter:native_build_env"` on +`flutter_core` when hooks need Android SDK inputs. + +Dev-only dependencies do not trigger this requirement. Host-test asset targets do not run native build hooks and need no app environment. @@ -369,12 +373,14 @@ relative-import helpers: ```python # integration_test/BUILD.bazel load("@rules_flutter_core//flutter:composition.bzl", "flutter_debug_core") +load("@rules_flutter_android//flutter:defs.bzl", "FLUTTER_ANDROID") flutter_debug_core( name = "prebuilt_core", app = "//:core", entrypoint = "smoke.dart", srcs = ["helper.dart"], + platforms = [FLUTTER_ANDROID], dart_defines = {"TEST_MARK": "prebuilt"}, ) ``` @@ -560,9 +566,11 @@ the app package and point the Android helper at that package-local target. For example, the app core declares the plugin source target: ```python +load("@rules_flutter_android//flutter:defs.bzl", "FLUTTER_ANDROID") flutter_core( name = "app", path_deps = ["//packages/greeter:srcs"], + platforms = [FLUTTER_ANDROID], pub = "@pub", ) ``` @@ -619,11 +627,36 @@ shows recipe inputs and ABI-specific native contributions. ## Consumer-owned stock android_binary 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` and `flutter_libapp` output groups; -`flutter_libapp` is empty in debug mode. Android engine libraries, possibly -several per ABI, come from `@rules_flutter_android//flutter:engine_libs`. The -Android composition module provides `flutter_plugin_registrant`, +needs to own Android packaging can combine the public building blocks: +`flutter_core` exposes the `flutter_assets` and `flutter_aot` output groups; +Android engine libraries come from +`@rules_flutter_android//flutter:engine_libs`. Declare the platform with +`flutter_core(platforms = [FLUTTER_ANDROID])`. + +Android app assets resolve the core under the first `--android_platforms` value, +or the first `FLUTTER_ANDROID` target platform when that flag is unset. +Hand-wired selects follow the requesting target's platform configuration and +default to the first descriptor. + +Breaking changes: `platforms` is required on `flutter_core`, +`flutter_debug_core`, and `flutter_integration_test`. +`flutter_assets(target_slices = ...)` and Android wrapper +`flutter_assets(target_platforms = ...)` replace the old ABI-oriented +`flutter_assets(abis = ...)` API. `FlutterArtifactInfo.abi`, `.libapp`, and +`.engine_libs` become `.target_slice`, `.aot`, and `.aot_kind`. +`flutter_libapp` is renamed to `flutter_aot`; the `flutter_engine` output group +and public `engine_jar_label` are removed. Android engine libraries now come +from `@rules_flutter_android//flutter:engine_libs` or explicit +`engine_libs__` overrides. Core's `flutter/abis.bzl` is removed: +load Android's `@rules_flutter_android//flutter:abis.bzl` for `ABIS` and core's +`@rules_flutter_core//flutter:modes.bzl` for `MODES`. A platform descriptor may +provide `native_build_env`, which `flutter_core` can override per platform. +AOT and asset actions miss caches once after upgrading. +Hand-wired Android `assets_dir` changes from `core_bundle_action` to the +selected output directory, `__bundle_action` (for example, +`core_android_bundle_action`). + +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 @@ -702,12 +735,13 @@ Place each `integration_test` source in the package root and declare it with ```python load("@rules_flutter_core//flutter:defs.bzl", "flutter_integration_test") -load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_app") +load("@rules_flutter_android//flutter:defs.bzl", "FLUTTER_ANDROID", "flutter_android_app") flutter_integration_test( name = "login", app = ":core", src = "integration_test/login_test.dart", + platforms = [FLUTTER_ANDROID], ) flutter_android_app( diff --git a/README.md b/README.md index 340cb503..7a4d582a 100644 --- a/README.md +++ b/README.md @@ -71,11 +71,34 @@ configurations. Capture each APK before the next configuration because the materialized APK path may be shared. Core output groups remain available to hand-wired consumers: `flutter_assets` -and `flutter_libapp` (empty in debug mode). Android engine libraries, possibly -several per ABI, are provided by `@rules_flutter_android//flutter:engine_libs`. -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. +and `flutter_aot` (empty in debug mode). Android engine libraries are provided +by `@rules_flutter_android//flutter:engine_libs`; callers compose the core API +with `flutter_core(platforms = [FLUTTER_ANDROID])`. +The Android app adapter resolves this group using the first `--android_platforms` +value, or the first `FLUTTER_ANDROID` target platform when that flag is unset. +Hand-wired selects follow the requesting target's platform configuration and +default to the first descriptor. +The embedding is owned by the Android ruleset; use +`@rules_flutter_android//flutter:embedding_jar` for its mode-selected jar and +`flutter_embedding_deps(maven_repo)` for its explicit AndroidX classpath. + +Breaking changes: `platforms` is required on `flutter_core`, +`flutter_debug_core`, and `flutter_integration_test`. +`flutter_assets(target_slices = ...)` and Android wrapper +`flutter_assets(target_platforms = ...)` replace the old ABI-oriented +`flutter_assets(abis = ...)` API. `FlutterArtifactInfo.abi`, `.libapp`, and +`.engine_libs` become `.target_slice`, `.aot`, and `.aot_kind`. +`flutter_libapp` is renamed to `flutter_aot`; the `flutter_engine` output group +and public `engine_jar_label` are removed. Android engine libraries now come +from `@rules_flutter_android//flutter:engine_libs` or explicit +`engine_libs__` overrides. Core's `flutter/abis.bzl` is removed: +load Android's `@rules_flutter_android//flutter:abis.bzl` for `ABIS` and core's +`@rules_flutter_core//flutter:modes.bzl` for `MODES`. A platform descriptor may +provide `native_build_env`, which `flutter_core` can override per platform. +Hand-wired Android `assets_dir` changes from `core_bundle_action` to the +selected output directory, `__bundle_action` (for example, +`core_android_bundle_action`). +AOT and asset actions miss caches once after upgrading. 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 @@ -196,6 +219,7 @@ module and `.bazelrc` configuration. Create the root BUILD file: ```python +load("@rules_flutter_android//flutter:defs.bzl", "FLUTTER_ANDROID") load("@rules_flutter_core//flutter:defs.bzl", "flutter_core") package(default_visibility = ["//visibility:public"]) @@ -203,6 +227,7 @@ package(default_visibility = ["//visibility:public"]) flutter_core( name = "core", assets = [], + platforms = [FLUTTER_ANDROID], pub = "@pub", ) ``` @@ -268,12 +293,13 @@ package root, then attach it to the Android app: ```python load("@rules_flutter_core//flutter:defs.bzl", "flutter_integration_test") -load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_app") +load("@rules_flutter_android//flutter:defs.bzl", "FLUTTER_ANDROID", "flutter_android_app") flutter_integration_test( name = "login", app = ":core", src = "integration_test/login_test.dart", + platforms = [FLUTTER_ANDROID], ) flutter_android_app( diff --git a/examples/demo_app/BUILD.bazel b/examples/demo_app/BUILD.bazel index 743792c4..a48c035b 100644 --- a/examples/demo_app/BUILD.bazel +++ b/examples/demo_app/BUILD.bazel @@ -1,19 +1,20 @@ # Dart targets for the flat demo app. The core owns the standard Flutter package # layout, and its outputs are composed into Android targets in android/app. -load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_core", "flutter_plugin_deps_guards") -load("@rules_flutter_core//flutter:defs.bzl", "flutter_test", "flutter_test_suite") +load("@rules_flutter_android//flutter:defs.bzl", "FLUTTER_ANDROID", "flutter_plugin_deps_guards") +load("@rules_flutter_core//flutter:defs.bzl", "flutter_core", "flutter_test", "flutter_test_suite") package(default_visibility = ["//visibility:public"]) flutter_plugin_deps_guards(plugins = "@flutter_plugins") -flutter_android_core( +flutter_core( name = "app", srcs = glob(["lib/**/*.dart"]) + [":lib/gen_info.dart"], dart_plugin_registrant = "lib/dart_plugin_registrant.dart", native_build_env = "@rules_flutter_android//flutter:native_build_env", path_deps = ["//packages/mylib:srcs"], + platforms = [FLUTTER_ANDROID], pub = "@pub", ) diff --git a/examples/demo_app/MODULE.bazel.lock b/examples/demo_app/MODULE.bazel.lock index 8526fb56..4bff01e4 100644 --- a/examples/demo_app/MODULE.bazel.lock +++ b/examples/demo_app/MODULE.bazel.lock @@ -409,7 +409,7 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%android_ndk": { "general": { - "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", + "bzlTransitiveDigest": "ookO9nSEvbJqeBy/tdtGBn7TVQtv0GXBN6yGHHqsAC4=", "usagesDigest": "HFu4wseopxonsvDUd/4S1JEEWdwTssTyzt3rsp61Cg8=", "recordedInputs": [ "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", @@ -430,7 +430,7 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%flutter_plugins_ext": { "general": { - "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", + "bzlTransitiveDigest": "ookO9nSEvbJqeBy/tdtGBn7TVQtv0GXBN6yGHHqsAC4=", "usagesDigest": "NAsegc0NGgbnISf1WUvMP9+cZAMUJEHXC+unOFzUAKo=", "recordedInputs": [ "REPO_MAPPING:,flutter_maven rules_jvm_external++maven+flutter_maven", @@ -476,7 +476,7 @@ }, "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_embedding": { "general": { - "bzlTransitiveDigest": "3eqHOPboGukDJg90t58rNh9rmebP4fSAVaS1+Xd+5d4=", + "bzlTransitiveDigest": "1rUycB7XyOhDdVVL+3WGQn0chQV5pDYB+oR++p5zzKA=", "usagesDigest": "VDubd1CC/3EsTr4xz8tz3uu65g6E5r8qQxmJ5I7keKE=", "recordedInputs": [ "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", @@ -537,7 +537,7 @@ }, "@@sameri11_rules_flutter_android+//flutter/private:target_toolchain_repo.bzl%flutter_android_toolchains": { "general": { - "bzlTransitiveDigest": "0D9Q8Q4AQrFirqM6wLGtrv3QlasjcQ2c2i2OxhVYFfI=", + "bzlTransitiveDigest": "qLNqwJd+HIxwhKWP/Y5+OzPbh0rMlz/HFaIdkadHxuM=", "usagesDigest": "2WhXsIhHLINViYFxmWfqIy1rlYxnWFK11LB9vSRc2M0=", "recordedInputs": [ "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", @@ -575,6 +575,11 @@ "armeabi-v7a": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a//:aot_toolchain'" }, "aot_toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:aot_toolchain_type'", + "cpu_constraints": { + "arm64-v8a": "'@@platforms//cpu:arm64'", + "x86_64": "'@@platforms//cpu:x86_64'", + "armeabi-v7a": "'@@platforms//cpu:armv7'" + }, "toolchain_targets": { "arm64-v8a": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_arm64_v8a'", "x86_64": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_x86_64'", @@ -588,7 +593,7 @@ }, "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { "general": { - "bzlTransitiveDigest": "Et4GM1IW3LCV7ah8t1qEeCgh9cOh1ek26YeJOVdYI+0=", + "bzlTransitiveDigest": "GRk2y7lduN/jEGLmTrEJhuPbFjOzVyU48T30FcvhBYU=", "usagesDigest": "PVRq/fIGaef1uAaEjTR0LCJbig9Z96itWnzuvhhPV74=", "recordedInputs": [], "generatedRepoSpecs": { diff --git a/examples/local_plugin/MODULE.bazel.lock b/examples/local_plugin/MODULE.bazel.lock index 85c44615..f6107d0b 100644 --- a/examples/local_plugin/MODULE.bazel.lock +++ b/examples/local_plugin/MODULE.bazel.lock @@ -409,7 +409,7 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%android_ndk": { "general": { - "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", + "bzlTransitiveDigest": "ookO9nSEvbJqeBy/tdtGBn7TVQtv0GXBN6yGHHqsAC4=", "usagesDigest": "mXQ4Q6FohJ5fXR4FTAao+VwkF3Gfgb646BQodIC/hD8=", "recordedInputs": [ "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", @@ -430,7 +430,7 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%flutter_plugins_ext": { "general": { - "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", + "bzlTransitiveDigest": "ookO9nSEvbJqeBy/tdtGBn7TVQtv0GXBN6yGHHqsAC4=", "usagesDigest": "G8kgLfLv/g4JO70/Fz65CA3gz9V9VxPrddSvVKtpw54=", "recordedInputs": [ "REPO_MAPPING:,flutter_maven rules_jvm_external++maven+flutter_maven", @@ -471,7 +471,7 @@ }, "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_embedding": { "general": { - "bzlTransitiveDigest": "3eqHOPboGukDJg90t58rNh9rmebP4fSAVaS1+Xd+5d4=", + "bzlTransitiveDigest": "1rUycB7XyOhDdVVL+3WGQn0chQV5pDYB+oR++p5zzKA=", "usagesDigest": "VDubd1CC/3EsTr4xz8tz3uu65g6E5r8qQxmJ5I7keKE=", "recordedInputs": [ "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", @@ -532,7 +532,7 @@ }, "@@sameri11_rules_flutter_android+//flutter/private:target_toolchain_repo.bzl%flutter_android_toolchains": { "general": { - "bzlTransitiveDigest": "0D9Q8Q4AQrFirqM6wLGtrv3QlasjcQ2c2i2OxhVYFfI=", + "bzlTransitiveDigest": "qLNqwJd+HIxwhKWP/Y5+OzPbh0rMlz/HFaIdkadHxuM=", "usagesDigest": "2WhXsIhHLINViYFxmWfqIy1rlYxnWFK11LB9vSRc2M0=", "recordedInputs": [ "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", @@ -570,6 +570,11 @@ "armeabi-v7a": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a//:aot_toolchain'" }, "aot_toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:aot_toolchain_type'", + "cpu_constraints": { + "arm64-v8a": "'@@platforms//cpu:arm64'", + "x86_64": "'@@platforms//cpu:x86_64'", + "armeabi-v7a": "'@@platforms//cpu:armv7'" + }, "toolchain_targets": { "arm64-v8a": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_arm64_v8a'", "x86_64": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_x86_64'", @@ -583,7 +588,7 @@ }, "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { "general": { - "bzlTransitiveDigest": "Et4GM1IW3LCV7ah8t1qEeCgh9cOh1ek26YeJOVdYI+0=", + "bzlTransitiveDigest": "GRk2y7lduN/jEGLmTrEJhuPbFjOzVyU48T30FcvhBYU=", "usagesDigest": "mhGaagnJ1rxsIhLvM71ruzvKd+zmw0qH6YhJRGCfdjM=", "recordedInputs": [], "generatedRepoSpecs": { diff --git a/examples/local_plugin/packages/host_app/BUILD.bazel b/examples/local_plugin/packages/host_app/BUILD.bazel index 8e728d55..a488e0b9 100644 --- a/examples/local_plugin/packages/host_app/BUILD.bazel +++ b/examples/local_plugin/packages/host_app/BUILD.bazel @@ -1,9 +1,9 @@ -load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_core") -load("@rules_flutter_core//flutter:defs.bzl", "flutter_test_suite") +load("@rules_flutter_android//flutter:defs.bzl", "FLUTTER_ANDROID") +load("@rules_flutter_core//flutter:defs.bzl", "flutter_core", "flutter_test_suite") package(default_visibility = ["//visibility:public"]) -flutter_android_core( +flutter_core( name = "app", assets = [], dart_plugin_registrant = "lib/dart_plugin_registrant.dart", @@ -12,6 +12,7 @@ flutter_android_core( # pubspec.lock carries no hash for it and its content is otherwise an # undeclared input. path_deps = ["//packages/greeter:srcs"], + platforms = [FLUTTER_ANDROID], pub = "@pub", ) diff --git a/examples/no_plugins/BUILD.bazel b/examples/no_plugins/BUILD.bazel index 1052ff4b..c4da17d1 100644 --- a/examples/no_plugins/BUILD.bazel +++ b/examples/no_plugins/BUILD.bazel @@ -1,16 +1,17 @@ # Dart targets for a plugin-free app. -load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_core") -load("@rules_flutter_core//flutter:defs.bzl", "flutter_integration_test", "flutter_test_suite") +load("@rules_flutter_android//flutter:defs.bzl", "FLUTTER_ANDROID") +load("@rules_flutter_core//flutter:defs.bzl", "flutter_core", "flutter_integration_test", "flutter_test_suite") package(default_visibility = ["//visibility:public"]) -flutter_android_core( +flutter_core( name = "core", srcs = glob(["lib/**/*.dart"]), # flutter create declares no assets. assets = [], native_build_env = "@rules_flutter_android//flutter:native_build_env", + platforms = [FLUTTER_ANDROID], pub = "@pub", ) @@ -18,36 +19,42 @@ flutter_integration_test( name = "integration_pass", src = "//integration_test:integration_pass_test.dart", app = ":core", + platforms = [FLUTTER_ANDROID], ) flutter_integration_test( name = "integration_fail", src = "//integration_test:integration_fail_test.dart", app = ":core", + platforms = [FLUTTER_ANDROID], ) flutter_integration_test( name = "integration_lifecycle", src = "//integration_test:integration_lifecycle_test.dart", app = ":core", + platforms = [FLUTTER_ANDROID], ) flutter_integration_test( name = "integration_timeout", src = "//integration_test:integration_timeout_test.dart", app = ":core", + platforms = [FLUTTER_ANDROID], ) flutter_integration_test( name = "integration_crash", src = "//integration_test:integration_crash_test.dart", app = ":core", + platforms = [FLUTTER_ANDROID], ) flutter_integration_test( name = "integration_deadline", src = "//integration_test:integration_deadline_test.dart", app = ":core", + platforms = [FLUTTER_ANDROID], ) # The host test reads core_test_package, declared by flutter_core for the diff --git a/examples/no_plugins/MODULE.bazel.lock b/examples/no_plugins/MODULE.bazel.lock index c97362ec..494be70d 100644 --- a/examples/no_plugins/MODULE.bazel.lock +++ b/examples/no_plugins/MODULE.bazel.lock @@ -409,7 +409,7 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%android_ndk": { "general": { - "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", + "bzlTransitiveDigest": "ookO9nSEvbJqeBy/tdtGBn7TVQtv0GXBN6yGHHqsAC4=", "usagesDigest": "gihfLsx8YMM+v3GPdKHQB+XDY0mBmcT3vsEGUwsp5YE=", "recordedInputs": [ "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", @@ -430,7 +430,7 @@ }, "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_embedding": { "general": { - "bzlTransitiveDigest": "3eqHOPboGukDJg90t58rNh9rmebP4fSAVaS1+Xd+5d4=", + "bzlTransitiveDigest": "1rUycB7XyOhDdVVL+3WGQn0chQV5pDYB+oR++p5zzKA=", "usagesDigest": "VDubd1CC/3EsTr4xz8tz3uu65g6E5r8qQxmJ5I7keKE=", "recordedInputs": [ "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", @@ -491,7 +491,7 @@ }, "@@sameri11_rules_flutter_android+//flutter/private:target_toolchain_repo.bzl%flutter_android_toolchains": { "general": { - "bzlTransitiveDigest": "0D9Q8Q4AQrFirqM6wLGtrv3QlasjcQ2c2i2OxhVYFfI=", + "bzlTransitiveDigest": "qLNqwJd+HIxwhKWP/Y5+OzPbh0rMlz/HFaIdkadHxuM=", "usagesDigest": "2WhXsIhHLINViYFxmWfqIy1rlYxnWFK11LB9vSRc2M0=", "recordedInputs": [ "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", @@ -529,6 +529,11 @@ "armeabi-v7a": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a//:aot_toolchain'" }, "aot_toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:aot_toolchain_type'", + "cpu_constraints": { + "arm64-v8a": "'@@platforms//cpu:arm64'", + "x86_64": "'@@platforms//cpu:x86_64'", + "armeabi-v7a": "'@@platforms//cpu:armv7'" + }, "toolchain_targets": { "arm64-v8a": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_arm64_v8a'", "x86_64": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_x86_64'", @@ -542,7 +547,7 @@ }, "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { "general": { - "bzlTransitiveDigest": "Et4GM1IW3LCV7ah8t1qEeCgh9cOh1ek26YeJOVdYI+0=", + "bzlTransitiveDigest": "GRk2y7lduN/jEGLmTrEJhuPbFjOzVyU48T30FcvhBYU=", "usagesDigest": "qgOPHTIJehHE7C5edcqknU9pqU6zUuSV2IGyzjt0tf8=", "recordedInputs": [], "generatedRepoSpecs": { diff --git a/examples/no_plugins/integration_test/BUILD.bazel b/examples/no_plugins/integration_test/BUILD.bazel index f11beaa0..1198d891 100644 --- a/examples/no_plugins/integration_test/BUILD.bazel +++ b/examples/no_plugins/integration_test/BUILD.bazel @@ -1,3 +1,4 @@ +load("@rules_flutter_android//flutter:defs.bzl", "FLUTTER_ANDROID") load("@rules_flutter_core//flutter:composition.bzl", "flutter_debug_core") package(default_visibility = ["//visibility:public"]) @@ -19,4 +20,5 @@ flutter_debug_core( app = "//:core", dart_defines = {"DEBUG_CORE_MARK": "prebuilt-value"}, entrypoint = "debug_core_test.dart", + platforms = [FLUTTER_ANDROID], ) diff --git a/examples/pub_plugins/BUILD.bazel b/examples/pub_plugins/BUILD.bazel index cad015e2..9994e71d 100644 --- a/examples/pub_plugins/BUILD.bazel +++ b/examples/pub_plugins/BUILD.bazel @@ -1,17 +1,18 @@ # Dart targets for the flat pub-plugin example. -load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_core", "flutter_plugin_deps_guards") -load("@rules_flutter_core//flutter:defs.bzl", "flutter_integration_test", "flutter_test_suite") +load("@rules_flutter_android//flutter:defs.bzl", "FLUTTER_ANDROID", "flutter_plugin_deps_guards") +load("@rules_flutter_core//flutter:defs.bzl", "flutter_core", "flutter_integration_test", "flutter_test_suite") package(default_visibility = ["//visibility:public"]) flutter_plugin_deps_guards(plugins = "@flutter_plugins") -flutter_android_core( +flutter_core( name = "app", assets = [], dart_plugin_registrant = "lib/dart_plugin_registrant.dart", native_build_env = "@rules_flutter_android//flutter:native_build_env", + platforms = [FLUTTER_ANDROID], pub = "@pub", ) @@ -19,6 +20,7 @@ flutter_integration_test( name = "smoke_integration", src = "//integration_test:smoke_test.dart", app = ":app", + platforms = [FLUTTER_ANDROID], ) flutter_test_suite( diff --git a/examples/pub_plugins/MODULE.bazel.lock b/examples/pub_plugins/MODULE.bazel.lock index a60cd2aa..97451d07 100644 --- a/examples/pub_plugins/MODULE.bazel.lock +++ b/examples/pub_plugins/MODULE.bazel.lock @@ -409,7 +409,7 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%android_ndk": { "general": { - "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", + "bzlTransitiveDigest": "ookO9nSEvbJqeBy/tdtGBn7TVQtv0GXBN6yGHHqsAC4=", "usagesDigest": "CWMaitCzriuNa8mQm9VlXo9SoC7z68RHk02zkINH09A=", "recordedInputs": [ "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", @@ -430,7 +430,7 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%flutter_plugins_ext": { "general": { - "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", + "bzlTransitiveDigest": "ookO9nSEvbJqeBy/tdtGBn7TVQtv0GXBN6yGHHqsAC4=", "usagesDigest": "mW8ky7T5PDBsdwo6w2Q5gOopJ+4T57XXK8sGug9bSvc=", "recordedInputs": [ "REPO_MAPPING:,flutter_maven rules_jvm_external++maven+flutter_maven", @@ -471,7 +471,7 @@ }, "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_embedding": { "general": { - "bzlTransitiveDigest": "3eqHOPboGukDJg90t58rNh9rmebP4fSAVaS1+Xd+5d4=", + "bzlTransitiveDigest": "1rUycB7XyOhDdVVL+3WGQn0chQV5pDYB+oR++p5zzKA=", "usagesDigest": "VDubd1CC/3EsTr4xz8tz3uu65g6E5r8qQxmJ5I7keKE=", "recordedInputs": [ "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", @@ -532,7 +532,7 @@ }, "@@sameri11_rules_flutter_android+//flutter/private:target_toolchain_repo.bzl%flutter_android_toolchains": { "general": { - "bzlTransitiveDigest": "0D9Q8Q4AQrFirqM6wLGtrv3QlasjcQ2c2i2OxhVYFfI=", + "bzlTransitiveDigest": "qLNqwJd+HIxwhKWP/Y5+OzPbh0rMlz/HFaIdkadHxuM=", "usagesDigest": "2WhXsIhHLINViYFxmWfqIy1rlYxnWFK11LB9vSRc2M0=", "recordedInputs": [ "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", @@ -570,6 +570,11 @@ "armeabi-v7a": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a//:aot_toolchain'" }, "aot_toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:aot_toolchain_type'", + "cpu_constraints": { + "arm64-v8a": "'@@platforms//cpu:arm64'", + "x86_64": "'@@platforms//cpu:x86_64'", + "armeabi-v7a": "'@@platforms//cpu:armv7'" + }, "toolchain_targets": { "arm64-v8a": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_arm64_v8a'", "x86_64": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_x86_64'", @@ -583,7 +588,7 @@ }, "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { "general": { - "bzlTransitiveDigest": "Et4GM1IW3LCV7ah8t1qEeCgh9cOh1ek26YeJOVdYI+0=", + "bzlTransitiveDigest": "GRk2y7lduN/jEGLmTrEJhuPbFjOzVyU48T30FcvhBYU=", "usagesDigest": "7g5j5szAjo79jAswLX0VYMWhMYvsF2r2WEYs8ccrN/k=", "recordedInputs": [], "generatedRepoSpecs": { diff --git a/rules_flutter_android/MODULE.bazel.lock b/rules_flutter_android/MODULE.bazel.lock index 18380dea..6ceed459 100644 --- a/rules_flutter_android/MODULE.bazel.lock +++ b/rules_flutter_android/MODULE.bazel.lock @@ -300,7 +300,7 @@ "moduleExtensions": { "//flutter:extensions.bzl%android_ndk": { "general": { - "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", + "bzlTransitiveDigest": "ookO9nSEvbJqeBy/tdtGBn7TVQtv0GXBN6yGHHqsAC4=", "usagesDigest": "BSiKVNTHy3EXCSxrVE2lcJp20b5h/Z0f4xjsXF45WZ0=", "recordedInputs": [ "REPO_MAPPING:,platforms platforms", @@ -321,7 +321,7 @@ }, "//flutter:extensions.bzl%flutter_plugins_ext": { "general": { - "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", + "bzlTransitiveDigest": "ookO9nSEvbJqeBy/tdtGBn7TVQtv0GXBN6yGHHqsAC4=", "usagesDigest": "9owEpU8N8w2F4asd++mkiTNLiSzbTw8JI0PGfMv4MyM=", "recordedInputs": [ "REPO_MAPPING:,parity_maven rules_jvm_external++maven+parity_maven", @@ -462,7 +462,7 @@ }, "//flutter/private:embedding_repo.bzl%flutter_android_embedding": { "general": { - "bzlTransitiveDigest": "3eqHOPboGukDJg90t58rNh9rmebP4fSAVaS1+Xd+5d4=", + "bzlTransitiveDigest": "1rUycB7XyOhDdVVL+3WGQn0chQV5pDYB+oR++p5zzKA=", "usagesDigest": "JpFePUNenAgojvGWTEykSWOR1tW+yKsbSwtNkwh8Zis=", "recordedInputs": [ "REPO_MAPPING:,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", @@ -523,7 +523,7 @@ }, "//flutter/private:target_toolchain_repo.bzl%flutter_android_toolchains": { "general": { - "bzlTransitiveDigest": "0D9Q8Q4AQrFirqM6wLGtrv3QlasjcQ2c2i2OxhVYFfI=", + "bzlTransitiveDigest": "qLNqwJd+HIxwhKWP/Y5+OzPbh0rMlz/HFaIdkadHxuM=", "usagesDigest": "X0W+UJfnyCDYkAN+wssgobE3kRonFYUkhzx27KR7fCI=", "recordedInputs": [ "REPO_MAPPING:,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", @@ -561,6 +561,11 @@ "armeabi-v7a": "'@@+flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a//:aot_toolchain'" }, "aot_toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:aot_toolchain_type'", + "cpu_constraints": { + "arm64-v8a": "'@@platforms//cpu:arm64'", + "x86_64": "'@@platforms//cpu:x86_64'", + "armeabi-v7a": "'@@platforms//cpu:armv7'" + }, "toolchain_targets": { "arm64-v8a": "'@@//flutter:target_toolchain_arm64_v8a'", "x86_64": "'@@//flutter:target_toolchain_x86_64'", @@ -681,7 +686,7 @@ }, "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { "general": { - "bzlTransitiveDigest": "Et4GM1IW3LCV7ah8t1qEeCgh9cOh1ek26YeJOVdYI+0=", + "bzlTransitiveDigest": "GRk2y7lduN/jEGLmTrEJhuPbFjOzVyU48T30FcvhBYU=", "usagesDigest": "yrcrs3q9AyVzH00WG98mD1OpGXB4f6VMs6fbGNdHd28=", "recordedInputs": [], "generatedRepoSpecs": { diff --git a/rules_flutter_android/flutter/BUILD.bazel b/rules_flutter_android/flutter/BUILD.bazel index 073f6829..a61483d6 100644 --- a/rules_flutter_android/flutter/BUILD.bazel +++ b/rules_flutter_android/flutter/BUILD.bazel @@ -6,7 +6,6 @@ package(default_visibility = ["//visibility:public"]) exports_files([ "native_build_env.bzl", "composition.bzl", - "android_core.bzl", ]) filegroup( @@ -41,6 +40,7 @@ config_setting( "@platforms//cpu:arm64", ], flag_values = {"@rules_flutter_core//flutter:mode": "release"}, + visibility = ["//visibility:private"], ) config_setting( @@ -50,6 +50,7 @@ config_setting( "@platforms//cpu:arm64", ], flag_values = {"@rules_flutter_core//flutter:mode": "debug"}, + visibility = ["//visibility:private"], ) config_setting( @@ -59,6 +60,7 @@ config_setting( "@platforms//cpu:x86_64", ], flag_values = {"@rules_flutter_core//flutter:mode": "release"}, + visibility = ["//visibility:private"], ) config_setting( @@ -68,6 +70,7 @@ config_setting( "@platforms//cpu:x86_64", ], flag_values = {"@rules_flutter_core//flutter:mode": "debug"}, + visibility = ["//visibility:private"], ) config_setting( @@ -77,6 +80,7 @@ config_setting( "@platforms//cpu:armv7", ], flag_values = {"@rules_flutter_core//flutter:mode": "release"}, + visibility = ["//visibility:private"], ) config_setting( @@ -86,6 +90,7 @@ config_setting( "@platforms//cpu:armv7", ], flag_values = {"@rules_flutter_core//flutter:mode": "debug"}, + visibility = ["//visibility:private"], ) filegroup( diff --git a/rules_flutter_android/flutter/abis.bzl b/rules_flutter_android/flutter/abis.bzl index 464b7e62..5268c447 100644 --- a/rules_flutter_android/flutter/abis.bzl +++ b/rules_flutter_android/flutter/abis.bzl @@ -11,12 +11,11 @@ _UNSUPPORTED_ANDROID_CPU_CONSTRAINTS = { Label("@platforms//cpu:riscv64"): "riscv64", } -def _abi(target_platform, engine_dir, maven_artifact, manifest_key, cpu_constraint, snapshot_flags = []): +def _abi(target_platform, engine_dir, maven_artifact, cpu_constraint, snapshot_flags = []): return struct( target_platform = target_platform, engine_dir = engine_dir, maven_artifact = maven_artifact, - manifest_key = manifest_key, cpu_constraint = cpu_constraint, snapshot_flags = snapshot_flags, ) @@ -26,22 +25,20 @@ ABIS = { target_platform = "android-arm64", engine_dir = {"release": "android-arm64-release"}, maven_artifact = {"release": "arm64_v8a_release", "debug": "arm64_v8a_debug"}, - manifest_key = "android_arm64", cpu_constraint = Label("@platforms//cpu:arm64"), ), "x86_64": _abi( target_platform = "android-x64", engine_dir = {"release": "android-x64-release"}, maven_artifact = {"release": "x86_64_release", "debug": "x86_64_debug"}, - manifest_key = "android_x64", cpu_constraint = Label("@platforms//cpu:x86_64"), ), "armeabi-v7a": _abi( target_platform = "android-arm", engine_dir = {"release": "android-arm-release"}, maven_artifact = {"release": "armeabi_v7a_release", "debug": "armeabi_v7a_debug"}, - manifest_key = "android_arm", cpu_constraint = Label("@platforms//cpu:armv7"), + # Keep gen_snapshot output compatible with Flutter's armv7 engine ABI. snapshot_flags = ["--no-sim-use-hardfp", "--no-use-integer-division"], ), } diff --git a/rules_flutter_android/flutter/android_core.bzl b/rules_flutter_android/flutter/android_core.bzl deleted file mode 100644 index c48d224f..00000000 --- a/rules_flutter_android/flutter/android_core.bzl +++ /dev/null @@ -1,12 +0,0 @@ -"""Android facade that supplies ABI data to the platform-neutral core macro.""" - -load("@rules_flutter_core//flutter:defs.bzl", _flutter_core = "flutter_core") -load("//flutter:abis.bzl", "ABIS") -load("//flutter/private:abis.bzl", "bazel_platform") - -def flutter_android_core(**kwargs): - _flutter_core( - android_target_platforms = [bazel_platform(abi) for abi in ABIS], - aot_compatible_with = [Label("@platforms//os:android")], - **kwargs - ) diff --git a/rules_flutter_android/flutter/defs.bzl b/rules_flutter_android/flutter/defs.bzl index 3f8c7cd5..101f901f 100644 --- a/rules_flutter_android/flutter/defs.bzl +++ b/rules_flutter_android/flutter/defs.bzl @@ -1,20 +1,97 @@ """Supported convenience BUILD API for Flutter Android consumer modules.""" load("@rules_flutter_core//flutter:composition.bzl", _core_flutter_assets = "flutter_assets") -load("//flutter:android_core.bzl", _flutter_android_core = "flutter_android_core") +load("@rules_flutter_core//flutter:defs.bzl", _flutter_platform = "flutter_platform", _flutter_target_slices_rule = "flutter_target_slices_rule", _platform_neutral_files_rule = "platform_neutral_files_rule") +load("//flutter:abis.bzl", "ABIS") +load("//flutter/private:abis.bzl", "bazel_platform") load("//flutter/private:android_app.bzl", _FLUTTER_DEBUG_UNCOMPRESSED_ASSETS = "FLUTTER_DEBUG_UNCOMPRESSED_ASSETS", _flutter_android_app = "flutter_android_app") load("//flutter/private:plugin_deps_guards.bzl", _flutter_plugin_deps_guards = "flutter_plugin_deps_guards") load("//flutter/private:recipe.bzl", _flutter_native_contribution = "flutter_native_contribution") +# Clear outer split settings so each descriptor applies exactly its own target slice. +# rules_android's bytecode_transformer option is not an output: a consumer that +# sets it gets a second kernel and bundle configuration instead of sharing the +# split's host-neutral configuration. +_ANDROID_SLICE_OUTPUTS = [ + "//command_line_option:Android configuration distinguisher", + "//command_line_option:android_compiler", + "//command_line_option:android_platforms", + "//command_line_option:compiler", + "//command_line_option:dynamic_mode", + "//command_line_option:platforms", +] + +def _android_slice_transition_impl(_settings, attr): + return { + str(platform): { + "//command_line_option:Android configuration distinguisher": "main", + "//command_line_option:android_compiler": None, + "//command_line_option:android_platforms": [], + "//command_line_option:compiler": None, + "//command_line_option:dynamic_mode": "default", + "//command_line_option:platforms": [str(platform)], + } + for platform in attr.target_platforms + } + +_android_slice_transition = transition( + implementation = _android_slice_transition_impl, + inputs = [], + outputs = _ANDROID_SLICE_OUTPUTS, +) + +# One configuration keeps shared files from being rebuilt for each outer Android split. +def _android_neutral_transition_impl(_settings, _attr): + return { + "//command_line_option:Android configuration distinguisher": "main", + "//command_line_option:android_compiler": None, + "//command_line_option:android_platforms": [], + "//command_line_option:compiler": None, + "//command_line_option:dynamic_mode": "default", + "//command_line_option:platforms": ["@platforms//host:host"], + } + +_android_neutral_transition = transition( + implementation = _android_neutral_transition_impl, + inputs = [], + outputs = _ANDROID_SLICE_OUTPUTS, +) +_android_neutral_files = _platform_neutral_files_rule(_android_neutral_transition) + +_android_target_slices = _flutter_target_slices_rule(_android_slice_transition) + _ANDROID_NATIVE_BUILD_ENV = Label("//flutter:native_build_env") -def flutter_assets(native_build_env = _ANDROID_NATIVE_BUILD_ENV, **kwargs): - """Declares flutter_assets with the Android SDK native build environment.""" - _core_flutter_assets(native_build_env = native_build_env, **kwargs) +FLUTTER_ANDROID = _flutter_platform( + name = "android", + target_platforms = [bazel_platform(abi) for abi in ABIS], + constraint = Label("@platforms//os:android"), + slices_rule = _android_target_slices, + neutral_files_rule = _android_neutral_files, +) + +def flutter_assets(name, target_platforms, native_build_env = _ANDROID_NATIVE_BUILD_ENV, **kwargs): + """Declares a public FlutterAssets action with Android target slices. + + Args: + name: Name of the generated target. + target_platforms: Android target platforms whose slices are bundled. + native_build_env: Native build environment label. + **kwargs: Arguments forwarded to the core flutter_assets rule. + """ + if "target_slices" not in kwargs: + slices_name = name + "_target_slices" + _android_target_slices( + name = slices_name, + target_platforms = target_platforms, + ) + kwargs["target_slices"] = [":" + slices_name] + _core_flutter_assets(name = name, native_build_env = native_build_env, **kwargs) -flutter_android_app = _flutter_android_app +def flutter_android_app(name, **kwargs): + """Declares an Android app, wiring its debug artifacts to FLUTTER_ANDROID.""" + _flutter_android_app(name = name, debug_platforms = [FLUTTER_ANDROID], **kwargs) -flutter_android_core = _flutter_android_core flutter_plugin_deps_guards = _flutter_plugin_deps_guards flutter_native_contribution = _flutter_native_contribution FLUTTER_DEBUG_UNCOMPRESSED_ASSETS = _FLUTTER_DEBUG_UNCOMPRESSED_ASSETS diff --git a/rules_flutter_android/flutter/private/android.bzl b/rules_flutter_android/flutter/private/android.bzl index 8b1bdc0b..6a2f4d58 100644 --- a/rules_flutter_android/flutter/private/android.bzl +++ b/rules_flutter_android/flutter/private/android.bzl @@ -1,11 +1,26 @@ """Android manifest and asset-path helpers used by composed app targets.""" load("@rules_flutter_core//flutter:composition.bzl", "FlutterArtifactInfo", "FlutterPubspecInfo") +load("//flutter:abis.bzl", "ABIS") +load("//flutter/private:abis.bzl", "bazel_platform") visibility(["//flutter"]) +_ANDROID_DEFAULT_PLATFORM = bazel_platform(ABIS.keys()[0]) + +def _android_assets_platform_transition_impl(settings, _attr): + android_platforms = settings["//command_line_option:android_platforms"] + platforms = android_platforms[:1] if android_platforms else [_ANDROID_DEFAULT_PLATFORM] + return {"//command_line_option:platforms": platforms} + +_android_assets_platform_transition = transition( + implementation = _android_assets_platform_transition_impl, + inputs = ["//command_line_option:android_platforms"], + outputs = ["//command_line_option:platforms"], +) def _inputs_bundle_impl(ctx): - src = ctx.attr.core[FlutterArtifactInfo].assets + (core,) = ctx.attr.core + src = core[FlutterArtifactInfo].assets out = ctx.actions.declare_directory(ctx.label.name + "/flutter_assets") ctx.actions.run_shell( outputs = [out], @@ -20,7 +35,7 @@ _inputs_bundle = rule( implementation = _inputs_bundle_impl, doc = "The core's flutter_assets tree, copied under this target's name.", attrs = { - "core": attr.label(mandatory = True, providers = [FlutterArtifactInfo]), + "core": attr.label(mandatory = True, providers = [FlutterArtifactInfo], cfg = _android_assets_platform_transition), }, ) diff --git a/rules_flutter_android/flutter/private/android_app.bzl b/rules_flutter_android/flutter/private/android_app.bzl index 97ec288e..7a6020b7 100644 --- a/rules_flutter_android/flutter/private/android_app.bzl +++ b/rules_flutter_android/flutter/private/android_app.bzl @@ -60,6 +60,7 @@ def flutter_android_app( test_apps = {}, integration_tests = [], integration_test_defaults = {}, + debug_platforms = None, **kwargs): """A stock android_binary `name` with Flutter's defaults for a standard layout. @@ -106,6 +107,7 @@ def flutter_android_app( as its adb runner. `shard_count` is refused. integration_test_defaults: loading-time attributes shared by the generated integration runner tests, overridden by each entry's attributes. + debug_platforms: descriptors used by generated debug cores. **kwargs: forwarded to android_binary, and tags and visibility also to `_check_test`. `assets` and `assets_dir` are set by the macro and refused here. @@ -235,6 +237,7 @@ def flutter_android_app( app_kwargs = kwargs, tags = kwargs.get("tags", []), native_label = native_label, + debug_platforms = debug_platforms, ) _declare_integration_tests( @@ -255,15 +258,25 @@ def flutter_android_app( debug_manifest_deps = debug_manifest_deps, tags = kwargs.get("tags", []), native_label = native_label, + debug_platforms = debug_platforms, ) -def _declare_test_apps(name, test_apps, app_core, inputs, inputs_label, app_manifest, pubspec, manifest_values, resource_files, app_deps, app_extra_deps, debug_manifest_deps, app_kwargs, tags, native_label): +def _declare_test_apps(name, test_apps, app_core, inputs, inputs_label, app_manifest, pubspec, manifest_values, resource_files, app_deps, app_extra_deps, debug_manifest_deps, app_kwargs, tags, native_label, debug_platforms): reserved = ["debug", "inputs", "manifest", "debug_manifest", "maven_repo_check", "check_test"] keys = sorted(test_apps.keys()) + generated_suffixes = ["_core", "_manifest", "_check_test", "_assets", "_core_kernel", "_core_bundle"] + for platform in debug_platforms or []: + generated_suffixes.append("_core_kernel_" + platform.name) + for platform in debug_platforms or []: + generated_suffixes.extend([ + "_core_kernel_neutral_" + platform.name, + "_core_bundle_action_" + platform.name, + "_core_bundle_" + platform.name, + ]) for key in keys: - if not key or key in reserved or key.startswith("inputs_") or key.endswith(("_core", "_manifest", "_check_test", "_assets", "_core_kernel", "_core_kernel_neutral", "_core_bundle", "_core_bundle_action")): + if not key or key in reserved or key.startswith("inputs_") or any([key.endswith(suffix) for suffix in generated_suffixes]): fail("flutter_android_app {}: test_apps key {} is reserved or collides with a generated target suffix.".format(name, key)) - for suffix in ["_core", "_manifest", "_check_test", "_assets", "_core_kernel", "_core_kernel_neutral", "_core_bundle", "_core_bundle_action"]: + for suffix in generated_suffixes: if key + suffix in test_apps: fail("flutter_android_app {}: test_apps keys {} and {} generate colliding targets.".format(name, key, key + suffix)) @@ -287,6 +300,7 @@ def _declare_test_apps(name, test_apps, app_core, inputs, inputs_label, app_mani name = test_core, app = entry.pop("app", app_core), entrypoint = entry.pop("entrypoint"), + platforms = debug_platforms, srcs = entry.pop("srcs", []), dart_defines = entry.pop("dart_defines", {}), tags = _test_tags(entry.get("tags", tags)), @@ -338,7 +352,7 @@ def _declare_test_apps(name, test_apps, app_core, inputs, inputs_label, app_mani ) flutter_apk_check(name = target + "_check_test", binary = ":" + target, native = [native_label], tags = entry_tags, visibility = visibility, testonly = testonly) -def _declare_integration_tests(name, integration_tests, defaults, app_kwargs, manifest_values, test_apps, app_core, inputs, inputs_label, app_manifest, pubspec, resource_files, app_deps, app_extra_deps, debug_manifest_deps, tags, native_label): +def _declare_integration_tests(name, integration_tests, defaults, app_kwargs, manifest_values, test_apps, app_core, inputs, inputs_label, app_manifest, pubspec, resource_files, app_deps, app_extra_deps, debug_manifest_deps, tags, native_label, debug_platforms): if "shard_count" in defaults: fail("flutter_android_app {}: shard_count is not supported for integration_tests.".format(name)) if type(integration_tests) == "list": @@ -380,6 +394,7 @@ def _declare_integration_tests(name, integration_tests, defaults, app_kwargs, ma app_kwargs = app_kwargs, tags = tags, native_label = native_label, + debug_platforms = debug_platforms, ) for label_text, overrides in entries.items(): diff --git a/rules_flutter_android/flutter/private/android_inputs_rules.bzl b/rules_flutter_android/flutter/private/android_inputs_rules.bzl index 8d4aabad..fdd3f9bf 100644 --- a/rules_flutter_android/flutter/private/android_inputs_rules.bzl +++ b/rules_flutter_android/flutter/private/android_inputs_rules.bzl @@ -5,6 +5,9 @@ android_binary deps. Each configured split reads its ABI from the Bazel platform takes the AOT library from core, the engine libraries from this ruleset's Android-owned selection, and the plugin repository's libraries for that ABI. The libraries reach the APK through CcInfo, not through a jar the app wraps. +If a split is analyzed without an Android platform, it yields a placeholder so +graph queries and `--nobuild` analysis can proceed; building the placeholder +fails with a clear diagnostic. """ load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") @@ -104,8 +107,8 @@ def _split_native_impl(ctx): info = ctx.attr.core[FlutterArtifactInfo] libraries = [] - if info.libapp != None: - libraries.append(info.libapp) + if info.aot != None: + libraries.append(info.aot) # The engine is stripped here, as the engine jar was, so the APK carries # the same bytes the jar carried. diff --git a/rules_flutter_android/flutter/private/embedding_repo.bzl b/rules_flutter_android/flutter/private/embedding_repo.bzl index 3a293cb3..95c3c85a 100644 --- a/rules_flutter_android/flutter/private/embedding_repo.bzl +++ b/rules_flutter_android/flutter/private/embedding_repo.bzl @@ -1,6 +1,6 @@ """Repositories for the Java embedding and Android engine artifacts.""" -load("@rules_flutter_core//flutter:abis.bzl", "MODES") +load("@rules_flutter_core//flutter:modes.bzl", "MODES") load("//flutter:abis.bzl", "ABIS") visibility(["//:__pkg__"]) diff --git a/rules_flutter_android/flutter/private/gen_snapshot_repo.bzl b/rules_flutter_android/flutter/private/gen_snapshot_repo.bzl index 064bec06..ca39947d 100644 --- a/rules_flutter_android/flutter/private/gen_snapshot_repo.bzl +++ b/rules_flutter_android/flutter/private/gen_snapshot_repo.bzl @@ -40,6 +40,11 @@ exports_files(["gen_snapshot"]) flutter_aot_toolchain( name = "aot_toolchain", gen_snapshot_release = ":gen_snapshot", + output_kind = "elf", + output_name = "libapp.so", + snapshot_kind = "app-aot-elf", + output_flag = "--elf=", + strip_flag = "--strip", ) """) else: diff --git a/rules_flutter_android/flutter/private/target_toolchain_repo.bzl b/rules_flutter_android/flutter/private/target_toolchain_repo.bzl index bbaaf0a6..6121dd04 100644 --- a/rules_flutter_android/flutter/private/target_toolchain_repo.bzl +++ b/rules_flutter_android/flutter/private/target_toolchain_repo.bzl @@ -34,11 +34,7 @@ def _android_toolchains_impl(ctx): return lines = ['package(default_visibility = ["//visibility:public"])', ""] - cpu_constraints = { - "arm64-v8a": "arm64", - "armeabi-v7a": "armv7", - "x86_64": "x86_64", - } + cpu_constraints = ctx.attr.cpu_constraints for abi in sorted(ctx.attr.toolchain_targets.keys()): lines.extend([ @@ -47,7 +43,7 @@ def _android_toolchains_impl(ctx): ' toolchain = "' + ctx.attr.toolchain_targets[abi] + '",', ' toolchain_type = "' + ctx.attr.toolchain_type + '",', " exec_compatible_with = " + repr(host) + ",", - ' target_compatible_with = ["@platforms//os:android", "@platforms//cpu:' + cpu_constraints[abi] + '"],', + ' target_compatible_with = ["@platforms//os:android", "' + cpu_constraints[abi] + '"],', ")", "", ]) @@ -59,7 +55,7 @@ def _android_toolchains_impl(ctx): ' toolchain = "' + ctx.attr.aot_toolchain_targets[abi] + '",', ' toolchain_type = "' + ctx.attr.aot_toolchain_type + '",', " exec_compatible_with = " + repr(host) + ",", - ' target_compatible_with = ["@platforms//os:android", "@platforms//cpu:' + cpu_constraints[abi] + '"],', + ' target_compatible_with = ["@platforms//os:android", "' + cpu_constraints[abi] + '"],', ")", "", ]) @@ -70,6 +66,7 @@ _android_toolchains = repository_rule( attrs = { "aot_toolchain_targets": attr.string_dict(mandatory = True), "aot_toolchain_type": attr.string(mandatory = True), + "cpu_constraints": attr.string_dict(mandatory = True), "toolchain_targets": attr.string_dict(mandatory = True), "toolchain_type": attr.string(mandatory = True), }, @@ -78,6 +75,7 @@ _android_toolchains = repository_rule( def _android_toolchains_extension_impl(_ctx): toolchain_targets = {} aot_toolchain_targets = {} + cpu_constraints = {} for abi, info in ABIS.items(): suffix = abi.replace("-", "_") flutter_gen_snapshot( @@ -87,10 +85,12 @@ def _android_toolchains_extension_impl(_ctx): toolchain_targets[abi] = str(Label("//flutter:target_toolchain_" + suffix)) aot_toolchain_targets[abi] = str(Label("@flutter_gen_snapshot_" + suffix + "//:aot_toolchain")) + cpu_constraints[abi] = str(info.cpu_constraint) _android_toolchains( name = "flutter_android_target_toolchains", aot_toolchain_targets = aot_toolchain_targets, aot_toolchain_type = str(Label("@rules_flutter_core//flutter:aot_toolchain_type")), + cpu_constraints = cpu_constraints, toolchain_targets = toolchain_targets, toolchain_type = str(Label("@rules_flutter_core//flutter:target_toolchain_type")), ) diff --git a/rules_flutter_android/flutter/private/target_toolchains.bzl b/rules_flutter_android/flutter/private/target_toolchains.bzl index c28a98e9..4f7f7b89 100644 --- a/rules_flutter_android/flutter/private/target_toolchains.bzl +++ b/rules_flutter_android/flutter/private/target_toolchains.bzl @@ -12,7 +12,6 @@ def flutter_android_target_toolchains(): name = "target_toolchain_" + suffix, target_os = "android", snapshot_flags = info.snapshot_flags, - native_assets_key = info.manifest_key, target_platform = info.target_platform, slice_id = abi, ) diff --git a/rules_flutter_core/MODULE.bazel b/rules_flutter_core/MODULE.bazel index 0d792364..60b8eec9 100644 --- a/rules_flutter_core/MODULE.bazel +++ b/rules_flutter_core/MODULE.bazel @@ -3,7 +3,7 @@ module( ) # The rules' own .bzl files name @flutter_sdk, so this module imports it. Which -# SDK version they hold is the root module's choice: the extension reads flutter.sdk only from the root module. +# SDK version is selected by the root module's flutter.sdk setting. flutter = use_extension("//flutter:extensions.bzl", "flutter") # Pin for developing this repository as the root module. A dev dependency, so @@ -53,8 +53,8 @@ bazel_dep(name = "bazel_skylib", version = "1.8.2") bazel_dep(name = "rules_shell", version = "0.6.1", dev_dependency = True) -# The ABI table and the split config_settings name @platforms constraints -# (os:android, cpu:*), so the platforms module is a core dependency. +# Platform descriptors use @platforms constraints, so the platforms module is +# a core dependency. bazel_dep(name = "platforms", version = "1.0.0") pub_dev = use_extension("//flutter:extensions.bzl", "pub", dev_dependency = True) diff --git a/rules_flutter_core/MODULE.bazel.lock b/rules_flutter_core/MODULE.bazel.lock index 393cf1a9..3f1609c1 100644 --- a/rules_flutter_core/MODULE.bazel.lock +++ b/rules_flutter_core/MODULE.bazel.lock @@ -193,7 +193,7 @@ "moduleExtensions": { "//flutter:extensions.bzl%flutter": { "general": { - "bzlTransitiveDigest": "Et4GM1IW3LCV7ah8t1qEeCgh9cOh1ek26YeJOVdYI+0=", + "bzlTransitiveDigest": "GRk2y7lduN/jEGLmTrEJhuPbFjOzVyU48T30FcvhBYU=", "usagesDigest": "3eQ7CSujwGo7HWistYKJNKsA65Je6fZw/a3upweGNLo=", "recordedInputs": [], "generatedRepoSpecs": { diff --git a/rules_flutter_core/flutter/BUILD.bazel b/rules_flutter_core/flutter/BUILD.bazel index 2cbf2d0a..6bdc5aa9 100644 --- a/rules_flutter_core/flutter/BUILD.bazel +++ b/rules_flutter_core/flutter/BUILD.bazel @@ -1,5 +1,5 @@ load("@bazel_skylib//rules:common_settings.bzl", "int_flag", "string_flag") -load(":abis.bzl", "MODES") +load(":modes.bzl", "MODES") package(default_visibility = ["//visibility:public"]) @@ -7,6 +7,7 @@ exports_files([ "defs.bzl", "composition.bzl", "extensions.bzl", + "target_toolchain.bzl", ]) # The Flutter SDK toolchain type, for rules outside this module. The type is @@ -17,6 +18,7 @@ alias( visibility = ["//visibility:public"], ) +# Platform rulesets register these public toolchain types by label. alias( name = "target_toolchain_type", actual = "//flutter/private:flutter_target_toolchain_type", @@ -29,8 +31,6 @@ alias( visibility = ["//visibility:public"], ) -exports_files(["target_toolchain.bzl"]) - # Flutter runtime mode, independent of --compilation_mode. string_flag( name = "mode", diff --git a/rules_flutter_core/flutter/composition.bzl b/rules_flutter_core/flutter/composition.bzl index 54d90e47..55dcca88 100644 --- a/rules_flutter_core/flutter/composition.bzl +++ b/rules_flutter_core/flutter/composition.bzl @@ -4,7 +4,7 @@ load("//flutter/private:core.bzl", _FlutterArtifactInfo = "FlutterArtifactInfo") load("//flutter/private:debug_core.bzl", _flutter_debug_core = "flutter_debug_core") load("//flutter/private:integration_test.bzl", _FlutterIntegrationTestInfo = "FlutterIntegrationTestInfo") load("//flutter/private:pubspec.bzl", _FlutterPubspecInfo = "FlutterPubspecInfo", _flutter_pubspec = "flutter_pubspec") -load("//flutter/private:rules.bzl", _FlutterNativeBuildEnvInfo = "FlutterNativeBuildEnvInfo", _dart_test_deps = "dart_test_deps", _flutter_assets = "flutter_assets", _pub_path_deps_check = "pub_path_deps_check", _pub_plugins_check = "pub_plugins_check") +load("//flutter/private:rules.bzl", _FlutterNativeBuildEnvInfo = "FlutterNativeBuildEnvInfo", _dart_test_deps = "dart_test_deps", _flutter_assets = "flutter_assets", _flutter_target_slices = "flutter_target_slices", _pub_path_deps_check = "pub_path_deps_check", _pub_plugins_check = "pub_plugins_check") FlutterArtifactInfo = _FlutterArtifactInfo FlutterPubspecInfo = _FlutterPubspecInfo @@ -13,6 +13,7 @@ FlutterNativeBuildEnvInfo = _FlutterNativeBuildEnvInfo flutter_debug_core = _flutter_debug_core flutter_pubspec = _flutter_pubspec flutter_assets = _flutter_assets +flutter_target_slices = _flutter_target_slices pub_path_deps_check = _pub_path_deps_check pub_plugins_check = _pub_plugins_check dart_test_deps = _dart_test_deps diff --git a/rules_flutter_core/flutter/defs.bzl b/rules_flutter_core/flutter/defs.bzl index 8e6bdf3e..61647e0b 100644 --- a/rules_flutter_core/flutter/defs.bzl +++ b/rules_flutter_core/flutter/defs.bzl @@ -1,12 +1,40 @@ """Supported convenience BUILD API for Flutter consumer modules.""" -load("//flutter/private:core.bzl", _flutter_core = "flutter_core") +load("//flutter/private:core.bzl", _default_platform_neutral_files = "platform_neutral_files", _flutter_core = "flutter_core", _platform_neutral_files_rule = "platform_neutral_files_rule") load("//flutter/private:flutter_test.bzl", _flutter_test = "flutter_test", _flutter_test_suite = "flutter_test_suite") load("//flutter/private:integration_test.bzl", _flutter_integration_test = "flutter_integration_test") load("//flutter/private:pubspec.bzl", _flutter_pubspec = "flutter_pubspec") -load("//flutter/private:rules.bzl", _flutter_package = "flutter_package") +load("//flutter/private:rules.bzl", _flutter_default_target_slices = "flutter_target_slices", _flutter_package = "flutter_package", _flutter_target_slices_rule = "flutter_target_slices_rule") flutter_core = _flutter_core + +def flutter_platform( + name, + target_platforms, + constraint, + slices_rule = None, + neutral_files_rule = None, + native_build_env = None): + """Describes target slices and the transitions used to build their artifacts. + + `slices_rule` returns split outputs keyed by `str(platform_label)`. + `neutral_files_rule` applies a 1:1 configuration transition to its source. + `native_build_env` supplies the environment for native asset hooks. + """ + if not target_platforms: + fail("flutter_platform: target_platforms must not be empty.") + return struct( + name = name, + target_platforms = target_platforms, + constraint = constraint, + slices_rule = slices_rule or _flutter_default_target_slices, + neutral_files_rule = neutral_files_rule or _default_platform_neutral_files, + native_build_env = native_build_env, + ) + +flutter_target_slices_rule = _flutter_target_slices_rule +platform_neutral_files_rule = _platform_neutral_files_rule + flutter_package = _flutter_package flutter_test = _flutter_test flutter_test_suite = _flutter_test_suite diff --git a/rules_flutter_core/flutter/extensions.bzl b/rules_flutter_core/flutter/extensions.bzl index 631934a8..837445a3 100644 --- a/rules_flutter_core/flutter/extensions.bzl +++ b/rules_flutter_core/flutter/extensions.bzl @@ -4,8 +4,7 @@ Consumers declare the expected host SDK version with the `flutter.sdk` tag. The extension stays independent of the host SDK; version validation happens when the local SDK repository is fetched. -Android extensions (`android_ndk`, `flutter_plugins_ext`) load from -`@rules_flutter_android//flutter:extensions.bzl`. +Platform-specific extensions load from their owning ruleset. """ load("//flutter/private:pub_lock.bzl", _pub = "pub") diff --git a/rules_flutter_core/flutter/abis.bzl b/rules_flutter_core/flutter/modes.bzl similarity index 59% rename from rules_flutter_core/flutter/abis.bzl rename to rules_flutter_core/flutter/modes.bzl index 6bdc999a..30cc819d 100644 --- a/rules_flutter_core/flutter/abis.bzl +++ b/rules_flutter_core/flutter/modes.bzl @@ -1,6 +1,6 @@ """Neutral Flutter build-mode names.""" -load("//flutter/private:abis.bzl", _MODES = "MODES") +load("//flutter/private:modes.bzl", _MODES = "MODES") visibility("public") diff --git a/rules_flutter_core/flutter/private/BUILD.bazel b/rules_flutter_core/flutter/private/BUILD.bazel index e8899868..c75c4d9c 100644 --- a/rules_flutter_core/flutter/private/BUILD.bazel +++ b/rules_flutter_core/flutter/private/BUILD.bazel @@ -93,7 +93,7 @@ toolchain_type( toolchain_type( name = "flutter_aot_toolchain_type", - no_match_error = "No Flutter AOT toolchain is registered for this platform. For Android, run `flutter precache --android` on this host to fetch the missing gen_snapshot payload.", + no_match_error = "No Flutter AOT toolchain is registered for this target platform. Ensure the platform ruleset has installed its gen_snapshot payload.", visibility = ["//visibility:public"], ) @@ -106,17 +106,3 @@ flutter_target_slice( pub_lock_parser_test_suite(name = "pub_lock_parser_test") staged_hook_packages_test(name = "staged_hook_packages_test") - -# Public because the core's select() is expanded in the consumer's package. -config_setting( - name = "target_os_android", - constraint_values = ["@platforms//os:android"], - visibility = ["//visibility:public"], -) - -config_setting( - name = "target_os_android_release", - constraint_values = ["@platforms//os:android"], - flag_values = {"//flutter:mode": "release"}, - visibility = ["//visibility:public"], -) diff --git a/rules_flutter_core/flutter/private/core.bzl b/rules_flutter_core/flutter/private/core.bzl index 3b9942aa..e8f53f5d 100644 --- a/rules_flutter_core/flutter/private/core.bzl +++ b/rules_flutter_core/flutter/private/core.bzl @@ -1,16 +1,15 @@ """The core artifact: an app's Dart outputs and host-test inputs. -`flutter_core` declares an app's platform-neutral inputs once. Its AOT outputs -are selected by `select()` on target-platform config settings; Android engine -libraries belong to `rules_flutter_android`. +`flutter_core` declares platform-neutral inputs once; each selected platform +supplies its target slices and artifact toolchains. """ load("@bazel_skylib//lib:shell.bzl", "shell") load("@bazel_skylib//rules:build_test.bzl", "build_test") load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") -load(":abis.bzl", "aot_target_compatible_with") +load(":modes.bzl", "aot_target_compatible_with") load(":pubspec.bzl", "FlutterPubspecInfo", "flutter_pubspec") -load(":rules.bzl", "dart_aot_elf", "dart_aot_kernel", "dart_jit_kernel", "flutter_assets", "flutter_package", "flutter_test_assets", "lib_root", "library_path", "pub_path_deps_check", "pub_plugins_check") +load(":rules.bzl", "FlutterAotSnapshotInfo", "dart_aot_kernel", "dart_aot_snapshot", "dart_jit_kernel", "flutter_assets", "flutter_package", "flutter_test_assets", "lib_root", "library_path", "pub_path_deps_check", "pub_plugins_check") visibility(["//flutter/...", "//tests/..."]) @@ -48,17 +47,18 @@ def _workspace_label(label): return label FlutterArtifactInfo = provider( - doc = "What flutter_core hands to the android_binary split that built it.", + doc = "A configured Flutter artifact set for one target slice.", fields = { - "mode": "`release` or `debug`.", - "abi": "The Android ABI of this split, or None on a host build.", - "libapp": "File: the libapp.so of this ABI, or None under debug.", - "assets": "Directory: the flutter_assets bundle, with the debug overlay under debug.", + "mode": "release or debug.", + "target_slice": "Stable platform slice identifier, or None on a host build.", + "aot": "The target platform's AOT artifact, or None under debug.", + "aot_kind": "The platform-declared AOT artifact kind, or None under debug.", + "assets": "Directory: the flutter_assets bundle with any debug overlay.", }, ) FlutterCoreInfo = provider( - doc = "What a flutter_debug_core needs from its app: the kernel inputs and the debug registrant.", + doc = "Kernel inputs, debug registrant, and per-platform mode-free assets for flutter_debug_core.", fields = { "package_config": "File: the pub hub's package_config.json.", "pub_srcs": "list of File: the hub's packageUri trees.", @@ -67,42 +67,19 @@ FlutterCoreInfo = provider( "debug_registrant": "File or None: the debug plugin registrant.", "registrant_path": "str: the registrant's path within its package, as a package: URI suffix.", "lib": "str: the app's lib/ prefix in the execroot; an entrypoint under it compiles as package:, under its own name.", - "mode_free_assets": "File: the app's flutter_assets tree before any debug overlay, platform neutral.", + "mode_free_assets": "File: the mode-free neutral asset tree for the selected platform configuration.", }, ) -_ANDROID_SPLIT_OUTPUTS = [ - # The options the Android split transition writes. The kernel and the bundle - # must not inherit any of them from whichever split asked. rules_android's - # bytecode_transformer flag is not in this list: core cannot name a - # rules_android label (D9). A consumer that sets it gets a second kernel and - # bundle configuration beside the split's, rather than one shared with the host. - "//command_line_option:Android configuration distinguisher", - "//command_line_option:android_compiler", - "//command_line_option:android_platforms", - "//command_line_option:compiler", - "//command_line_option:dynamic_mode", - "//command_line_option:platforms", -] +_PLATFORM_NEUTRAL_OUTPUTS = ["//command_line_option:platforms"] def _platform_neutral_transition_impl(_settings, _attr): - # One configuration for the kernel and the bundle, whichever split asks. - # Each value is the one a non-Android host configuration has. Setting only - # the platform left the distinguisher and dynamic_mode from the split, so - # the bundle was built twice. - return { - "//command_line_option:Android configuration distinguisher": "main", - "//command_line_option:android_compiler": None, - "//command_line_option:android_platforms": [], - "//command_line_option:compiler": None, - "//command_line_option:dynamic_mode": "default", - "//command_line_option:platforms": ["@platforms//host:host"], - } + return {"//command_line_option:platforms": ["@platforms//host:host"]} _platform_neutral_transition = transition( implementation = _platform_neutral_transition_impl, inputs = [], - outputs = _ANDROID_SPLIT_OUTPUTS, + outputs = _PLATFORM_NEUTRAL_OUTPUTS, ) def _platform_neutral_files_impl(ctx): @@ -111,16 +88,20 @@ def _platform_neutral_files_impl(ctx): (src,) = ctx.attr.src return [DefaultInfo(files = src[DefaultInfo].files)] -platform_neutral_files = rule( - implementation = _platform_neutral_files_impl, - doc = "Passes a target's files through, built under the host platform.", - attrs = { - "src": attr.label( - mandatory = True, - cfg = _platform_neutral_transition, - ), - }, -) +def platform_neutral_files_rule(configuration_transition): + """Creates a file pass-through rule with a 1:1 source configuration transition.""" + return rule( + implementation = _platform_neutral_files_impl, + doc = "Passes a target's files through under the supplied configuration reset.", + attrs = { + "src": attr.label( + mandatory = True, + cfg = configuration_transition, + ), + }, + ) + +platform_neutral_files = platform_neutral_files_rule(_platform_neutral_transition) def assets_bundle_action(ctx, src, kernel_files): """Declares the flutter_assets tree of `src`, with the debug overlay of the kernel when given. @@ -182,27 +163,30 @@ assets_bundle = rule( def _flutter_core_impl(ctx): mode = ctx.attr._mode[BuildSettingInfo].value - abi = ctx.toolchains[_TARGET_TOOLCHAIN_LABEL].flutter_target.slice_id if ctx.toolchains[_TARGET_TOOLCHAIN_LABEL] != None else None - libapp = ctx.files.libapp + target_slice = ctx.toolchains[_TARGET_TOOLCHAIN_LABEL].flutter_target.slice_id if ctx.toolchains[_TARGET_TOOLCHAIN_LABEL] != None else None + aot_files = ctx.files.aot assets = ctx.attr.assets[DefaultInfo].files.to_list() - (mode_free_assets,) = ctx.files.mode_free_assets if len(assets) != 1: fail("{}: expected one assets directory, got {}".format(ctx.label, len(assets))) - if len(libapp) > 1: - fail("{}: expected at most one libapp.so, got {}".format(ctx.label, len(libapp))) - if abi != None and mode == "release" and len(libapp) != 1: - fail("{}: release ABI '{}' needs one libapp.so".format(ctx.label, abi)) - + if len(aot_files) > 1: + fail("{}: expected at most one AOT artifact, got {}".format(ctx.label, len(aot_files))) + if target_slice != None and mode == "release" and len(aot_files) != 1: + fail("{}: release target slice '{}' needs one AOT artifact".format(ctx.label, target_slice)) + + mode_free_assets = ctx.files.mode_free_assets + if len(mode_free_assets) != 1: + fail("{}: expected one mode-free asset tree, got {}".format(ctx.label, len(mode_free_assets))) return [ - DefaultInfo(files = depset(libapp + [assets[0]])), + DefaultInfo(files = depset(aot_files + [assets[0]])), OutputGroupInfo( flutter_assets = depset([assets[0]]), - flutter_libapp = depset(libapp), + flutter_aot = depset(aot_files), ), FlutterArtifactInfo( mode = mode, - abi = abi, - libapp = libapp[0] if libapp else None, + target_slice = target_slice, + aot = aot_files[0] if aot_files else None, + aot_kind = ctx.attr.aot[0][FlutterAotSnapshotInfo].kind if ctx.attr.aot else None, assets = assets[0], ), FlutterCoreInfo( @@ -213,7 +197,7 @@ def _flutter_core_impl(ctx): debug_registrant = ctx.file.debug_registrant, registrant_path = library_path(ctx, ctx.file.debug_registrant, "dart_plugin_registrant") if ctx.file.debug_registrant else "", lib = lib_root(ctx.label), - mode_free_assets = mode_free_assets, + mode_free_assets = mode_free_assets[0], ), ctx.attr.pubspec[FlutterPubspecInfo], ] @@ -222,9 +206,9 @@ _flutter_core = rule( implementation = _flutter_core_impl, doc = "Collects one split's view of a flutter_core into FlutterArtifactInfo.", attrs = { - "libapp": attr.label_list(allow_files = True, doc = "The dart_aot_elf of this split; empty under debug and on a host build."), + "aot": attr.label_list(allow_files = True, doc = "The selected platform's AOT artifact; empty under debug or on host."), "assets": attr.label(mandatory = True, doc = "The platform-neutral bundle."), - "mode_free_assets": attr.label(allow_files = True, mandatory = True, cfg = _platform_neutral_transition, doc = "The flutter_assets tree before any debug overlay, built under the host platform. A flutter_debug_core overlays its own kernel on it, so the app's assets label is never named outside this package."), + "mode_free_assets": attr.label(mandatory = True, allow_files = True, doc = "The neutral mode-free assets selected for this platform configuration."), "pubspec": attr.label(mandatory = True, providers = [FlutterPubspecInfo]), "package_config": attr.label(allow_single_file = True, mandatory = True, doc = "The pub hub's package_config.json, read by a flutter_debug_core's kernel."), "pub_srcs": attr.label_list(allow_files = True, allow_empty = False, mandatory = True, doc = "The pub hub's :lib, read by a flutter_debug_core's kernel."), @@ -247,6 +231,7 @@ def _pub_labels(pub, who): def flutter_core( name, pub, + platforms, assets = None, entrypoint = "lib/main.dart", srcs = None, @@ -254,8 +239,6 @@ def flutter_core( dart_defines = {}, native_build_env = None, dart_plugin_registrant = None, - android_target_platforms = None, - aot_compatible_with = [], **kwargs): """Declares an app's Flutter artifacts and host-test inputs. @@ -273,10 +256,9 @@ def flutter_core( srcs: Dart sources for change detection; defaults to `glob(["lib/**/*.dart"])`. path_deps: sources of `path:` dependencies. dart_defines: values forwarded to frontend_server as `-D=` options. - native_build_env: the native build environment for asset bundle hooks, or None. + native_build_env: one label override for all platforms, a dict keyed by platform name, or None to use each descriptor's default. dart_plugin_registrant: an optional plugin registrant Dart file to compile. - android_target_platforms: platform labels used to configure target toolchains. - aot_compatible_with: additional compatibility constraints for platform-specific AOT artifacts. + platforms: platform descriptors returned by flutter_platform. **kwargs: visibility and tags passed to declared targets. """ if srcs == None: @@ -286,9 +268,11 @@ def flutter_core( if name == "pubspec": fail("flutter_core: `name` may not be \"pubspec\", which the core declares.") - if android_target_platforms == None: - fail("flutter_core: Android asset bundling requires target platforms; use flutter_android_core from rules_flutter_android.") - + target_platforms = [] + for platform in platforms: + target_platforms.extend(platform.target_platforms) + if not target_platforms: + fail("flutter_core: at least one platform with target platforms is required.") package_config, project_package_config, pub_lib, pub_all = _pub_labels(pub, "flutter_core " + name) aot_registrant = None @@ -302,25 +286,49 @@ def flutter_core( flutter_pubspec(name = "pubspec", src = "pubspec.yaml", visibility = kwargs.get("visibility")) - dart_aot_kernel( - name = name + "_kernel", - srcs = srcs, - pubspec = ":pubspec", - entrypoint = entrypoint, - package_config = package_config, - pub_srcs = [pub_lib], - path_deps = path_deps, - dart_plugin_registrant = aot_registrant, - dart_defines = dart_defines, - target_platforms = android_target_platforms, - target_compatible_with = aot_target_compatible_with(), - ) - - platform_neutral_files( - name = name + "_kernel_neutral", - src = ":" + name + "_kernel", - ) - + platform_slices = {} + platform_jit_kernel_neutral = {} + for platform in platforms: + slices_name = name + "_" + platform.name + "_slices" + platform.slices_rule( + name = slices_name, + target_platforms = platform.target_platforms, + ) + platform_slices[platform.name] = ":" + slices_name + kernel_neutral = {} + for platform in platforms: + kernel = name + "_" + platform.name + "_kernel" + dart_aot_kernel( + name = kernel, + srcs = srcs, + pubspec = ":pubspec", + entrypoint = entrypoint, + package_config = package_config, + pub_srcs = [pub_lib], + path_deps = path_deps, + dart_plugin_registrant = aot_registrant, + dart_defines = dart_defines, + target_slices = [platform_slices[platform.name]], + target_compatible_with = aot_target_compatible_with(), + ) + neutral = kernel + "_neutral" + platform.neutral_files_rule(name = neutral, src = ":" + kernel) + kernel_neutral[platform.name] = ":" + neutral + release_aot = {} + for platform in platforms: + release_config = name + "_" + platform.name + "_release" + native.config_setting( + name = release_config, + constraint_values = [platform.constraint], + flag_values = {Label("//flutter:mode"): "release"}, + ) + artifact = name + "_" + platform.name + "_aot" + dart_aot_snapshot( + name = artifact, + dill = kernel_neutral[platform.name], + target_compatible_with = [platform.constraint] + aot_target_compatible_with(), + ) + release_aot[":" + release_config] = [":" + artifact] dart_jit_kernel( name = name + "_jit_kernel", srcs = srcs, @@ -333,24 +341,31 @@ def flutter_core( dart_defines = dart_defines, ) - platform_neutral_files( - name = name + "_jit_kernel_neutral", - src = ":" + name + "_jit_kernel", - ) - - flutter_assets( - name = name + "_assets", - target_platforms = android_target_platforms, - native_build_env = native_build_env, - assets = assets, - pubspec = ":pubspec", - package_config = project_package_config, - pub_srcs = [pub_all], - path_deps = path_deps, - ) + for platform in platforms: + neutral = name + "_jit_kernel_neutral_" + platform.name + platform.neutral_files_rule(name = neutral, src = ":" + name + "_jit_kernel") + platform_jit_kernel_neutral[platform.name] = ":" + neutral + + platform_asset_actions = {} + for platform in platforms: + asset_action = name + "_" + platform.name + "_assets_action" + if type(native_build_env) == "dict": + platform_native_build_env = native_build_env.get(platform.name, platform.native_build_env) + else: + platform_native_build_env = native_build_env if native_build_env != None else platform.native_build_env + flutter_assets( + name = asset_action, + target_slices = [platform_slices[platform.name]], + native_build_env = platform_native_build_env, + assets = assets, + pubspec = ":pubspec", + package_config = project_package_config, + pub_srcs = [pub_all], + path_deps = path_deps, + ) + platform_asset_actions[platform.name] = ":" + asset_action # The host test reads this bundle and package, built for flutter_tester. - # It takes no ABI and no Android build environment. flutter_test_assets( name = name + "_test_assets", assets = assets, @@ -371,26 +386,36 @@ def flutter_core( tags = ["manual"], ) - assets_bundle( - name = name + "_bundle_action", - src = ":" + name + "_assets", - kernel = select({ - Label("//flutter:mode_debug"): [":" + name + "_jit_kernel_neutral"], - "//conditions:default": [], - }), - ) - - platform_neutral_files( - name = name + "_bundle", - src = ":" + name + "_bundle_action", - ) - - dart_aot_elf( - name = name + "_libapp", - dill = ":" + name + "_kernel_neutral", - strip = True, - target_compatible_with = aot_compatible_with + aot_target_compatible_with(), - ) + platform_assets = {} + platform_bundles = {} + assets_select = {} + mode_free_assets_select = {} + bundle_select = {} + for platform in platforms: + assets_neutral = name + "_assets_neutral_" + platform.name + bundle_neutral = name + "_bundle_" + platform.name + platform.neutral_files_rule(name = assets_neutral, src = platform_asset_actions[platform.name]) + platform_assets[platform.name] = ":" + assets_neutral + + bundle_action = name + "_" + platform.name + "_bundle_action" + assets_bundle( + name = bundle_action, + src = platform_asset_actions[platform.name], + kernel = select({ + Label("//flutter:mode_debug"): [platform_jit_kernel_neutral[platform.name]], + "//conditions:default": [], + }), + ) + platform.neutral_files_rule(name = bundle_neutral, src = ":" + bundle_action) + platform_bundles[platform.name] = ":" + bundle_neutral + mode_free_assets_select[platform.constraint] = ":" + assets_neutral + assets_select[platform.constraint] = [":" + assets_neutral] + bundle_select[platform.constraint] = [":" + bundle_neutral] + assets_select["//conditions:default"] = [platform_assets[platforms[0].name]] + bundle_select["//conditions:default"] = [platform_bundles[platforms[0].name]] + mode_free_assets_select["//conditions:default"] = platform_assets[platforms[0].name] + native.filegroup(name = name + "_assets", srcs = select(assets_select)) + native.filegroup(name = name + "_bundle", srcs = select(bundle_select)) guards = [":path_deps_check"] pub_path_deps_check( @@ -421,14 +446,12 @@ def flutter_core( **kwargs ) + release_aot["//conditions:default"] = [] _flutter_core( name = name, - libapp = select({ - Label("//flutter/private:target_os_android_release"): [":" + name + "_libapp"], - "//conditions:default": [], - }), + aot = select(release_aot), assets = ":" + name + "_bundle", - mode_free_assets = ":" + name + "_assets", + mode_free_assets = select(mode_free_assets_select), pubspec = ":pubspec", package_config = package_config, pub_srcs = [pub_lib], diff --git a/rules_flutter_core/flutter/private/debug_core.bzl b/rules_flutter_core/flutter/private/debug_core.bzl index 666e6a43..d23ef2c0 100644 --- a/rules_flutter_core/flutter/private/debug_core.bzl +++ b/rules_flutter_core/flutter/private/debug_core.bzl @@ -10,8 +10,13 @@ The app is reached only through its FlutterCoreInfo provider, so it may live in another package, behind a restrictive visibility, or be an alias. """ -load(":abis.bzl", "debug_target_compatible_with") -load(":core.bzl", "FlutterArtifactInfo", "FlutterCoreInfo", "assets_bundle_action", "platform_neutral_files") +load( + ":core.bzl", + "FlutterArtifactInfo", + "FlutterCoreInfo", + "assets_bundle_action", +) +load(":modes.bzl", "debug_target_compatible_with") load(":pubspec.bzl", "FlutterPubspecInfo") load(":rules.bzl", "TEST_IMPORT_HINT", "jit_kernel_action") @@ -19,9 +24,19 @@ visibility(["//flutter/..."]) _TOOLCHAIN = "//flutter/private:flutter_toolchain_type" +def _debug_bundle_platform_transition_impl(_settings, attr): + return {"//command_line_option:platforms": [attr.target_platform]} + +_debug_bundle_platform_transition = transition( + implementation = _debug_bundle_platform_transition_impl, + inputs = [], + outputs = ["//command_line_option:platforms"], +) + def _flutter_debug_kernel_impl(ctx): dill = ctx.actions.declare_file(ctx.label.name + ".dill") - core = ctx.attr.app[FlutterCoreInfo] + (app,) = ctx.attr.app + core = app[FlutterCoreInfo] # The app's inputs, read from its provider. The action is the one # dart_jit_kernel runs, so the test kernel and the app's debug kernel differ @@ -30,7 +45,7 @@ def _flutter_debug_kernel_impl(ctx): ctx, dill = dill, pkg = struct( - package_name = ctx.attr.app[FlutterPubspecInfo].package_name, + package_name = app[FlutterPubspecInfo].package_name, package_config = core.package_config, pub_srcs = core.pub_srcs, path_deps = core.path_deps, @@ -54,9 +69,11 @@ _flutter_debug_kernel = rule( attrs = { "app": attr.label( mandatory = True, + cfg = _debug_bundle_platform_transition, providers = [FlutterCoreInfo, FlutterPubspecInfo], doc = "The app's flutter_core target.", ), + "target_platform": attr.string(mandatory = True), "entrypoint": attr.label( allow_single_file = [".dart"], mandatory = True, @@ -74,26 +91,27 @@ _flutter_debug_kernel = rule( ) def _flutter_debug_bundle_impl(ctx): - core = ctx.attr.app[FlutterCoreInfo] + (app,) = ctx.attr.app + core = app[FlutterCoreInfo] (kernel,) = ctx.files.kernel - return [DefaultInfo(files = depset([assets_bundle_action(ctx, core.mode_free_assets, [kernel])]))] + mode_free_assets = core.mode_free_assets + return [DefaultInfo(files = depset([assets_bundle_action(ctx, mode_free_assets, [kernel])]))] -# Wrapped in platform_neutral_files, so the app is read in the platform-neutral -# configuration, as its own bundle is, and the bundle is built once. +# A descriptor's neutral wrapper resets this action before it reads the app +# bundle, so the kernel overlay does not inherit the requesting split settings. _flutter_debug_bundle_action = rule( implementation = _flutter_debug_bundle_impl, doc = "Overlays a debug kernel on an app's mode-free flutter_assets tree.", attrs = { + # Resolve the app edge under this descriptor so the overlay uses its asset tree. "app": attr.label( mandatory = True, + cfg = _debug_bundle_platform_transition, providers = [FlutterCoreInfo], doc = "The app's flutter_core target.", ), - "kernel": attr.label_list( - mandatory = True, - allow_files = True, - doc = "The debug kernel of this artifact's entrypoint.", - ), + "target_platform": attr.string(mandatory = True), + "kernel": attr.label_list(mandatory = True, allow_files = True, doc = "The debug kernel of this artifact's entrypoint."), }, toolchains = [_TOOLCHAIN], ) @@ -107,12 +125,13 @@ def _flutter_debug_core_impl(ctx): DefaultInfo(files = depset([assets])), OutputGroupInfo( flutter_assets = depset([assets]), - flutter_libapp = depset([]), + flutter_aot = depset([]), ), FlutterArtifactInfo( mode = "debug", - abi = artifact.abi, - libapp = None, + target_slice = artifact.target_slice, + aot = None, + aot_kind = None, assets = assets, ), ctx.attr.app[FlutterPubspecInfo], @@ -135,7 +154,7 @@ _flutter_debug_core = rule( }, ) -def flutter_debug_core(name, app, entrypoint, dart_defines = {}, srcs = [], **kwargs): +def flutter_debug_core(name, app, entrypoint, platforms, dart_defines = {}, srcs = [], **kwargs): """Declares a debug artifact of an app's core, compiled from another entrypoint. The artifact is debug only: a release configuration reports it incompatible, @@ -143,60 +162,76 @@ def flutter_debug_core(name, app, entrypoint, dart_defines = {}, srcs = [], **kw Args: name: the artifact's target name. - app: the app's `flutter_core` target. It may be in another package, under a + app: the app's `flutter_core` target. It may be in another package, under restrictive visibility, or an alias; only its own visibility applies. entrypoint: the Dart file compiled as `main`. A file under the app's lib/ - compiles as `package:/...`, the same library the app itself loads; - any other .dart file compiles by its execroot path, as dart_jit_kernel - compiles them. + compiles as `package:/...`; other files compile by their execroot path. + platforms: descriptors returned by flutter_platform; required to select the + platform-specific neutral bundle and asset tree. dart_defines: `-D` constants of this artifact's kernel. The app's are not inherited. srcs: Dart files outside lib/ that the entrypoint imports by relative path, - such as a helper under integration_test/. Undeclared, they are missing - from the kernel's sandbox and the compile fails. - **kwargs: `tags`, default `["manual"]`; `target_compatible_with`, combined - with the debug-only constraint; `testonly`, applied to every helper target; - and `visibility` for the artifact. + such as helpers under integration_test/. They must be declared for the + kernel's sandbox. + **kwargs: `tags`, default ["manual"]; `target_compatible_with`, combined + with the debug-only constraint; `testonly` for every helper; and + `visibility` for the artifact. """ + + if not platforms: + fail("flutter_debug_core: at least one platform is required.") testonly = kwargs.pop("testonly", False) tags = kwargs.pop("tags", ["manual"]) # The debug-only constraint is always kept; a caller's constraints add to it. + # Keep every helper manual and debug-only so wildcard builds cannot request + # an intermediate target in a configuration where the public artifact is incompatible. compatible = debug_target_compatible_with() + kwargs.pop("target_compatible_with", []) - - # Helpers are manual and debug-only as well, so no wildcard build reaches them - # in a configuration that the artifact itself is incompatible with. internal = {"tags": ["manual"], "target_compatible_with": compatible, "testonly": testonly} - _flutter_debug_kernel( - name = name + "_kernel", - app = app, - entrypoint = entrypoint, - dart_defines = dart_defines, - srcs = srcs, - **internal - ) - - # Built under the host platform, as the app's kernel is, so the Android - # split transitions run it once. - platform_neutral_files( - name = name + "_kernel_neutral", - src = ":" + name + "_kernel", - **internal - ) - - _flutter_debug_bundle_action( - name = name + "_bundle_action", - app = app, - kernel = [":" + name + "_kernel_neutral"], - **internal - ) - - platform_neutral_files( + bundle_select = {} + + for platform in platforms: + kernel = name + "_kernel_" + platform.name + _flutter_debug_kernel( + name = kernel, + app = app, + target_platform = str(platform.target_platforms[0]), + entrypoint = entrypoint, + dart_defines = dart_defines, + srcs = srcs, + **internal + ) + kernel_neutral = name + "_kernel_neutral_" + platform.name + platform.neutral_files_rule( + name = kernel_neutral, + src = ":" + kernel, + **internal + ) + bundle_action = name + "_bundle_action_" + platform.name + _flutter_debug_bundle_action( + name = bundle_action, + app = app, + target_platform = str(platform.target_platforms[0]), + kernel = [":" + kernel_neutral], + **internal + ) + bundle = name + "_bundle_" + platform.name + platform.neutral_files_rule( + name = bundle, + src = ":" + bundle_action, + **internal + ) + bundle_select[platform.constraint] = [":" + bundle] + bundle_select["//conditions:default"] = [":" + name + "_bundle_" + platforms[0].name] + + native.filegroup( name = name + "_bundle", - src = ":" + name + "_bundle_action", - **internal + srcs = select(bundle_select), + tags = tags, + testonly = testonly, + target_compatible_with = compatible, + visibility = kwargs.get("visibility"), ) - _flutter_debug_core( name = name, app = app, diff --git a/rules_flutter_core/flutter/private/integration_test.bzl b/rules_flutter_core/flutter/private/integration_test.bzl index 3d560728..4f01b0b4 100644 --- a/rules_flutter_core/flutter/private/integration_test.bzl +++ b/rules_flutter_core/flutter/private/integration_test.bzl @@ -29,13 +29,14 @@ _integration_test_info = rule( }, ) -def flutter_integration_test(name, app, src, srcs = [], dart_defines = {}, visibility = None): +def flutter_integration_test(name, app, src, platforms, srcs = [], dart_defines = {}, visibility = None): """Declares one platform-neutral integration-test file. Args: name: Name for the generated targets. app: Flutter application target used to compile the test. src: Integration-test Dart source file. + platforms: descriptors used to select the debug artifact's bundle. srcs: Additional Dart sources needed by the test. dart_defines: Compile-time Dart environment values. visibility: Visibility for this declaration. @@ -62,6 +63,7 @@ def flutter_integration_test(name, app, src, srcs = [], dart_defines = {}, visib name = core, app = app, entrypoint = ":" + bootstrap, + platforms = platforms, srcs = all_srcs, dart_defines = defines, tags = ["manual"], diff --git a/rules_flutter_core/flutter/private/merge_native_assets.py b/rules_flutter_core/flutter/private/merge_native_assets.py index a5b93de5..7c32665b 100644 --- a/rules_flutter_core/flutter/private/merge_native_assets.py +++ b/rules_flutter_core/flutter/private/merge_native_assets.py @@ -1,17 +1,10 @@ -"""Merge per-ABI asset bundles into one, and refuse if they differ elsewhere. +"""Merge asset bundles for one platform and reject inconsistent slice outputs. -`flutter build bundle` produces an almost architecture-independent tree. Exactly -one file depends on the target: `NativeAssetsManifest.json`, which is keyed by -architecture, and which the engine reads only its own key from -- the key is -compiled into the engine. So one bundle carrying every key serves every APK -variant, and a fat APK needs exactly that. +Each slice can emit a different NativeAssetsManifest.json entry, while all +other bundle files must be byte-identical. Verify that contract before merging +the manifests so one slice's assets are never silently used by another. -The merge is only sound while the rest of the tree is identical, so that is -checked rather than assumed: every other file is compared by content across the -bundles, and any difference fails the build. Without the check this rule would -silently ship one architecture's asset as another's. - -Key order is sorted, so the output is byte-stable across runs. +Manifest keys are sorted, keeping the output byte-stable across runs. """ import argparse @@ -42,16 +35,16 @@ def digest(path): return h.hexdigest() -def compare(base_abi, base, other_abi, other): +def compare(base_slice, base, other_slice, other): """Every difference between two bundles, ignoring the manifest.""" problems = [] only_base = sorted((tree(base) - tree(other)) - {MANIFEST}) only_other = sorted((tree(other) - tree(base)) - {MANIFEST}) for name in only_base: - problems.append(" {} has {}, {} does not".format(base_abi, name, other_abi)) + problems.append(" {} has {}, {} does not".format(base_slice, name, other_slice)) for name in only_other: - problems.append(" {} has {}, {} does not".format(other_abi, name, base_abi)) + problems.append(" {} has {}, {} does not".format(other_slice, name, base_slice)) for name in sorted((tree(base) & tree(other)) - {MANIFEST}): a, b = os.path.join(base, name), os.path.join(other, name) @@ -70,21 +63,21 @@ def merge(manifests): """One manifest carrying every bundle's native-assets keys.""" merged = None assets = {} - for abi, path in manifests: + for target_slice, path in manifests: with open(path) as handle: raw = handle.read() try: loaded = json.loads(raw) except ValueError as err: sys.exit("{} ({}) is not valid JSON: {}\nContents: {!r}".format( - path, abi, err, raw[:200])) + path, target_slice, err, raw[:200])) version = loaded.get("format-version") if merged is None: merged = version elif version != merged: sys.exit( - "NativeAssetsManifest.json format-version differs across ABIs: " + "NativeAssetsManifest.json format-version differs across target slices: " "{} vs {}. Merging them would be a guess.".format(merged, version) ) @@ -92,7 +85,7 @@ def merge(manifests): if key in assets and assets[key] != value: sys.exit( "Two bundles disagree about native-assets key " - "{!r} (seen again in {}).".format(key, abi) + "{!r} (seen again in {}).".format(key, target_slice) ) assets[key] = value @@ -109,33 +102,33 @@ def main(): "--bundle", action="append", default=[], - metavar="ABI=DIR", - help="A bundle to merge. The first is the one written into.", + metavar="TARGET_SLICE=DIR", + help="A bundle slice to merge. The first is the one written into.", ) args = parser.parse_args() bundles = [] for entry in args.bundle: - abi, _, path = entry.partition("=") - bundles.append((abi, path)) + target_slice, _, path = entry.partition("=") + bundles.append((target_slice, path)) if not bundles: sys.exit("--bundle is required") - base_abi, base = bundles[0] + base_slice, base = bundles[0] problems = [] - for abi, path in bundles[1:]: - problems += compare(base_abi, base, abi, path) + for target_slice, path in bundles[1:]: + problems += compare(base_slice, base, target_slice, path) if problems: sys.exit( - "Asset bundles differ between ABIs in files other than " - "{}:\n{}\n\nThe merged bundle is only correct while every ABI " + "Asset bundles differ between target slices in files other than " + "{}:\n{}\n\nThe merged bundle is only correct while every target slice " "produces the same assets. Something now varies by " - "architecture.".format(MANIFEST, "\n".join(problems)) + "target.".format(MANIFEST, "\n".join(problems)) ) - manifests = [(abi, os.path.join(path, MANIFEST)) for abi, path in bundles] - manifests = [(abi, path) for abi, path in manifests if os.path.exists(path)] + manifests = [(target_slice, os.path.join(path, MANIFEST)) for target_slice, path in bundles] + manifests = [(target_slice, path) for target_slice, path in manifests if os.path.exists(path)] if not manifests: return diff --git a/rules_flutter_core/flutter/private/abis.bzl b/rules_flutter_core/flutter/private/modes.bzl similarity index 100% rename from rules_flutter_core/flutter/private/abis.bzl rename to rules_flutter_core/flutter/private/modes.bzl diff --git a/rules_flutter_core/flutter/private/pub_lock.bzl b/rules_flutter_core/flutter/private/pub_lock.bzl index fd86e060..8fb18d9d 100644 --- a/rules_flutter_core/flutter/private/pub_lock.bzl +++ b/rules_flutter_core/flutter/private/pub_lock.bzl @@ -13,7 +13,7 @@ The `pub` module extension reads a consumer's `pubspec.lock` and creates `flutter build bundle` only reads `.dart_tool/package_config.json`, so the bundle action copies this one there inside its stage. - `package_graph.json`, the other file `flutter build bundle` reads there. - - `plugins_metadata.json`, platform-specific plugin lists; Android's list is consumed by `plugins.project`. + - `plugins_metadata.json`, platform-specific plugin lists consumed by the corresponding plugin-project rules. - `dart_plugin_registrant.dart`, the Dart registrant over the same plugins. - filegroups `:lib` (every hosted package's packageUri tree and pubspec) and `:all` (every hosted package file, for the bundle), and `:test_deps` (the debug diff --git a/rules_flutter_core/flutter/private/pubspec.bzl b/rules_flutter_core/flutter/private/pubspec.bzl index 51bb6b26..a4f56758 100644 --- a/rules_flutter_core/flutter/private/pubspec.bzl +++ b/rules_flutter_core/flutter/private/pubspec.bzl @@ -78,8 +78,7 @@ def flutter_pubspec(name = "pubspec", src = "pubspec.yaml", **kwargs): flutter_pubspec() - flutter_core(name = "app", pub = "@pub", ...) - flutter_android_app(name = "demo_app", core = "//:app", ...) + flutter_core(name = "app", pub = "@pub", platforms = [platform]) The defaults live here rather than on the rule's attribute deliberately. A label default on a rule resolves in the package that *defines* the rule, so diff --git a/rules_flutter_core/flutter/private/repo.bzl b/rules_flutter_core/flutter/private/repo.bzl index 78033f02..ce1840ef 100644 --- a/rules_flutter_core/flutter/private/repo.bzl +++ b/rules_flutter_core/flutter/private/repo.bzl @@ -217,7 +217,7 @@ def _flutter_sdk_impl(ctx): if not ctx.path(engine).exists: fail( "Flutter engine artifacts missing at {}.\n".format(engine) + - "Run `flutter precache --android` first.", + "Run `flutter precache` first.", ) ctx.symlink( @@ -268,7 +268,7 @@ def _flutter_sdk_impl(ctx): "flutter_patched_sdk", ) - # Android owns its per-ABI gen_snapshot repositories. + # Platform rulesets own their target AOT payload repositories. # The host-engine files: the debug snapshots the debug overlay ships as # vm_snapshot_data and isolate_snapshot_data, and the test runtime @@ -280,7 +280,7 @@ def _flutter_sdk_impl(ctx): if not ctx.path(path).exists: fail( "{} missing at {}.\n".format(name, path) + - "Run `flutter precache --android` on this host.", + "Run `flutter precache` on this host.", ) ctx.symlink(path, name) @@ -297,6 +297,8 @@ def _flutter_sdk_impl(ctx): for pkg_root in sdk_package_roots: ctx.symlink("{}/{}".format(root, pkg_root), pkg_root) + # The bundle action collects package-root LICENSE and NOTICES files; keep them + # declared so the SDK's third-party notices reach the final bundle. ctx.file("BUILD.bazel", _BUILD_TEMPLATE.format( sdk_globs = repr( ["{}/lib/**".format(r) for r in sdk_package_roots] + diff --git a/rules_flutter_core/flutter/private/rules.bzl b/rules_flutter_core/flutter/private/rules.bzl index 68414e46..946caa44 100644 --- a/rules_flutter_core/flutter/private/rules.bzl +++ b/rules_flutter_core/flutter/private/rules.bzl @@ -2,16 +2,16 @@ The Flutter build decomposes into two independent halves: - 1. Dart: .dart sources -> app.dill (kernel) -> libapp.so (AOT ELF) - 2. Platform: libapp.so + libflutter.so + flutter_assets -> APK/IPA + 1. Dart: .dart sources -> app.dill -> platform-declared AOT artifact + 2. Platform: Flutter artifacts -> platform-native application -These rules cover half 1 only, producing a plain `cc`-consumable .so that -rules_android / rules_apple can package in half 2. Nothing here shells out to -`flutter build`; the underlying frontend_server and gen_snapshot are driven -directly. +These rules cover the Dart half only, producing neutral Dart outputs that +platform rulesets package into their native applications. Nothing here shells +out to `flutter build`; the underlying frontend_server and gen_snapshot are +driven directly. -Half 2 is per-platform and is not loaded from here: see android.bzl. Keeping -them apart is what lets a consumer load only the platforms they build. +Platform packaging is per-platform and is not loaded from here. Keeping it +separate lets a consumer load only the platforms it builds. Package resolution ------------------ @@ -38,32 +38,26 @@ FlutterTargetSliceInfo = provider( ], ) -_ANDROID_SPLIT_OUTPUTS = [ - "//command_line_option:Android configuration distinguisher", - "//command_line_option:android_compiler", - "//command_line_option:android_platforms", - "//command_line_option:compiler", - "//command_line_option:dynamic_mode", - "//command_line_option:platforms", -] +FlutterTargetSlicesInfo = provider( + doc = "Platform metadata for the selected target slices.", + fields = ["slices"], +) + +FlutterAotSnapshotInfo = provider( + doc = "The AOT artifact kind produced by a snapshot action.", + fields = ["kind"], +) -def _target_platform_split_impl(_settings, attr): +def _platform_slice_transition_impl(_settings, attr): return { - str(platform): { - "//command_line_option:Android configuration distinguisher": "main", - "//command_line_option:android_compiler": None, - "//command_line_option:android_platforms": [], - "//command_line_option:compiler": None, - "//command_line_option:dynamic_mode": "default", - "//command_line_option:platforms": [str(platform)], - } + str(platform): {"//command_line_option:platforms": [str(platform)]} for platform in attr.target_platforms } -_target_platform_split = transition( - implementation = _target_platform_split_impl, +_platform_slice_transition = transition( + implementation = _platform_slice_transition_impl, inputs = [], - outputs = _ANDROID_SPLIT_OUTPUTS, + outputs = ["//command_line_option:platforms"], ) def _flutter_target_slice_impl(ctx): @@ -89,6 +83,28 @@ flutter_target_slice = rule( )], ) +def _flutter_target_slices_impl(ctx): + return [FlutterTargetSlicesInfo(slices = [ + ctx.split_attr.targets[str(platform.label)][0][FlutterTargetSliceInfo] + for platform in ctx.attr.target_platforms + ])] + +def flutter_target_slices_rule(slice_transition): + """Creates a split rule keyed by `str(platform_label)` for target-slice metadata.""" + return rule( + implementation = _flutter_target_slices_impl, + attrs = { + "target_platforms": attr.label_list(mandatory = True), + "targets": attr.label_list( + default = [Label("//flutter/private:target_slice")], + cfg = slice_transition, + providers = [FlutterTargetSliceInfo], + ), + }, + ) + +flutter_target_slices = flutter_target_slices_rule(_platform_slice_transition) + visibility(["//flutter"]) # The bundle action runs `flutter build bundle`, and the flutter tool is still a @@ -259,16 +275,18 @@ def _dart_aot_kernel_impl(ctx): target_os = ctx.attr.target_os if not target_os: - target_slices = ctx.split_attr.target_slice + target_slices = [ + slice + for target in ctx.attr.target_slices + for slice in target[FlutterTargetSlicesInfo].slices + ] target_os_values = { - target[FlutterTargetSliceInfo].target_os: True - for target in target_slices.values() - if target[FlutterTargetSliceInfo].target_os + target.target_os: True + for target in target_slices + if target.target_os } if not target_os_values: - fail("dart_aot_kernel has no Flutter target toolchain for {}; set target_os or register a platform ruleset".format( - ctx.attr.target_platforms[0] if ctx.attr.target_platforms else ctx.label, - )) + fail("dart_aot_kernel has no Flutter target toolchain for {}; set target_os or register a platform ruleset".format(ctx.label)) if len(target_os_values) != 1: fail("dart_aot_kernel target platforms must agree on target_os; got {}".format(sorted(target_os_values.keys()))) target_os = target_os_values.keys()[0] @@ -444,17 +462,13 @@ not get these values, and returns the default. They are in the action key, so changing a value recompiles the kernel; an empty dict adds no flags, so existing keys are unchanged.""", ), - "target_platforms": attr.label_list( - doc = "Platforms whose target toolchains provide kernel target-OS metadata.", - ), "target_os": attr.string( doc = "Target OS override; otherwise derived from the registered platform toolchain.", ), - "target_slice": attr.label( - default = Label("//flutter/private:target_slice"), - cfg = _target_platform_split, - providers = [FlutterTargetSliceInfo], - doc = "Internal per-platform target-toolchain metadata.", + "target_slices": attr.label_list( + mandatory = True, + providers = [FlutterTargetSlicesInfo], + doc = "Per-platform target-toolchain metadata.", ), }, toolchains = ["//flutter/private:flutter_toolchain_type"], @@ -1078,14 +1092,15 @@ flags, so existing keys are unchanged.""", toolchains = ["//flutter/private:flutter_toolchain_type"], ) -def _dart_aot_elf_impl(ctx): - # Under the target name, not at the package root. The basename is fixed -- - # Android splits compile this same label against distinct target toolchains; - # configuration-specific output roots keep the resulting libapp.so files apart. - so = ctx.actions.declare_file(ctx.label.name + "/libapp.so") +def _dart_aot_snapshot_impl(ctx): + toolchain = _flutter_aot_toolchain(ctx) + + # Keep the toolchain's output basename under the target's directory. Different + # targets can emit the same basename; the label path prevents duplicate outputs. + artifact = ctx.actions.declare_file(ctx.label.name + "/" + toolchain.output_name) if ctx.file.dill == None: fail( - "Target OS selection failed for label {} (slice {}): no AOT kernel is configured.".format( + "Target platform selection failed for label {} (slice {}): no AOT kernel is configured.".format( ctx.label, _flutter_target_toolchain(ctx).slice_id, ), @@ -1093,50 +1108,42 @@ def _dart_aot_elf_impl(ctx): args = ctx.actions.args() - # --deterministic is what makes this output byte-stable across runs, and - # therefore safe to cache. Verified: identical sha256 over repeat builds. + # Deterministic output omits timestamps that would otherwise perturb caches. args.add("--deterministic") - args.add("--snapshot_kind=app-aot-elf") - args.add_all(_flutter_target_toolchain(ctx).snapshot_flags) - # gen_snapshot only accepts the --flag=value form here; a space-separated - # pair makes it print usage and exit non-zero. - args.add(so, format = "--elf=%s") - if ctx.attr.strip: - args.add("--strip") + # gen_snapshot accepts these options only as --name=value. + args.add("--snapshot_kind=" + toolchain.snapshot_kind) + + # armv7 requires compatibility flags provided by the target toolchain. + args.add_all(_flutter_target_toolchain(ctx).snapshot_flags) + args.add(artifact, format = toolchain.output_flag + "%s") + if ctx.attr.strip and toolchain.strip_flag: + args.add(toolchain.strip_flag) - # Note Flutter's --split-debug-info (--save-debugging-info plus - # --dwarf-stack-traces) is deliberately not wired up. Without it a release - # snapshot keeps Dart's name table inline, so stack traces are already - # symbolic; the flag trades that away for ~512 KB. Not worth it here. + # Split-debug-info is deliberately not wired up. Without it the release + # snapshot retains Dart's name table inline, so stack traces remain symbolic; + # splitting trades that away for roughly 512 KB, which is not worthwhile here. args.add(ctx.file.dill) ctx.actions.run( executable = _flutter_aot_toolchain(ctx).gen_snapshot_release, arguments = [args], inputs = [ctx.file.dill], - outputs = [so], - mnemonic = "DartAotElf", - progress_message = "Generating AOT ELF %{label}", + outputs = [artifact], + mnemonic = "DartAotSnapshot", + progress_message = "Generating AOT artifact %{label}", ) - - return [DefaultInfo(files = depset([so]))] - -dart_aot_elf = rule( - implementation = _dart_aot_elf_impl, - doc = "Turns a kernel .dill into a stripped AOT libapp.so via gen_snapshot.", + return [ + DefaultInfo(files = depset([artifact])), + FlutterAotSnapshotInfo(kind = toolchain.output_kind), + ] + +dart_aot_snapshot = rule( + implementation = _dart_aot_snapshot_impl, + doc = "Turns an AOT kernel into the artifact declared by its platform toolchain.", attrs = { - "dill": attr.label( - allow_single_file = [".dill"], - ), - "strip": attr.bool( - default = True, - doc = "Drop DWARF debug info from the ELF.", - ), - "_mode": attr.label( - default = "//flutter:mode", - providers = [BuildSettingInfo], - ), + "dill": attr.label(allow_single_file = [".dill"]), + "strip": attr.bool(default = True), }, toolchains = [ "//flutter/private:flutter_target_toolchain_type", @@ -1368,18 +1375,17 @@ def flutter_package(name, pub, pubspec, srcs = None, path_deps = [], assets = No **kwargs ) -def _bundle_dir(out, abi, index): - """Where one ABI's bundle goes. +def _bundle_dir(out, slice_id, index): + """Where one target slice's bundle goes. - The first is the declared output itself, so the shared files are written - once rather than copied in afterwards; the rest are action-local scratch - used only for comparison and to read their manifests. + The first is the declared output itself, so shared files are written once. + Other slices use action-local scratch for comparison and manifest reading. """ if index == 0: return "$EXECROOT/{}".format(out.path) - return "$STAGE/bundles/{}".format(abi) + return "$STAGE/bundles/{}".format(slice_id) -def _bundle_command(out, abi, index, target_platform): +def _bundle_command(out, slice_id, index, target_platform): # `flutter_tool` (defined in the action preamble) runs the tool's snapshot # directly: bin/flutter writes engine.stamp into the SDK, which a sandbox # forbids. `--no-version-check` skips the update check, which runs `git fetch` @@ -1397,15 +1403,15 @@ def _bundle_command(out, abi, index, target_platform): --suppress-analytics >/dev/null rm -f "{dir}/.last_build_id" rm -rf "{dir}/native_assets" - """.format( +""".format( platform = target_platform, - dir = _bundle_dir(out, abi, index), + dir = _bundle_dir(out, slice_id, index), ) def _stage_flutter_assets(ctx, toolchain, platform_env): """Declares a flutter_assets bundle's inputs and stages them for flutter_tools. - The Android bundle and the tester bundle read the same files, so both stage + The application bundle and the tester bundle read the same files, so both stage them here. Returns the declared files, the manifest that lists them, and the shell that stages them and leaves it in the project root with FLUTTER_ROOT set. platform_env is shell that exports the platform's environment, if any. @@ -1503,8 +1509,8 @@ FlutterNativeBuildEnvInfo = provider( ) def _flutter_assets_impl(ctx): - # The directory must be named flutter_assets: android_binary derives the - # in-APK path from the artifact path with assets_dir stripped, and Flutter + # The directory must be named flutter_assets: consumers derive the + # runtime path from the artifact path with assets_dir stripped, and Flutter # requires the bundle at assets/flutter_assets/ at runtime. out = ctx.actions.declare_directory(ctx.label.name + "/flutter_assets") @@ -1529,9 +1535,20 @@ def _flutter_assets_impl(ctx): ), ) - # Code assets belong in APK JNI libraries, not the asset tree. Packaging - # recipes install them under lib//; discard flutter_tools' duplicate. - # NativeAssetsManifest.json is retained beside the bundle for runtime lookup. + target_slices = [ + slice + for target in ctx.attr.target_slices + for slice in target[FlutterTargetSlicesInfo].slices + ] + if not target_slices: + fail("{}: flutter_assets requires at least one target slice.".format(ctx.label)) + for target_slice in target_slices: + if not target_slice.slice_id or not target_slice.target_platform: + fail("{}: flutter_assets has no Flutter target toolchain for slice metadata.".format(ctx.label)) + + # Code assets belong in platform native libraries, not the asset tree. + # Packaging recipes install them in native library directories; discard + # flutter_tools' duplicate. NativeAssetsManifest.json stays beside the bundle for runtime lookup. cmd = staged.command + """# flutter_tools takes the project directory as two levels above --target, so # the stub sits in its own directory. It is compiled only for record-use and # imports nothing. @@ -1551,18 +1568,18 @@ python3 "$EXECROOT/{merger}" {merge_args} bundles = "\n".join([ _bundle_command( out, - ctx.split_attr.target_slice[str(platform.label)][FlutterTargetSliceInfo].slice_id, + target_slice.slice_id, i, - ctx.split_attr.target_slice[str(platform.label)][FlutterTargetSliceInfo].target_platform, + target_slice.target_platform, ) - for i, platform in enumerate(ctx.attr.target_platforms) + for i, target_slice in enumerate(target_slices) ]), merge_args = " ".join([ '--bundle "{}={}"'.format( - ctx.split_attr.target_slice[str(platform.label)][FlutterTargetSliceInfo].slice_id, - _bundle_dir(out, ctx.split_attr.target_slice[str(platform.label)][FlutterTargetSliceInfo].slice_id, i), + target_slice.slice_id, + _bundle_dir(out, target_slice.slice_id, i), ) - for i, platform in enumerate(ctx.attr.target_platforms) + for i, target_slice in enumerate(target_slices) ]), ) @@ -1628,20 +1645,21 @@ than a fact read out of it.""", allow_files = True, doc = "Sources of `path:` dependencies, staged and declared. See dart_aot_kernel.path_deps.", ), - "target_platforms": attr.label_list( - doc = "Platforms whose target toolchains provide asset-bundle metadata.", - ), - "target_slice": attr.label( - default = Label("//flutter/private:target_slice"), - cfg = _target_platform_split, - doc = "Internal per-platform target-toolchain metadata.", + "target_slices": attr.label_list( + mandatory = True, + providers = [FlutterTargetSlicesInfo], + doc = """Target slices to bundle for. Required, always a list. + +`flutter build bundle` runs once per target slice inside this action, because +NativeAssetsManifest.json is keyed by target architecture. The manifests are +merged and everything else is compared, so an architecture-varying bundle fails +instead of shipping.""", ), "native_build_env": attr.label( providers = [FlutterNativeBuildEnvInfo], doc = """The native build environment: shell exports and the input files they read, -provided by a native build environment rule. Android builds pass the rule from -rules_flutter_android so that native build hooks find the Android SDK. Omit it for a -build that has no Android SDK.""", +provided by a platform-specific native build environment rule. Omit it for a +build that has no native SDK.""", ), "_merger": attr.label( default = "//flutter/private:merge_native_assets.py", @@ -1687,7 +1705,7 @@ flutter_test_assets = rule( doc = """The asset bundle host tests read, built for flutter_tester's platform. Takes the flutter_assets inputs (pubspec, package config, pub hub, declared assets, -path dependencies), but no ABIs and no Android SDK. Built through flutter_tools' +path dependencies), but no target slices or native SDK. Built through flutter_tools' AssetBundle and writeBundle with TargetPlatform.tester, as `flutter test` builds it: `flutter build bundle` does not accept that platform.""", attrs = { diff --git a/rules_flutter_core/flutter/target_toolchain.bzl b/rules_flutter_core/flutter/target_toolchain.bzl index a05646fa..437ceeb0 100644 --- a/rules_flutter_core/flutter/target_toolchain.bzl +++ b/rules_flutter_core/flutter/target_toolchain.bzl @@ -5,16 +5,20 @@ FlutterTargetToolchainInfo = provider( fields = { "target_os": "Target OS passed to the AOT kernel compiler.", "snapshot_flags": "Target-specific gen_snapshot flags.", - "native_assets_key": "Flutter native-assets manifest key.", "target_platform": "Flutter bundle target-platform spelling.", "slice_id": "Stable identifier for this target slice.", }, ) FlutterAotToolchainInfo = provider( - doc = "AOT compiler executable for a target platform.", + doc = "AOT compiler and artifact format for a target platform.", fields = { - "gen_snapshot_release": "Host executable used to produce the release AOT ELF.", + "gen_snapshot_release": "Host executable used to produce the release AOT artifact.", + "output_kind": "Neutral artifact kind, such as elf or assembly.", + "output_name": "Artifact basename produced by the compiler.", + "snapshot_kind": "gen_snapshot snapshot kind argument.", + "output_flag": "gen_snapshot output argument prefix.", + "strip_flag": "Optional compiler flag for stripping the artifact.", }, ) @@ -22,7 +26,6 @@ def _flutter_target_toolchain_impl(ctx): return [platform_common.ToolchainInfo(flutter_target = FlutterTargetToolchainInfo( target_os = ctx.attr.target_os, snapshot_flags = ctx.attr.snapshot_flags, - native_assets_key = ctx.attr.native_assets_key, target_platform = ctx.attr.target_platform, slice_id = ctx.attr.slice_id, ))] @@ -30,6 +33,11 @@ def _flutter_target_toolchain_impl(ctx): def _flutter_aot_toolchain_impl(ctx): return [platform_common.ToolchainInfo(flutter_aot = FlutterAotToolchainInfo( gen_snapshot_release = ctx.executable.gen_snapshot_release, + output_kind = ctx.attr.output_kind, + output_name = ctx.attr.output_name, + snapshot_kind = ctx.attr.snapshot_kind, + output_flag = ctx.attr.output_flag, + strip_flag = ctx.attr.strip_flag, ))] flutter_target_toolchain = rule( @@ -37,7 +45,6 @@ flutter_target_toolchain = rule( attrs = { "target_os": attr.string(mandatory = True), "snapshot_flags": attr.string_list(), - "native_assets_key": attr.string(mandatory = True), "target_platform": attr.string(mandatory = True), "slice_id": attr.string(mandatory = True), }, @@ -52,5 +59,10 @@ flutter_aot_toolchain = rule( cfg = "exec", allow_single_file = True, ), + "output_kind": attr.string(mandatory = True), + "output_name": attr.string(mandatory = True), + "snapshot_kind": attr.string(mandatory = True), + "output_flag": attr.string(mandatory = True), + "strip_flag": attr.string(), }, ) diff --git a/rules_flutter_core/tools/ci/key_portability_test.py b/rules_flutter_core/tools/ci/key_portability_test.py index c78d0a0c..70ad3302 100755 --- a/rules_flutter_core/tools/ci/key_portability_test.py +++ b/rules_flutter_core/tools/ci/key_portability_test.py @@ -294,13 +294,13 @@ def check_c(self): if _contains_string(tree, token): self.fail("C", "rules.bzl must not use the {} execution requirement".format(token)) - aot_function = _function(tree, "_dart_aot_elf_impl") + aot_function = _function(tree, "_dart_aot_snapshot_impl") if aot_function is None: - self.fail("C", "{} must define _dart_aot_elf_impl".format(FILES["defs"])) + self.fail("C", "{} must define _dart_aot_snapshot_impl".format(FILES["defs"])) else: aot_action = _action_call(aot_function, "run") if aot_action is None: - self.fail("C", "_dart_aot_elf_impl must create its AOT action with ctx.actions.run") + self.fail("C", "_dart_aot_snapshot_impl must create its AOT action with ctx.actions.run") else: executable = _keyword(aot_action, "executable") if ( @@ -308,14 +308,14 @@ def check_c(self): or executable.attr != "gen_snapshot_release" or _call_path(executable.value) != ["_flutter_aot_toolchain"] ): - self.fail("C", "DartAotElf executable must come from its declared AOT toolchain") + self.fail("C", "DartAotSnapshot executable must come from its declared AOT toolchain") aot_toolchain = _function(tree, "_flutter_aot_toolchain") if aot_toolchain is None or not _contains_string(aot_toolchain, "//flutter/private:flutter_aot_toolchain_type"): - self.fail("C", "DartAotElf executable helper must read its declared AOT toolchain") - if not _rule_uses_toolchain(tree, "dart_aot_elf", "//flutter/private:flutter_target_toolchain_type"): - self.fail("C", "dart_aot_elf must declare the Flutter target toolchain type") - if not _rule_uses_toolchain(tree, "dart_aot_elf", "//flutter/private:flutter_aot_toolchain_type"): - self.fail("C", "dart_aot_elf must declare the Flutter AOT toolchain type") + self.fail("C", "DartAotSnapshot executable helper must read its declared AOT toolchain") + if not _rule_uses_toolchain(tree, "dart_aot_snapshot", "//flutter/private:flutter_target_toolchain_type"): + self.fail("C", "dart_aot_snapshot must declare the Flutter target toolchain type") + if not _rule_uses_toolchain(tree, "dart_aot_snapshot", "//flutter/private:flutter_aot_toolchain_type"): + self.fail("C", "dart_aot_snapshot must declare the Flutter AOT toolchain type") for target in ( "_dartaotruntime", @@ -1108,7 +1108,7 @@ def check_s(self): # Each pkg field comes from the app's provider or the rule's own srcs, so a # dropped or substituted input changes the shape and fails here. expected = { - "package_name": "ctx.attr.app[FlutterPubspecInfo].package_name", + "package_name": "app[FlutterPubspecInfo].package_name", "package_config": "core.package_config", "pub_srcs": "core.pub_srcs", "path_deps": "core.path_deps", @@ -1126,11 +1126,11 @@ def check_s(self): if values != expected: self.fail("S", "the debug kernel's pkg struct must take each field from the app's FlutterCoreInfo or ctx.files.srcs: {}".format(values)) kernel = _function(tree, "_flutter_debug_kernel_impl") - if kernel is None or "ctx.attr.app[FlutterCoreInfo]" not in ast.unparse(kernel): - self.fail("S", "_flutter_debug_kernel_impl must read the app through FlutterCoreInfo") + if kernel is None or "app[FlutterCoreInfo]" not in ast.unparse(kernel): + self.fail("S", "_flutter_debug_kernel_impl must read the transitioned app through FlutterCoreInfo") bundle = _function(tree, "_flutter_debug_bundle_impl") if bundle is None or "core.mode_free_assets" not in ast.unparse(bundle): - self.fail("S", "_flutter_debug_bundle_impl must overlay the app's mode-free assets from FlutterCoreInfo") + self.fail("S", "_flutter_debug_bundle_impl must overlay the app's selected mode-free assets from FlutterCoreInfo") # The provider's values must come from the app's own files, not from a label # the debug artifact derives. @@ -1150,7 +1150,7 @@ def check_s(self): "debug_registrant": "ctx.file.debug_registrant", "registrant_path": "library_path(ctx, ctx.file.debug_registrant, 'dart_plugin_registrant') if ctx.file.debug_registrant else ''", "lib": "lib_root(ctx.label)", - "mode_free_assets": "mode_free_assets", + "mode_free_assets": "mode_free_assets[0]", } if fields != filled: self.fail("S", "FlutterCoreInfo must be filled from the app's own files and labels: {}".format(fields)) diff --git a/tests/consumer/BUILD.bazel b/tests/consumer/BUILD.bazel index ed57df58..09e9aaf0 100644 --- a/tests/consumer/BUILD.bazel +++ b/tests/consumer/BUILD.bazel @@ -7,9 +7,9 @@ targets may also load composition.bzl; Android macros load from load("@bazel_skylib//rules:build_test.bzl", "build_test") load("@rules_flutter_android//flutter:composition.bzl", "flutter_android_inputs", "flutter_embedding_deps") -load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_app", "flutter_android_core", "flutter_assets", "flutter_native_contribution", "flutter_plugin_deps_guards") +load("@rules_flutter_android//flutter:defs.bzl", "FLUTTER_ANDROID", "flutter_android_app", "flutter_assets", "flutter_native_contribution", "flutter_plugin_deps_guards") load("@rules_flutter_core//flutter:composition.bzl", "pub_path_deps_check") -load("@rules_flutter_core//flutter:defs.bzl", "flutter_package", "flutter_pubspec", "flutter_test") +load("@rules_flutter_core//flutter:defs.bzl", "flutter_core", "flutter_package", "flutter_pubspec", "flutter_test") load("@rules_java//java:defs.bzl", "java_binary", "java_import") load( "@rules_kotlin//kotlin:android.bzl", @@ -97,7 +97,7 @@ flutter_test( # The macro, with every named parameter supplied -- it is the public entry point # most consumers use, and its signature is the thing most likely to drift. -flutter_android_core( +flutter_core( name = "app_core", srcs = ["lib/main.dart"], dart_plugin_registrant = "lib/dart_plugin_registrant.dart", @@ -107,6 +107,7 @@ flutter_android_core( ":production_probe_srcs", ":shared_probe_srcs", ], + platforms = [FLUTTER_ANDROID], pub = "@pub", ) diff --git a/tests/consumer/MODULE.bazel b/tests/consumer/MODULE.bazel index 4e67c2b5..fce25279 100644 --- a/tests/consumer/MODULE.bazel +++ b/tests/consumer/MODULE.bazel @@ -40,10 +40,19 @@ pub.lock( name = "hand_wired_pub", lock = "//hand_wired_app:pubspec.lock", ) -use_repo(pub, "composed_pub", "hand_wired_pub", "pub", "standard_layout_pub") +pub.lock( + name = "mixed_pub", + lock = "//mixed_platform:pubspec.lock", +) +use_repo(pub, "composed_pub", "hand_wired_pub", "mixed_pub", "pub", "standard_layout_pub") include("//android:config.MODULE.bazel") +register_toolchains( + "//fake_platform:fake_target_toolchain", + "//fake_platform:fake_aot_toolchain", +) + hand_wired_maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven") hand_wired_maven.install( name = "hand_wired_release", diff --git a/tests/consumer/MODULE.bazel.lock b/tests/consumer/MODULE.bazel.lock index c04ab18a..7022da10 100644 --- a/tests/consumer/MODULE.bazel.lock +++ b/tests/consumer/MODULE.bazel.lock @@ -408,7 +408,7 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%android_ndk": { "general": { - "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", + "bzlTransitiveDigest": "ookO9nSEvbJqeBy/tdtGBn7TVQtv0GXBN6yGHHqsAC4=", "usagesDigest": "AFQI5PN97mnks2yWgIX361TV8306rJ4VPuWmhG1yFNA=", "recordedInputs": [ "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", @@ -429,7 +429,7 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%flutter_plugins_ext": { "general": { - "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", + "bzlTransitiveDigest": "ookO9nSEvbJqeBy/tdtGBn7TVQtv0GXBN6yGHHqsAC4=", "usagesDigest": "MIZKH8xcfb1pscqq81jQhHF4ylGO08PRxzsEQhM4UcY=", "recordedInputs": [ "REPO_MAPPING:,composed_pub sameri11_rules_flutter_core++pub+composed_pub", @@ -540,7 +540,7 @@ }, "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_embedding": { "general": { - "bzlTransitiveDigest": "3eqHOPboGukDJg90t58rNh9rmebP4fSAVaS1+Xd+5d4=", + "bzlTransitiveDigest": "1rUycB7XyOhDdVVL+3WGQn0chQV5pDYB+oR++p5zzKA=", "usagesDigest": "VDubd1CC/3EsTr4xz8tz3uu65g6E5r8qQxmJ5I7keKE=", "recordedInputs": [ "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", @@ -601,7 +601,7 @@ }, "@@sameri11_rules_flutter_android+//flutter/private:target_toolchain_repo.bzl%flutter_android_toolchains": { "general": { - "bzlTransitiveDigest": "0D9Q8Q4AQrFirqM6wLGtrv3QlasjcQ2c2i2OxhVYFfI=", + "bzlTransitiveDigest": "qLNqwJd+HIxwhKWP/Y5+OzPbh0rMlz/HFaIdkadHxuM=", "usagesDigest": "2WhXsIhHLINViYFxmWfqIy1rlYxnWFK11LB9vSRc2M0=", "recordedInputs": [ "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", @@ -639,6 +639,11 @@ "armeabi-v7a": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a//:aot_toolchain'" }, "aot_toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:aot_toolchain_type'", + "cpu_constraints": { + "arm64-v8a": "'@@platforms//cpu:arm64'", + "x86_64": "'@@platforms//cpu:x86_64'", + "armeabi-v7a": "'@@platforms//cpu:armv7'" + }, "toolchain_targets": { "arm64-v8a": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_arm64_v8a'", "x86_64": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_x86_64'", @@ -652,7 +657,7 @@ }, "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { "general": { - "bzlTransitiveDigest": "Et4GM1IW3LCV7ah8t1qEeCgh9cOh1ek26YeJOVdYI+0=", + "bzlTransitiveDigest": "GRk2y7lduN/jEGLmTrEJhuPbFjOzVyU48T30FcvhBYU=", "usagesDigest": "3YgLOQ5KctGblzmwvkmzbmE3OR8fATY6yGn0owwy7n8=", "recordedInputs": [], "generatedRepoSpecs": { diff --git a/tests/consumer/composed_app/BUILD.bazel b/tests/consumer/composed_app/BUILD.bazel index 599380b7..5027414a 100644 --- a/tests/consumer/composed_app/BUILD.bazel +++ b/tests/consumer/composed_app/BUILD.bazel @@ -11,12 +11,8 @@ load("@rules_cc//cc:cc_binary.bzl", "cc_binary") load("@rules_cc//cc:cc_import.bzl", "cc_import") load("@rules_cc//cc:cc_library.bzl", "cc_library") load("@rules_flutter_android//flutter:composition.bzl", "android_native_files", "flutter_embedding_deps") -load( - "@rules_flutter_android//flutter:defs.bzl", - "FLUTTER_DEBUG_UNCOMPRESSED_ASSETS", - "flutter_android_app", - "flutter_android_core", -) +load("@rules_flutter_android//flutter:defs.bzl", "FLUTTER_ANDROID", "FLUTTER_DEBUG_UNCOMPRESSED_ASSETS", "flutter_android_app") +load("@rules_flutter_core//flutter:defs.bzl", "flutter_core") load("@rules_java//java:defs.bzl", "java_import", "java_library") load("@rules_java//java:java_single_jar.bzl", "java_single_jar") load("@rules_kotlin//kotlin:android.bzl", "kt_android_library") @@ -30,7 +26,7 @@ exports_files([ "recipe_package.bzl", ]) -flutter_android_core( +flutter_core( name = "core", srcs = glob(["lib/**/*.dart"]), assets = ["assets/composed-marker.txt"], @@ -44,6 +40,7 @@ flutter_android_core( "//:recipe_package_srcs", "//:shared_probe_srcs", ], + platforms = [FLUTTER_ANDROID], pub = "@composed_pub", ) @@ -302,7 +299,7 @@ filegroup( filegroup( name = "direct_libapp", srcs = [":core"], - output_group = "flutter_libapp", + output_group = "flutter_aot", tags = ["manual"], ) @@ -342,7 +339,7 @@ kt_android_library( android_binary( name = "direct_apk", assets = [":direct_assets"], - assets_dir = "core_bundle_action", + assets_dir = "core_android_bundle_action", manifest = "AndroidManifest.xml", manifest_values = { "applicationId": "com.example.composed_app.direct", diff --git a/tests/consumer/external_app/app/BUILD.bazel b/tests/consumer/external_app/app/BUILD.bazel index 4324b401..e4182eb0 100644 --- a/tests/consumer/external_app/app/BUILD.bazel +++ b/tests/consumer/external_app/app/BUILD.bazel @@ -1,12 +1,14 @@ load("@rules_flutter_android//flutter:composition.bzl", "flutter_android_inputs") -load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_core") +load("@rules_flutter_android//flutter:defs.bzl", "FLUTTER_ANDROID") +load("@rules_flutter_core//flutter:defs.bzl", "flutter_core") package(default_visibility = ["//visibility:public"]) -flutter_android_core( +flutter_core( name = "app", entrypoint = "lib/alternate$release.dart", native_build_env = "@rules_flutter_android//flutter:native_build_env", + platforms = [FLUTTER_ANDROID], pub = "@external_app_pub", ) diff --git a/tests/consumer/fake_platform/BUILD.bazel b/tests/consumer/fake_platform/BUILD.bazel new file mode 100644 index 00000000..b7f61529 --- /dev/null +++ b/tests/consumer/fake_platform/BUILD.bazel @@ -0,0 +1,49 @@ +load("@rules_flutter_core//flutter:target_toolchain.bzl", "flutter_aot_toolchain", "flutter_target_toolchain") +load("//fake_platform:fake_gen_snapshot.bzl", "fake_gen_snapshot") + +package(default_visibility = ["//visibility:public"]) + +constraint_setting(name = "fake_os") + +constraint_value( + name = "fake_os_value", + constraint_setting = ":fake_os", +) + +platform( + name = "fake_target", + constraint_values = [":fake_os_value"], +) + +fake_gen_snapshot(name = "fake_gen_snapshot") + +flutter_target_toolchain( + name = "fake_target_data", + slice_id = "fake", + snapshot_flags = [], + target_os = "ios", + target_platform = "ios", +) + +toolchain( + name = "fake_target_toolchain", + target_compatible_with = [":fake_os_value"], + toolchain = ":fake_target_data", + toolchain_type = "@rules_flutter_core//flutter:target_toolchain_type", +) + +flutter_aot_toolchain( + name = "fake_aot_data", + gen_snapshot_release = ":fake_gen_snapshot", + output_flag = "--assembly=", + output_kind = "assembly", + output_name = "app.S", + snapshot_kind = "app-aot-assembly", +) + +toolchain( + name = "fake_aot_toolchain", + target_compatible_with = [":fake_os_value"], + toolchain = ":fake_aot_data", + toolchain_type = "@rules_flutter_core//flutter:aot_toolchain_type", +) diff --git a/tests/consumer/fake_platform/defs.bzl b/tests/consumer/fake_platform/defs.bzl new file mode 100644 index 00000000..8adc90d3 --- /dev/null +++ b/tests/consumer/fake_platform/defs.bzl @@ -0,0 +1,49 @@ +"""Public descriptor for the second consumer-test platform.""" + +load("@rules_flutter_core//flutter:defs.bzl", "flutter_platform") + +def _fake_files_transition_impl(_settings, _attr): + return {"//command_line_option:platforms": [Label("//fake_platform:fake_target")]} + +_fake_files_transition = transition( + implementation = _fake_files_transition_impl, + inputs = [], + outputs = ["//command_line_option:platforms"], +) + +def _fake_neutral_files_impl(ctx): + (src,) = ctx.files.src + if src.is_directory: + out = ctx.actions.declare_directory(ctx.label.name + "/files") + command = 'cp -RL "$1"/. "$2"/' + if ctx.label.name.endswith("_assets_neutral_fake"): + command += ' && touch "$2/FAKE_PLATFORM_MARKER"' + ctx.actions.run_shell( + inputs = [src], + outputs = [out], + arguments = [src.path, out.path], + command = command, + ) + else: + out = ctx.actions.declare_file(ctx.label.name + "/" + src.basename) + ctx.actions.run_shell( + inputs = [src], + outputs = [out], + arguments = [src.path, out.path], + command = 'cp "$1" "$2"', + ) + return [DefaultInfo(files = depset([out]))] + +_fake_neutral_files = rule( + implementation = _fake_neutral_files_impl, + attrs = { + "src": attr.label(mandatory = True, allow_files = True, cfg = _fake_files_transition), + }, +) + +FLUTTER_FAKE = flutter_platform( + name = "fake", + target_platforms = [Label("//fake_platform:fake_target")], + constraint = Label("//fake_platform:fake_os_value"), + neutral_files_rule = _fake_neutral_files, +) diff --git a/tests/consumer/fake_platform/fake_app.bzl b/tests/consumer/fake_platform/fake_app.bzl new file mode 100644 index 00000000..d83c9d76 --- /dev/null +++ b/tests/consumer/fake_platform/fake_app.bzl @@ -0,0 +1,61 @@ +"""A consumer-side app rule that checks the public core artifact contract.""" + +load("@rules_flutter_core//flutter:composition.bzl", "FlutterArtifactInfo") + +def _fake_platform_transition_impl(_settings, _attr): + return {"//command_line_option:platforms": [Label("//fake_platform:fake_target")]} + +_fake_platform_transition = transition( + implementation = _fake_platform_transition_impl, + inputs = [], + outputs = ["//command_line_option:platforms"], +) + +def _fake_app_impl(ctx): + core_targets = ctx.attr.core + if len(core_targets) != 1: + fail("{}: expected one transitioned core target, got {}".format(ctx.label, len(core_targets))) + artifact = core_targets[0][FlutterArtifactInfo] + if artifact.aot_kind != "assembly": + fail("{}: expected the fake platform's assembly AOT artifact, got {}".format(ctx.label, artifact.aot_kind)) + if artifact.aot == None or artifact.aot.basename != "app.S": + fail("{}: expected the toolchain-declared app.S artifact".format(ctx.label)) + return [DefaultInfo(files = depset([artifact.aot, artifact.assets]))] + +flutter_fake_app = rule( + implementation = _fake_app_impl, + attrs = { + "core": attr.label( + mandatory = True, + providers = [FlutterArtifactInfo], + cfg = _fake_platform_transition, + ), + }, +) + +def _fake_debug_app_impl(ctx): + core_targets = ctx.attr.core + if len(core_targets) != 1: + fail("{}: expected one transitioned debug core target, got {}".format(ctx.label, len(core_targets))) + artifact = core_targets[0][FlutterArtifactInfo] + if artifact.mode != "debug": + fail("{}: expected a debug artifact, got {}".format(ctx.label, artifact.mode)) + verified = ctx.actions.declare_file(ctx.label.name + ".verified") + ctx.actions.run_shell( + inputs = [artifact.assets], + outputs = [verified], + arguments = [artifact.assets.path, verified.path], + command = 'test -f "$1/FAKE_PLATFORM_MARKER" || { echo "missing fake-platform asset marker" >&2; exit 1; }; touch "$2"', + ) + return [DefaultInfo(files = depset([artifact.assets, verified]))] + +flutter_fake_debug_app = rule( + implementation = _fake_debug_app_impl, + attrs = { + "core": attr.label( + mandatory = True, + providers = [FlutterArtifactInfo], + cfg = _fake_platform_transition, + ), + }, +) diff --git a/tests/consumer/fake_platform/fake_gen_snapshot.bzl b/tests/consumer/fake_platform/fake_gen_snapshot.bzl new file mode 100644 index 00000000..a829276c --- /dev/null +++ b/tests/consumer/fake_platform/fake_gen_snapshot.bzl @@ -0,0 +1,26 @@ +"""Produces a deterministic executable stand-in for gen_snapshot.""" + +_FAKE_SNAPSHOT = """#!/usr/bin/env bash +set -eu +output="" +for arg in "$@"; do + case "$arg" in + --assembly=*) output=${arg#--assembly=} ;; + esac +done +if [ -z "$output" ]; then + echo "expected --assembly=" >&2 + exit 1 +fi +printf '.text\\n.globl fake_flutter_aot\\nfake_flutter_aot:\\n' > "$output" +""" + +def _fake_gen_snapshot_impl(ctx): + output = ctx.actions.declare_file(ctx.label.name) + ctx.actions.write(output = output, content = _FAKE_SNAPSHOT, is_executable = True) + return [DefaultInfo(executable = output, files = depset([output]))] + +fake_gen_snapshot = rule( + implementation = _fake_gen_snapshot_impl, + executable = True, +) diff --git a/tests/consumer/hand_wired_app/BUILD.bazel b/tests/consumer/hand_wired_app/BUILD.bazel index d7e14efd..bf6b27a3 100644 --- a/tests/consumer/hand_wired_app/BUILD.bazel +++ b/tests/consumer/hand_wired_app/BUILD.bazel @@ -1,17 +1,19 @@ load("@rules_android//rules:rules.bzl", "android_binary", "android_library") load("@rules_flutter_android//flutter:composition.bzl", "flutter_build_config", "flutter_embedding_deps", "flutter_plugin_registrant") -load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_core") -load("@rules_java//java:defs.bzl", "java_import") +load("@rules_flutter_android//flutter:defs.bzl", "FLUTTER_ANDROID") +load("@rules_flutter_core//flutter:defs.bzl", "flutter_core") +load("@rules_java//java:java_import.bzl", "java_import") package(default_visibility = ["//visibility:private"]) -flutter_android_core( +flutter_core( name = "core", srcs = glob(["lib/**/*.dart"]), path_deps = [ "//fixtures/hand_wired_regular:sources", "//fixtures/hand_wired_dev:sources", ], + platforms = [FLUTTER_ANDROID], pub = "@hand_wired_pub", ) @@ -99,7 +101,7 @@ android_library( android_binary( name = "hand_wired_app", assets = [":flutter_assets"], - assets_dir = "core_bundle_action", + assets_dir = "core_android_bundle_action", manifest = "src/main/AndroidManifest.xml", manifest_values = { "applicationId": "com.example.handwired", diff --git a/tests/consumer/mixed_platform/AndroidManifest.xml b/tests/consumer/mixed_platform/AndroidManifest.xml new file mode 100644 index 00000000..4fd1eb70 --- /dev/null +++ b/tests/consumer/mixed_platform/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/tests/consumer/mixed_platform/BUILD.bazel b/tests/consumer/mixed_platform/BUILD.bazel new file mode 100644 index 00000000..dea4b9f5 --- /dev/null +++ b/tests/consumer/mixed_platform/BUILD.bazel @@ -0,0 +1,48 @@ +load("@rules_flutter_android//flutter:defs.bzl", "FLUTTER_ANDROID", "flutter_android_app") +load("@rules_flutter_core//flutter:composition.bzl", "flutter_debug_core") +load("@rules_flutter_core//flutter:defs.bzl", "flutter_core") +load("//fake_platform:defs.bzl", "FLUTTER_FAKE") +load("//fake_platform:fake_app.bzl", "flutter_fake_app", "flutter_fake_debug_app") + +package(default_visibility = ["//visibility:public"]) + +flutter_core( + name = "core", + platforms = [ + FLUTTER_FAKE, + FLUTTER_ANDROID, + ], + pub = "@mixed_pub", +) + +flutter_debug_core( + name = "mixed_debug_core", + testonly = True, + app = ":core", + entrypoint = "lib/main.dart", + platforms = [ + FLUTTER_ANDROID, + FLUTTER_FAKE, + ], +) + +flutter_fake_app( + name = "fake_app", + core = ":core", +) + +flutter_fake_debug_app( + name = "fake_debug_app", + testonly = True, + core = ":mixed_debug_core", +) + +flutter_android_app( + name = "android_app", + core = ":core", + manifest = "AndroidManifest.xml", + manifest_values = {"applicationId": "com.example.mixed_platform"}, + plugins = None, + resource_files = [], + test_apps = {"mixed_debug": {"core": ":mixed_debug_core"}}, +) diff --git a/tests/consumer/mixed_platform/assets/marker.txt b/tests/consumer/mixed_platform/assets/marker.txt new file mode 100644 index 00000000..3052ebd6 --- /dev/null +++ b/tests/consumer/mixed_platform/assets/marker.txt @@ -0,0 +1 @@ +mixed platform asset diff --git a/tests/consumer/mixed_platform/lib/main.dart b/tests/consumer/mixed_platform/lib/main.dart new file mode 100644 index 00000000..653b164e --- /dev/null +++ b/tests/consumer/mixed_platform/lib/main.dart @@ -0,0 +1,5 @@ +import 'package:flutter/material.dart'; + +void main() { + runApp(const MaterialApp(home: Text('mixed platform'))); +} diff --git a/tests/consumer/mixed_platform/pubspec.lock b/tests/consumer/mixed_platform/pubspec.lock new file mode 100644 index 00000000..4c6a0478 --- /dev/null +++ b/tests/consumer/mixed_platform/pubspec.lock @@ -0,0 +1,55 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + characters: + dependency: transitive + description: + name: characters + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + url: "https://pub.dev" + source: hosted + version: "1.4.1" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" + url: "https://pub.dev" + source: hosted + version: "0.13.0" + meta: + dependency: transitive + description: + name: meta + sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" + url: "https://pub.dev" + source: hosted + version: "1.18.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + url: "https://pub.dev" + source: hosted + version: "2.2.0" +sdks: + dart: ">=3.12.2 <4.0.0" diff --git a/tests/consumer/mixed_platform/pubspec.yaml b/tests/consumer/mixed_platform/pubspec.yaml new file mode 100644 index 00000000..da66aece --- /dev/null +++ b/tests/consumer/mixed_platform/pubspec.yaml @@ -0,0 +1,9 @@ +name: mixed_platform +version: 1.0.0+1 +environment: + sdk: ^3.12.2 +dependencies: + flutter: + sdk: flutter +flutter: + uses-material-design: true diff --git a/tests/consumer/standard_layout/BUILD.bazel b/tests/consumer/standard_layout/BUILD.bazel index 575cb1b7..d3327144 100644 --- a/tests/consumer/standard_layout/BUILD.bazel +++ b/tests/consumer/standard_layout/BUILD.bazel @@ -1,11 +1,13 @@ # A standard Flutter package layout with a nondefault entrypoint. -load("@rules_flutter_android//flutter:defs.bzl", "flutter_android_core") +load("@rules_flutter_android//flutter:defs.bzl", "FLUTTER_ANDROID") +load("@rules_flutter_core//flutter:defs.bzl", "flutter_core") package(default_visibility = ["//visibility:public"]) -flutter_android_core( +flutter_core( name = "core", entrypoint = "lib/alternate$release.dart", native_build_env = "@rules_flutter_android//flutter:native_build_env", + platforms = [FLUTTER_ANDROID], pub = "@standard_layout_pub", ) diff --git a/tests/core_consumer/MODULE.bazel.lock b/tests/core_consumer/MODULE.bazel.lock index 32c7b48b..1debea63 100644 --- a/tests/core_consumer/MODULE.bazel.lock +++ b/tests/core_consumer/MODULE.bazel.lock @@ -467,7 +467,7 @@ }, "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { "general": { - "bzlTransitiveDigest": "Et4GM1IW3LCV7ah8t1qEeCgh9cOh1ek26YeJOVdYI+0=", + "bzlTransitiveDigest": "GRk2y7lduN/jEGLmTrEJhuPbFjOzVyU48T30FcvhBYU=", "usagesDigest": "M9zv5xCGzmNUd7ho3lam884NDO9+c0DK/RoIPwdODYA=", "recordedInputs": [], "generatedRepoSpecs": { diff --git a/tests/namespace_dynamic/MODULE.bazel.lock b/tests/namespace_dynamic/MODULE.bazel.lock index b29207f5..af0e5c14 100644 --- a/tests/namespace_dynamic/MODULE.bazel.lock +++ b/tests/namespace_dynamic/MODULE.bazel.lock @@ -404,7 +404,7 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%android_ndk": { "general": { - "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", + "bzlTransitiveDigest": "ookO9nSEvbJqeBy/tdtGBn7TVQtv0GXBN6yGHHqsAC4=", "usagesDigest": "EZM/Ob3Auw0v6B0CQI9nhTMvFryd8ZLaCRIMfYFeY0s=", "recordedInputs": [ "REPO_MAPPING:sameri11_rules_flutter_android+,platforms platforms", @@ -425,7 +425,7 @@ }, "@@sameri11_rules_flutter_android+//flutter:extensions.bzl%flutter_plugins_ext": { "general": { - "bzlTransitiveDigest": "bMzqQxYNohpWNFPUQxYQALXNG5OTO80rAQiCo+YCcuk=", + "bzlTransitiveDigest": "ookO9nSEvbJqeBy/tdtGBn7TVQtv0GXBN6yGHHqsAC4=", "usagesDigest": "RqVa86t+EWrrGg+0ZGJnQnjPe2QKakZHbi5XlHpeEh8=", "recordedInputs": [ "REPO_MAPPING:,dynamic_namespace_metadata +dynamic_namespace_metadata+dynamic_namespace_metadata", @@ -465,7 +465,7 @@ }, "@@sameri11_rules_flutter_android+//flutter/private:embedding_repo.bzl%flutter_android_embedding": { "general": { - "bzlTransitiveDigest": "3eqHOPboGukDJg90t58rNh9rmebP4fSAVaS1+Xd+5d4=", + "bzlTransitiveDigest": "1rUycB7XyOhDdVVL+3WGQn0chQV5pDYB+oR++p5zzKA=", "usagesDigest": "VDubd1CC/3EsTr4xz8tz3uu65g6E5r8qQxmJ5I7keKE=", "recordedInputs": [ "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", @@ -526,7 +526,7 @@ }, "@@sameri11_rules_flutter_android+//flutter/private:target_toolchain_repo.bzl%flutter_android_toolchains": { "general": { - "bzlTransitiveDigest": "0D9Q8Q4AQrFirqM6wLGtrv3QlasjcQ2c2i2OxhVYFfI=", + "bzlTransitiveDigest": "qLNqwJd+HIxwhKWP/Y5+OzPbh0rMlz/HFaIdkadHxuM=", "usagesDigest": "2WhXsIhHLINViYFxmWfqIy1rlYxnWFK11LB9vSRc2M0=", "recordedInputs": [ "REPO_MAPPING:sameri11_rules_flutter_android+,flutter_sdk sameri11_rules_flutter_core++flutter+flutter_sdk", @@ -564,6 +564,11 @@ "armeabi-v7a": "'@@sameri11_rules_flutter_android++flutter_android_toolchains+flutter_gen_snapshot_armeabi_v7a//:aot_toolchain'" }, "aot_toolchain_type": "'@@sameri11_rules_flutter_core+//flutter:aot_toolchain_type'", + "cpu_constraints": { + "arm64-v8a": "'@@platforms//cpu:arm64'", + "x86_64": "'@@platforms//cpu:x86_64'", + "armeabi-v7a": "'@@platforms//cpu:armv7'" + }, "toolchain_targets": { "arm64-v8a": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_arm64_v8a'", "x86_64": "'@@sameri11_rules_flutter_android+//flutter:target_toolchain_x86_64'", @@ -577,7 +582,7 @@ }, "@@sameri11_rules_flutter_core+//flutter:extensions.bzl%flutter": { "general": { - "bzlTransitiveDigest": "Et4GM1IW3LCV7ah8t1qEeCgh9cOh1ek26YeJOVdYI+0=", + "bzlTransitiveDigest": "GRk2y7lduN/jEGLmTrEJhuPbFjOzVyU48T30FcvhBYU=", "usagesDigest": "4778tf6hjNJFXpm3IbdSjOP2Pn3Qa43Y+4cT+Z0KOnk=", "recordedInputs": [], "generatedRepoSpecs": { diff --git a/tools/ci/core_target_os_rejection.py b/tools/ci/core_target_os_rejection.py index 64eb3943..56b120b5 100755 --- a/tools/ci/core_target_os_rejection.py +++ b/tools/ci/core_target_os_rejection.py @@ -22,7 +22,7 @@ def run(workspace, output_user_root): "--nobuild", "--lockfile_mode=update", "--platforms=@platforms//host:host", - "//:app_core_libapp", + "//:app_core_android_aot", ], cwd=workspace, stdout=subprocess.PIPE, @@ -64,12 +64,12 @@ def main(): if rejected.returncode == 0: sys.exit("FAIL: host-configured macro-generated libapp unexpectedly analyzed") explicit_error = ( - "Target //:app_core_libapp is incompatible and cannot be built, " + "Target //:app_core_android_aot is incompatible and cannot be built, " "but was explicitly requested" ) if explicit_error not in rejected.stdout: sys.exit("FAIL: rejection lacked {!r}:\n{}".format(explicit_error, rejected.stdout)) - target_line = "//:app_core_libapp" + target_line = "//:app_core_android_aot" dependency_chain = rejected.stdout.partition("Dependency chain:")[2] has_target_os_rejection = ( target_line in dependency_chain diff --git a/tools/ci/native_hook_env_analysis.py b/tools/ci/native_hook_env_analysis.py index 2dcc55fb..69dbb8d5 100755 --- a/tools/ci/native_hook_env_analysis.py +++ b/tools/ci/native_hook_env_analysis.py @@ -64,7 +64,7 @@ def main(): missing = _run(workspace, output_root, "//:app_core_assets") if missing.returncode == 0: sys.exit("FAIL: production staged hook did not require native_build_env") - expected = ("Error in fail:", "//:app_core_assets", "native_build_env", "production_probe", "hook/build.dart") + expected = ("Error in fail:", "//:app_core_android_assets_action", "native_build_env", "production_probe", "hook/build.dart") diagnostic = next( (line for line in missing.stdout.splitlines() if all(part in line for part in expected)), None, diff --git a/tools/ci/test_apk_kernel_check.py b/tools/ci/test_apk_kernel_check.py index f9f06dca..62153a6d 100755 --- a/tools/ci/test_apk_kernel_check.py +++ b/tools/ci/test_apk_kernel_check.py @@ -39,14 +39,15 @@ def apk_path(bazel: str, module: Path, label: str, bazel_args: list[str]) -> Pat return files[0] -# Kernel artifacts are named by the core rule's private output target. +# Kernel artifacts are named by the core rule's private platform-specific output target. def kernel_path(bazel: str, module: Path, label: str, bazel_args: list[str]) -> Path: - files = [path for path in outputs(bazel, module, label + "_kernel", bazel_args) if path.suffix == ".dill" and path.is_file()] + kernel_label = label + "_kernel_android" + files = [path for path in outputs(bazel, module, kernel_label, bazel_args) if path.suffix == ".dill" and path.is_file()] unique = {} for path in files: unique.setdefault(hashlib.sha256(path.read_bytes()).digest(), path) if len(unique) != 1: - raise RuntimeError("{} has no unique built kernel contents: {}".format(label, files)) + raise RuntimeError("{} has no unique built Android kernel contents: {}".format(label, files)) return next(iter(unique.values())) @@ -107,7 +108,7 @@ def main() -> int: with zipfile.ZipFile(apk) as archive: embedded = archive.read("assets/flutter_assets/kernel_blob.bin") if embedded != kernel.read_bytes(): - raise RuntimeError("{} kernel_blob.bin differs from {}_kernel".format(apk_label, core_label)) + raise RuntimeError("{} kernel_blob.bin differs from {}_kernel_android".format(apk_label, core_label)) graph = json.loads(run(args.bazel, module, [ "aquery", "--lockfile_mode=error", "--output=jsonproto", "deps({})".format(apk_label), diff --git a/tools/ci/test_kernel_invariants.sh b/tools/ci/test_kernel_invariants.sh index 4cdfc8d1..bf038388 100755 --- a/tools/ci/test_kernel_invariants.sh +++ b/tools/ci/test_kernel_invariants.sh @@ -103,12 +103,12 @@ find_build() { if grep -E -q "$1" "$f"; then echo "$f"; fi done | sed -n 1p } -app_build=$(find_build '^flutter_(android_)?core\(') +app_build=$(find_build '^flutter_core\(') if [ -z "$app_build" ]; then - echo "FAIL: no flutter_core or flutter_android_core package for the undeclared-import probe" >&2 + echo "FAIL: no flutter_core package for the undeclared-import probe" >&2 exit 1 fi -core_name=$(awk '/^flutter_(android_)?core\(/ { f = 1 } f && /name = "/ { match($0, /"[^"]*"/); print substr($0, RSTART + 1, RLENGTH - 2); exit }' "$app_build") +core_name=$(awk '/^flutter_core\(/ { f = 1 } f && /name = "/ { match($0, /"[^"]*"/); print substr($0, RSTART + 1, RLENGTH - 2); exit }' "$app_build") app_target=":${core_name}_test_package" probe_dir=$(dirname "$app_build") app_pkg=${probe_dir#.}