Files
Eric Seidel 34509fca3c refactor: split C API into Dart and engine surfaces (#350)
The C surface in `library/src/c_api` was a single bucket of `pub extern "C"`
functions covering both consumers — `package:shorebird_code_push` (via
ffigen) and Shorebird's Flutter engine fork (via direct C++ link). That
made it hard to reason about which symbols are stable ABI versus
internal, and ffigen was generating bindings for engine-only symbols
that no Dart code calls.

Split into two self-contained submodules and two cbindgen-generated
headers:

- `c_api::dart` → `include/updater_dart.h` (stable ABI; ffigen entry
  point). Defines `UpdateResult`, the `SHOREBIRD_*` status constants, and
  the five Dart-stable functions: `shorebird_current_boot_patch_number`,
  `shorebird_next_boot_patch_number`,
  `shorebird_check_for_downloadable_update`,
  `shorebird_update_with_result`, `shorebird_free_update_result`.
- `c_api::engine` → `include/updater_engine.h` (no stability guarantee).
  Defines `AppParameters`, `FileCallbacks`, and the engine-only functions:
  `shorebird_init`, `shorebird_should_auto_update`,
  `shorebird_validate_next_boot_patch`, `shorebird_next_boot_patch_path`,
  `shorebird_free_string`, `shorebird_start_update_thread`, and the
  `shorebird_report_launch_*` trio.

Each bucket file is self-contained: cbindgen scans only the file
(`with_src` in build.rs) and emits the items it defines plus the C
types they reference. There are no exclude/include lists in the
cbindgen configs — adding a function to one bucket automatically lands
it in the right header, and items in the other bucket cannot leak.

`mod.rs` shrinks to a thin layer of private helpers shared by both
buckets (`to_rust`, `allocate_c_string`, `free_c_string`, `log_on_error`)
plus the test module.

`include/updater.h` is removed; consumers include the specific header
for their use case. The Flutter engine's
`shell/common/shorebird/updater.cc` will be updated in a follow-up
engine-repo PR to include `updater_engine.h` directly.

Also drops two retired Dart-side symbols:

- `shorebird_update` (replaced by `shorebird_update_with_result` in the
  Dart 2.0 rewrite, Nov 2024).
- `shorebird_check_for_update` (replaced by
  `shorebird_check_for_downloadable_update` in the same rewrite).

The shorebird_code_push package's `_legacyFallback` was the only path
that still called `shorebird_update`. The package's `flutter: >=3.24.5`
constraint guarantees the engine has `shorebird_update_with_result`, so
the fallback was unreachable in practice. Removing it lets us drop the
ABI symbol.

Bumps shorebird_code_push to 2.0.7. Bindings regenerated via ffigen now
contain only the five Dart-stable symbols.

Follow-up engine PR will: include `updater_engine.h` instead of the
removed `updater.h`; clean up `android_exports.lst` (drop the ghost
`shorebird_active_path` and `shorebird_active_patch_number` exports,
drop `shorebird_check_for_update`).
2026-05-04 15:56:09 -07:00

58 lines
1.9 KiB
Rust

extern crate cbindgen;
use std::env;
use std::path::{Path, PathBuf};
// See:
// <https://github.com/eqrion/cbindgen/blob/master/docs.md#buildrs>
// <https://doc.rust-lang.org/cargo/reference/build-scripts.html>
// <https://doc.rust-lang.org/cargo/reference/build-script-examples.html>
fn main() {
let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
// Each header is generated from a single source file. cbindgen scans
// exactly that file and emits the `pub extern "C"` items it defines plus
// the C types they reference. Since each bucket file is self-contained
// (defines its own types), there is no cross-bucket leak and no need for
// exclusion lists in the cbindgen configs.
generate_header(
&crate_dir,
"cbindgen_dart.toml",
"src/c_api/dart.rs",
"include/updater_dart.h",
);
generate_header(
&crate_dir,
"cbindgen_engine.toml",
"src/c_api/engine.rs",
"include/updater_engine.h",
);
}
fn generate_header(crate_dir: &Path, config_name: &str, src_relative: &str, output_path: &str) {
let config_path = crate_dir.join(config_name);
let config = match cbindgen::Config::from_file(&config_path) {
Ok(config) => config,
Err(e) => {
println!("cargo:warning=Error loading {}: {e}", config_path.display());
return;
}
};
let src_path = crate_dir.join(src_relative);
let result = cbindgen::Builder::new()
.with_src(&src_path)
.with_config(config)
.generate();
match result {
Ok(contents) => {
contents.write_to_file(output_path);
}
Err(e) => {
println!("cargo:warning=Error generating {output_path}: {e}");
// We don't exit non-zero here so local rust-analyzer keeps
// working when cbindgen has an issue.
}
}
}