use std::path::Path;
/// Opt-in env var gating FRB regeneration from this build script.
///
/// alef owns `flutter_rust_bridge_codegen` regeneration exclusively through its own
/// post-build step (`alef generate` / `alef build`), which applies the full post-processing
/// pipeline (sealed-variant renaming, native-library-loader import rewrite, excluded-function
/// filtering, injected text methods, bridge-coverage verification). This build script's own
/// invocation only ever replicated a partial subset of that pipeline, so a `cargo build` run
/// after `alef generate` (lint, `cargo test`, `cargo clippy`, or plain local iteration) would
/// silently regenerate the committed bridge with different content -- alef #140. Regeneration
/// here is now opt-in for local Flutter-only iteration (editing `lib.rs` without alef on
/// hand); the result bypasses alef's post-processing and must not be committed.
const ALEF_FRB_REGENERATE_ON_BUILD: &str = "ALEF_FRB_REGENERATE_ON_BUILD";
fn frb_regeneration_opted_in() -> bool {
matches!(std::env::var(ALEF_FRB_REGENERATE_ON_BUILD).as_deref(), Ok("1") | Ok("true"))
}
fn main() {
// Re-run whenever any Rust source changes, FRB config changes, or the opt-in gate flips.
println!("cargo:rerun-if-changed=src");
println!("cargo:rerun-if-changed=flutter_rust_bridge.yaml");
println!("cargo:rerun-if-env-changed={ALEF_FRB_REGENERATE_ON_BUILD}");
if !frb_regeneration_opted_in() {
println!(
"cargo:warning=flutter_rust_bridge_codegen regeneration skipped by default -- using the committed generated bridge. Run `alef generate` to regenerate it (with alef's full post-processing). Set {ALEF_FRB_REGENERATE_ON_BUILD}=1 to regenerate here instead for local-only iteration; the result bypasses alef's post-processing and must not be committed."
);
return;
}
match std::process::Command::new("flutter_rust_bridge_codegen")
.args(["generate", "--config-file", "flutter_rust_bridge.yaml"])
.status()
{
Ok(status) if status.success() => {
// FRB v2.12+ emits `use` lists in an order rustfmt 2024 edition rewrites
// (e.g. `{transform_result_dco, Lifetimeable, Lockable}` →
// `{Lifetimeable, Lockable, transform_result_dco}`). Run rustfmt against
// the generated file so committed output is fmt-clean and `cargo fmt --check`
// stays green in CI.
match std::process::Command::new("rustfmt")
.args(["--edition", "2024", "src/frb_generated.rs"])
.status()
{
Ok(s) if s.success() => {}
Ok(s) => println!("cargo:warning=rustfmt on src/frb_generated.rs exited {s}"),
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
println!(
"cargo:warning=rustfmt not on PATH — skipping post-FRB format. Install rustfmt via rustup to keep generated bridge sources fmt-clean."
);
}
Err(err) => println!("cargo:warning=failed to spawn rustfmt: {err}"),
}
// Patch the generated Dart entrypoint so the published package resolves
// its native library from its own installed location.
patch_published_loader();
// Rewrite FRB-generated handler.executeSync/handler.executeNormal calls
// into direct handler invocations. FRB 2.x emits these calls assuming
// `handler` is a BaseHandler field, but in service-API methods `handler`
// is a user-supplied function parameter (FutureOr<R> Function(T)) which
// does not expose those methods, so the generated Dart fails to compile.
// The rewrite is idempotent (marker-gated) and runs after every FRB
// invocation this build script performs — i.e. only under the opt-in
// gate above, so it never contends with alef's own regeneration.
fix_handler_executor_calls();
// FRB is not feature-aware: it emits a wire wrapper and a dispatch arm for
// every `pub fn` it can see, including ones behind `#[cfg(feature = ...)]`.
// Under a reduced feature set (Android: --no-default-features --features
// android-target) those functions are configured out and the glue fails with
// E0425. alef injects the gates during `alef generate`, but the FRB run above
// rewrites the file from scratch and drops them — same reversion the handler
// rewrite above exists to survive, so this re-applies for the same reason.
carry_frb_cfg_gates();
}
Ok(status) => panic!("flutter_rust_bridge_codegen generate failed (exit code: {status})"),
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
println!(
"cargo:warning=flutter_rust_bridge_codegen not on PATH — skipping codegen. Install via `cargo install flutter_rust_bridge_codegen --locked` to regenerate FRB artifacts at build time."
);
}
Err(err) => panic!("failed to spawn flutter_rust_bridge_codegen: {err}"),
}
}
{{ loader_patch }}
{{ cfg_gates_fn }}