mirror of
https://github.com/immich-app/immich
synced 2026-08-22 13:13:05 +00:00
refactor: rename ffi crate to immich_core_ffi
This commit is contained in:
parent
bca556b6c5
commit
7d27eeceb6
15 changed files with 29 additions and 27 deletions
2
native/Cargo.lock
generated
2
native/Cargo.lock
generated
|
|
@ -235,7 +235,7 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "immich_core_dart"
|
||||
name = "immich_core_ffi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cbindgen",
|
||||
|
|
|
|||
|
|
@ -2,14 +2,15 @@
|
|||
resolver = "2"
|
||||
members = [
|
||||
"crates/immich_core",
|
||||
"crates/immich_core_dart",
|
||||
"crates/immich_core_ffi",
|
||||
"crates/immich_core_napi",
|
||||
]
|
||||
|
||||
# shared logic lives in immich_core (no binding deps). each binding crate is a
|
||||
# thin wrapper that picks its own crate-type: immich_core_dart -> cdylib/staticlib
|
||||
# for dart:ffi (mobile), immich_core_napi -> cdylib (.node) for the node server.
|
||||
# capabilities (hashing, exif, ...) are cargo features on immich_core so both
|
||||
# thin wrapper that picks its own crate-type: immich_core_ffi -> cdylib/staticlib,
|
||||
# the C ABI consumed by dart (ffigen), swift (C interop) and kotlin (JNI shim);
|
||||
# immich_core_napi -> cdylib (.node) for the node server.
|
||||
# capabilities (hashing, image, ...) are cargo features on immich_core so both
|
||||
# bindings opt into the same set. crate-type can't be feature-gated, which is why
|
||||
# the bindings are separate crates rather than one crate with feature flags.
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,9 @@ round-trips but is not wired into the server yet.
|
|||
## Layout
|
||||
```
|
||||
crates/
|
||||
immich_core pure logic, no binding deps. capabilities = cargo features (hashing).
|
||||
immich_core_dart cdylib/staticlib + cbindgen header for dart:ffi (mobile)
|
||||
immich_core pure logic, no binding deps. capabilities = cargo features (hashing, image).
|
||||
immich_core_ffi the hand-written C ABI + cbindgen header — consumed by dart (ffigen),
|
||||
swift (native C interop) and kotlin (JNI shim)
|
||||
immich_core_napi cdylib (.node) via napi-rs (server, unwired)
|
||||
immich_native_core/ the Flutter package mobile depends on. build hook + ffigen @Native bindings.
|
||||
smoke/ host dart + node roundtrip scripts (no device)
|
||||
|
|
@ -23,13 +24,13 @@ Bindings are separate crates (Cargo can't gate `crate-type` by feature).
|
|||
|
||||
## How the native lib is built (Flutter native assets — no prebuilt, no CI)
|
||||
`immich_native_core/hook/build.dart` (`native_toolchain_rust`) compiles
|
||||
`crates/immich_core_dart` **from source on every app build** via rustup and bundles
|
||||
`crates/immich_core_ffi` **from source on every app build** via rustup and bundles
|
||||
it as a Flutter *code asset*. The Dart side uses ffigen `@Native` externals bound to
|
||||
that asset — no `DynamicLibrary`, no prebuilt artifacts, no fetch/publish/separate-repo.
|
||||
|
||||
Native assets is on by default on Flutter stable (3.38+), so a stock `flutter build`
|
||||
runs the hook. Each builder needs **rustup** (the hook auto-installs the pinned
|
||||
toolchain + targets from `crates/immich_core_dart/rust-toolchain.toml`).
|
||||
toolchain + targets from `crates/immich_core_ffi/rust-toolchain.toml`).
|
||||
|
||||
## Dev commands (mise)
|
||||
```
|
||||
|
|
@ -43,7 +44,7 @@ mise run smoke Rust tests + host dart:ffi + host napi roundtrips
|
|||
|
||||
## Add a capability (end to end)
|
||||
1. add the logic to `crates/immich_core` (behind a cargo feature if it pulls a dep).
|
||||
2. expose a C entry in `crates/immich_core_dart/src/lib.rs` — `#[no_mangle] pub extern "C"`,
|
||||
2. expose a C entry in `crates/immich_core_ffi/src/lib.rs` — `#[no_mangle] pub extern "C"`,
|
||||
wrap the body in `guard(...)` (panic at the boundary → null, never unwind into the host),
|
||||
validate pointers, return Rust-owned memory the caller frees via `immich_core_free_string`.
|
||||
3. `mise run codegen` — regenerates the committed cbindgen header + ffigen `@Native` bindings.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! immich_native_core — shared Rust core for the immich server (napi) and mobile (dart:ffi).
|
||||
//!
|
||||
//! Pure logic only: no binding or platform deps live here. Each binding crate
|
||||
//! (`immich_core_dart`, `immich_core_napi`) is a thin wrapper. Capabilities are
|
||||
//! (`immich_core_ffi`, `immich_core_napi`) is a thin wrapper. Capabilities are
|
||||
//! cargo features (`hashing`, `image`, ...) so every binding opts into the same set.
|
||||
|
||||
#[cfg(feature = "hashing")]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "immich_core_dart"
|
||||
name = "immich_core_ffi"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
@ -30,7 +30,7 @@ Flutter bundles it as a code asset; the `@Native` bindings resolve against it.
|
|||
|
||||
## Layout
|
||||
|
||||
- `hook/build.dart` — builds `../crates/immich_core_dart` via `native_toolchain_rust`.
|
||||
- `hook/build.dart` — builds `../crates/immich_core_ffi` via `native_toolchain_rust`.
|
||||
- `lib/immich_native_core.dart` — barrel, the public API.
|
||||
- `lib/src/{core,hashing,image}.dart` — thin wrappers, one file per Rust module.
|
||||
- `lib/src/ffi/bindings.g.dart` — ffigen `@Native` output (committed; do not edit).
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ description: 'FFI bindings to immich_native_core — generated, do not edit.'
|
|||
output: 'lib/src/ffi/bindings.g.dart'
|
||||
headers:
|
||||
entry-points:
|
||||
- '../crates/immich_core_dart/include/immich_core.h'
|
||||
- '../crates/immich_core_ffi/include/immich_core.h'
|
||||
include-directives:
|
||||
- '**/immich_core.h'
|
||||
functions:
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import 'package:code_assets/code_assets.dart';
|
|||
import 'package:hooks/hooks.dart';
|
||||
import 'package:native_toolchain_rust/native_toolchain_rust.dart';
|
||||
|
||||
// Builds crates/immich_core_dart from source on every app build and bundles it as
|
||||
// Builds crates/immich_core_ffi from source on every app build and bundles it as
|
||||
// a code asset. assetName must match the ffigen output (its package URI is the
|
||||
// @Native DefaultAsset id). The crate is a sibling, so point cratePath at it.
|
||||
void main(List<String> args) async {
|
||||
|
|
@ -12,7 +12,7 @@ void main(List<String> args) async {
|
|||
await _ensureRustTarget(input);
|
||||
await RustBuilder(
|
||||
assetName: 'src/ffi/bindings.g.dart',
|
||||
cratePath: '../crates/immich_core_dart',
|
||||
cratePath: '../crates/immich_core_ffi',
|
||||
).run(input: input, output: output);
|
||||
});
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ Future<void> _ensureRustTarget(BuildInput input) async {
|
|||
};
|
||||
if (triple == null) return;
|
||||
final crate = input.packageRoot
|
||||
.resolve('../crates/immich_core_dart/')
|
||||
.resolve('../crates/immich_core_ffi/')
|
||||
.toFilePath();
|
||||
// best effort — if rustup itself is broken the build below reports it properly
|
||||
await Process.run('rustup', [
|
||||
|
|
|
|||
|
|
@ -22,17 +22,17 @@ run = "cargo clippy --workspace --all-targets -- -D warnings"
|
|||
alias = "codegen"
|
||||
description = "Generate the C header (cbindgen) + Dart @Native bindings (ffigen)"
|
||||
sources = [
|
||||
"crates/immich_core_dart/src/lib.rs",
|
||||
"crates/immich_core_dart/cbindgen.toml",
|
||||
"crates/immich_core_ffi/src/lib.rs",
|
||||
"crates/immich_core_ffi/cbindgen.toml",
|
||||
"immich_native_core/ffigen.yaml",
|
||||
]
|
||||
outputs = [
|
||||
"crates/immich_core_dart/include/immich_core.h",
|
||||
"immich_native_core/lib/immich_native_core_bindings_generated.dart",
|
||||
"crates/immich_core_ffi/include/immich_core.h",
|
||||
"immich_native_core/lib/src/ffi/bindings.g.dart",
|
||||
]
|
||||
run = [
|
||||
"cargo build -p immich_core_dart",
|
||||
"cd immich_native_core && dart run ffigen --config ffigen.yaml && dart format lib/immich_native_core_bindings_generated.dart",
|
||||
"cargo build -p immich_core_ffi",
|
||||
"cd immich_native_core && dart run ffigen --config ffigen.yaml && dart format lib/src/ffi/bindings.g.dart",
|
||||
]
|
||||
|
||||
# Host FFI roundtrip through the real build hook — no device. Builds the Rust crate
|
||||
|
|
@ -44,7 +44,7 @@ run = "flutter test"
|
|||
|
||||
[tasks."build:dart"]
|
||||
description = "Build the dart:ffi cdylib directly (host, raw cargo)"
|
||||
run = "cargo build -p immich_core_dart"
|
||||
run = "cargo build -p immich_core_ffi"
|
||||
|
||||
[tasks."build:napi"]
|
||||
description = "Build the node addon + stage a .node for require() (server, unwired)"
|
||||
|
|
@ -56,7 +56,7 @@ run = [
|
|||
[tasks."smoke:dart"]
|
||||
description = "Host dart:ffi ABI roundtrip (raw DynamicLibrary on the cdylib)"
|
||||
depends = ["build:dart"]
|
||||
run = "dart run smoke/dart_smoke.dart target/debug/libimmich_core_dart.dylib"
|
||||
run = "dart run smoke/dart_smoke.dart target/debug/libimmich_core_ffi.dylib"
|
||||
|
||||
[tasks."smoke:node"]
|
||||
description = "Host napi roundtrip"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Mobile-side roundtrip: open the dart:ffi cdylib and call into the shared core.
|
||||
// Standalone script (no package:ffi dep) — reads the returned C string by hand.
|
||||
//
|
||||
// dart run smoke/dart_smoke.dart target/debug/libimmich_core_dart.dylib
|
||||
// dart run smoke/dart_smoke.dart target/debug/libimmich_core_ffi.dylib
|
||||
|
||||
import 'dart:ffi';
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ String _readCString(Pointer<Uint8> p) {
|
|||
}
|
||||
|
||||
void main(List<String> args) {
|
||||
final libPath = args.isNotEmpty ? args.first : 'target/debug/libimmich_core_dart.dylib';
|
||||
final libPath = args.isNotEmpty ? args.first : 'target/debug/libimmich_core_ffi.dylib';
|
||||
final lib = DynamicLibrary.open(libPath);
|
||||
|
||||
final version = lib.lookupFunction<_VersionNative, _VersionNative>('immich_core_version');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue