feat: auto-resolve PackageKit native bridge; use hosted packagekit_dart

- Depend on the published `packagekit_dart ^0.3.2` instead of a path
  dependency.
- native_lib.dart: locate `libpackagekit_nc.so` from the package's own build
  or, for hosted installs, the `package:hooks` build-hook output under
  `.dart_tool/`, so the Linux backend works without `PK_NC_LIB`.
- docs: full per-command README reference (every option, default, value set);
  add `example/`; refresh CHANGELOG.
- chore: whitelist `Kitware`, `armv`, `armhf` for cspell.

Signed-off-by: Joel Winarske <joel.winarske@gmail.com>
This commit is contained in:
Joel Winarske
2026-06-15 20:29:39 -07:00
parent 091793029d
commit 0cc9bfe244
6 changed files with 494 additions and 128 deletions
+3
View File
@@ -21,6 +21,8 @@
"aarch",
"aotruntime",
"archs",
"armhf",
"armv",
"Brewfile",
"cmake",
"cpp",
@@ -35,6 +37,7 @@
"glibc",
"homescreen",
"icudtl",
"Kitware",
"libapp",
"libc",
"libflutter",
+7
View File
@@ -16,6 +16,13 @@ Port of `meta-flutter/workspace-automation` to a Dart CLI.
matrix.
- feat: `emb setup` — one-shot provision; emits `setup_env.sh`.
- feat: `emb env` — write `setup_env.sh`.
- feat: depend on the published `packagekit_dart` `^0.3.2` (hosted, not a path
dependency).
- feat: auto-resolve the PackageKit native bridge (`libpackagekit_nc.so`) — from
the package's own build or, for hosted installs, the `package:hooks` build-hook
output under `.dart_tool/` — so the Linux backend works without `PK_NC_LIB`.
- docs: full per-command reference (every option, default, and value set) in the
README; add `example/`.
# 0.0.1
+329 -106
View File
@@ -9,7 +9,7 @@ deployable app bundles for embedders such as
port of [meta-flutter/workspace-automation](https://github.com/meta-flutter/workspace-automation)
(`flutter_workspace.py` + `create_aot.py`).
It does the whole flow in a handful of commands:
The whole flow is a handful of commands:
```
deps → repos → Flutter SDK → engine → AOT → ivi-homescreen bundle
@@ -21,32 +21,44 @@ deps → repos → Flutter SDK → engine → AOT → ivi-homescreen bundle
[meta-flutter/flutter-engine](https://github.com/meta-flutter/flutter-engine)
releases (auto, keyed by the SDK's engine commit).
- **Cross-compile AOT** for `arm64` / `riscv64` from an `x86_64` host using the
engine's simulator `gen_snapshot` (no qemu the artifact is self-contained).
- **Self-describing packages**: a package declares its build in its manifest and
`emb build <package>` does the rest.
engine's simulator `gen_snapshot` no qemu (the artifact is self-contained).
- **Self-describing packages**: a package declares its build in a manifest and
`emb build <dir>` does the rest.
---
## Requirements
- Dart SDK ≥ 3.10.1 (the `emb` tool itself).
- Linux for host **dependency install** (PackageKit — dnf/apt/zypper). The
macOS/Windows package backends are stubbed in this build.
- `git`, `tar`, `curl` on PATH.
- **Dart SDK ≥ 3.10.1** (to run/build the `emb` tool itself).
- **Linux** for host **dependency install** (PackageKit — dnf/apt/zypper). The
macOS (Homebrew) and Windows (WinGet) backends are stubbed in this build; all
other commands are cross-platform.
- `git`, `tar`, `curl` on `PATH`.
- Cross-compiling to a device arch is supported **from an x86_64 host**.
---
## Install
```sh
# From this directory:
# From the package root:
dart pub get
dart pub global activate --source=path .
dart pub global activate --source=path . # puts `emb` on PATH
# Then (ensure the pub-global bin dir is on PATH):
emb --help
```
You can also run it without activating: `dart run bin/emb.dart <command>`.
Make sure the pub-global bin dir (`$PUB_CACHE/bin`, e.g. `~/.pub-cache/bin`) is
on `PATH`. You can also run without activating:
```sh
dart run bin/emb.dart <command> # from the package root
```
> The Linux backend loads `packagekit_dart`'s native bridge
> (`libpackagekit_nc.so`). `emb` locates it automatically — from the package's
> own build, or from the `package:hooks` build-hook output under
> `.dart_tool/`. Override with `PK_NC_LIB=/path/to/libpackagekit_nc.so`.
---
@@ -69,114 +81,324 @@ emb bundle --app-path ./app/my_app --arch arm64 --build
# ivi-homescreen --b=<workspace>/bundle/my_app-release-arm64
```
`emb setup` runs everything; you can also run the phases individually
(`emb deps`, `emb sync`, `emb flutter`, `emb engine`). The workspace root is
`$FLUTTER_WORKSPACE`, else the current directory, else `--workspace <dir>`.
`emb setup` runs every phase; you can also run them individually
(`emb deps`, `emb sync`, `emb flutter`, `emb engine`).
---
## Building bundles
## Workspace & path resolution
A bundle is the directory layout `ivi-homescreen` consumes:
Every command that touches the workspace resolves its root **in this order**:
1. `--workspace <dir>` (explicit flag), else
2. the `$FLUTTER_WORKSPACE` environment variable, else
3. the current working directory.
```
<workspace>/ # the resolved root
app/ # cloned source repositories
flutter/ # Flutter SDK
bundle/ # built bundles (default output)
setup_env.sh # generated environment script
.config/flutter_workspace/
flutter-engine/<commit>/... # downloaded + extracted engine SDKs
flutter-engine/bundle-<mode>-<arch>/ # staged engine halves (icudtl + .so)
```
---
## Command reference
```
emb <command> [arguments]
```
### Global options
| Option | Description |
|---|---|
| `-h`, `--help` | Print usage. Works at the top level and per command (`emb <cmd> --help`). |
| `-v`, `--version` | Print the CLI version. |
| `--[no-]verbose` | Noisy logging, including every shell command executed. |
`--workspace` (`-w`), shown on most commands, follows the resolution order
above. `--mode`/`--arch` defaults and value sets differ **per command** — see
each entry.
---
### `emb doctor`
Report host detection (os / arch / distro) and package-manager backend
availability. No options. Exit code is non-zero if the backend is unavailable.
```sh
emb doctor
```
---
### `emb setup`
One-shot provision: **deps → repos → Flutter SDK → engine**, then writes
`setup_env.sh`. Each phase is individually skippable.
| Option | Default | Description |
|---|---|---|
| `-c`, `--config <dir>` | `configs` | Legacy JSON config directory. |
| `-p`, `--packages <dir>` | — | Directory to discover self-describing `emb` manifests. |
| `-w`, `--workspace <dir>` | resolution order | Workspace root. |
| `--flutter-version <ref>` | `globals.json` `flutter_version` | Flutter version/tag/branch. |
| `--arch <arch>` | host arch | Engine arch to prefetch. |
| `-m`, `--mode <mode>` | `release` | Engine runtime modes to prefetch (repeatable): `release`, `profile`, `debug`. |
| `-y`, `--yes` | off | Skip the deps confirmation prompt (CI). |
| `--skip-deps` | off | Skip host dependency install. |
| `--skip-sync` | off | Skip repository sync. |
| `--skip-flutter` | off | Skip Flutter SDK install. |
| `--skip-engine` | off | Skip engine artifact fetch. |
```sh
emb setup --config ../configs --yes
emb setup --config ../configs --skip-deps --skip-sync # SDK + engine only
```
---
### `emb deps`
Coalesce host dependencies across all selected manifests, filter to what's
**missing** on this host, and install the union in **one** transaction.
| Option | Default | Description |
|---|---|---|
| `-c`, `--config <dir>` | `configs` | Legacy JSON config directory. **Repeatable.** |
| `-p`, `--packages <dir>` | — | Directory to discover self-describing `emb` manifests. **Repeatable.** |
| `--dry-run` | off | Resolve and print the install plan without changing the system. |
| `-y`, `--yes` | off | Skip the confirmation prompt (CI). |
```sh
emb deps --config ../configs --dry-run # plan only
emb deps --config ../configs --yes # install in one transaction
```
---
### `emb sync`
Clone/update source repositories into `<workspace>/app` (bounded concurrency).
| Option | Default | Description |
|---|---|---|
| `-c`, `--config <dir>` | `configs` | Legacy JSON config directory. **Repeatable.** |
| `-p`, `--packages <dir>` | — | Directory to discover self-describing `emb` manifests. |
| `--repos <file>` | — | A JSON file containing a bare array of repo entries. **Repeatable.** |
| `-w`, `--workspace <dir>` | resolution order | Workspace root. |
| `-j`, `--concurrency <n>` | `4` | Maximum concurrent git operations. |
```sh
emb sync --config ../configs -j 8
```
---
### `emb flutter`
Install the Flutter SDK into `<workspace>/flutter`.
| Option | Default | Description |
|---|---|---|
| `-w`, `--workspace <dir>` | resolution order | Workspace root. |
| `--flutter-version <ref>` | `globals.json` `flutter_version` | Version/tag/branch to check out. |
| `-c`, `--config <dir>` | `configs` | Directory to read `globals.json` from. |
| `--configure` | off | Run `flutter config` (desktop + custom devices) and `flutter doctor` after install. |
```sh
emb flutter --flutter-version 3.44.2 --configure
```
---
### `emb engine`
Fetch prebuilt Flutter engine artifacts (auto fetch-else-build). Modes you don't
fetch here are auto-fetched on demand by `emb bundle`/`emb build`.
| Option | Default | Description |
|---|---|---|
| `-w`, `--workspace <dir>` | resolution order | Workspace root. |
| `--commit <sha>` | `<workspace>/flutter/bin/internal/engine.version` | Engine commit to fetch. |
| `--arch <arch>` | host arch | Engine arch token (see [Architectures](#architectures--cross-compiling)). |
| `-m`, `--mode <mode>` | `release` | Runtime modes to fetch (repeatable): `release`, `profile`, `debug`. |
| `--clean` | off | Re-stage bundles even when already present. |
| `--check` | off | Only check prebuilt availability; do not download. |
```sh
emb engine --arch arm64 --mode release --mode profile
emb engine --arch riscv64 --check # is a prebuilt available?
```
---
### `emb aot`
Build the AOT image (`libapp.so`) for a Flutter app — the AOT primitive used by
`emb bundle --build`. **Debug is not AOT**, so only `release`/`profile` apply.
| Option | Default | Description |
|---|---|---|
| `-a`, `--app-path <dir>` | **mandatory** | Path to the Flutter application to build. |
| `-w`, `--workspace <dir>` | resolution order | Workspace root. |
| `-m`, `--mode <mode>` | `release` | Runtime modes to build (repeatable): `release`, `profile`. |
| `--arch <arch>` | host arch | Target arch for `gen_snapshot` (e.g. `arm64` for a Pi). |
| `--gen-snapshot <path>` | auto-resolved | Explicit `gen_snapshot` path (overrides resolution). |
| `--glibc-sysroot <dir>` | artifact's bundled `clang_x64/lib64` | Directory with `ld-linux` + libc to run `gen_snapshot` under. |
```sh
emb aot --app-path ./app/my_app --arch arm64 --mode release --mode profile
```
---
### `emb bundle`
Assemble an ivi-homescreen bundle from app + engine artifacts. With `--build` it
runs `emb aot` first; the engine SDK for the `(mode, arch)` is fetched
implicitly if not already cached.
| Option | Default | Description |
|---|---|---|
| `-a`, `--app-path <dir>` | **mandatory** | Path to the Flutter application. |
| `-w`, `--workspace <dir>` | resolution order | Workspace root. |
| `-m`, `--mode <mode>` | `release` | Single mode: `debug` (JIT, no AOT), `profile`, or `release` (AOT). |
| `--arch <arch>` | host arch | Target arch (e.g. `arm64`). |
| `-o`, `--output <dir>` (alias `--out`) | `<workspace>/bundle/<app>-<mode>-<arch>` | Output bundle directory — any path. |
| `--build` | off | Run `emb aot` first to (re)build `flutter_assets` + `libapp.so`. |
```sh
emb bundle --app-path ./app/my_app --arch arm64 --build # release
emb bundle --app-path ./app/my_app --arch arm64 --build --mode debug # JIT
emb bundle --app-path ./app/my_app --arch riscv64 --build --mode profile
emb bundle --app-path ./app/my_app --build # host
emb bundle --app-path ./app/my_app --arch arm64 --output /tmp/out # custom path
```
---
### `emb build`
Build a **self-describing** package — a directory with an `emb.yaml` (or an
`emb:` key in `pubspec.yaml`) — into bundles, using the manifest's `build:`
matrix. The package directory is a **positional argument** (mandatory; omitting
it prints a usage error).
```
emb build <package-dir> [options]
```
| Option | Default | Description |
|---|---|---|
| `<package-dir>` | **mandatory (positional)** | Directory containing `emb.yaml` or a `pubspec.yaml` with an `emb:` key. |
| `-w`, `--workspace <dir>` | resolution order | Workspace root. |
| `--arch <arch>` | manifest's `archs` | Override target arch(es). **Repeatable.** |
| `-m`, `--mode <mode>` | manifest's `modes` | Override mode(s): `debug`, `profile`, `release`. **Repeatable.** |
| `--no-build` | off | Assemble from existing artifacts; skip compiling. |
```sh
emb build ./app/my_app # full manifest matrix
emb build ./app/my_app --arch arm64 --mode release # override the matrix
emb build ./app/my_app --no-build # assemble only
```
#### Manifest (`emb.yaml`)
```yaml
id: my_app
type: app
build:
app_path: . # Flutter app dir, relative to this manifest (default ".")
archs: [arm64, x86_64] # target architectures (default: host)
modes: [release, debug] # default: [release]
output: bundles # optional output dir, relative to the workspace
deps: # optional host packages, by OS / distro
linux:
fedora: [pkg-config, freetype-devel]
ubuntu: [pkg-config, libfreetype-dev]
macos: [pkg-config, freetype]
windows: [Kitware.CMake]
```
---
### `emb env`
Write `setup_env.sh` (`PATH` for Flutter/Dart, `FLUTTER_WORKSPACE`, `PUB_CACHE`,
`XDG_CONFIG_HOME`, the engine version, …).
| Option | Default | Description |
|---|---|---|
| `-w`, `--workspace <dir>` | resolution order | Workspace root. |
| `-o`, `--output <path>` | `<workspace>/setup_env.sh` | Output file path. |
| `--print` | off | Print to stdout instead of writing a file. |
```sh
emb env # write <workspace>/setup_env.sh
emb env --print # preview on stdout
```
---
### `emb update`
Update the CLI itself (via `pub`). No options.
```sh
emb update
```
---
## Modes
| `--mode` | How it builds | Bundle contents | Valid in |
|-----------|---------------------------------------|-----------------|----------|
| `debug` | JIT — `flutter build bundle --debug` | `kernel_blob.bin`, **no** `libapp.so` | `bundle`, `build`, `engine`, `setup` |
| `profile` | AOT (`gen_snapshot`) | `libapp.so` (+ profile engine) | all |
| `release` | AOT (`gen_snapshot`) | `libapp.so` (+ release engine) | all |
`emb aot` only accepts `release`/`profile` (debug isn't AOT). Where a command
takes **multiple** modes (`aot`, `build`, `engine`, `setup`), repeat the flag:
`--mode release --mode profile`. `emb bundle` takes a **single** mode.
A bundle is the directory ivi-homescreen consumes:
```
<bundle>/
data/flutter_assets/ # app code + assets
data/icudtl.dat # from the engine
lib/libflutter_engine.so # from the engine (per mode)
lib/libapp.so # AOT image (profile/release only)
```
### Modes
| `--mode` | how it builds | bundle contents |
|-----------|---------------------------------------|------------------------------|
| `debug` | JIT — `flutter build bundle --debug` | `kernel_blob.bin`, **no** `libapp.so` |
| `profile` | AOT (`gen_snapshot`) | `libapp.so` (+ profile engine) |
| `release` | AOT (`gen_snapshot`) | `libapp.so` (+ release engine) |
Defaults: **`--mode release`**, **`--arch` = host arch**. The engine SDK for the
selected `(mode, arch)` is **fetched implicitly** if it isn't already cached, so
you don't have to run `emb engine` first.
```sh
emb bundle --app-path ./app/my_app --arch arm64 --build # release
emb bundle --app-path ./app/my_app --arch arm64 --build --mode debug # JIT
emb bundle --app-path ./app/my_app --arch riscv64 --build --mode profile
emb bundle --app-path ./app/my_app --build # host (x86_64)
emb bundle --app-path ./app/my_app --arch arm64 --out /tmp/bundle-out # custom path
```
### Manifest-driven (`emb build`)
Give a package an `emb` manifest (an `emb.yaml`, or an `emb:` key in
`pubspec.yaml`) so it builds itself with no flags:
```yaml
# emb.yaml
id: my_app
type: app
build:
app_path: . # Flutter app dir, relative to this manifest
archs: [arm64, x86_64] # target architectures (default: host)
modes: [release, debug]# default: [release]
output: bundles # optional output dir, relative to the workspace
deps: # optional host packages, by OS / distro
linux:
fedora: [pkg-config, freetype-devel]
ubuntu: [pkg-config, libfreetype-dev]
```
```sh
emb build ./app/my_app # builds the full arch × mode matrix
emb build ./app/my_app --arch arm64 --mode release # override the matrix
emb build ./app/my_app --no-build # assemble from existing artifacts only
lib/libapp.so # AOT image (profile/release only)
```
---
## Cross-compiling
## Architectures & cross-compiling
Cross-compile is **x86_64 host → target arch** (`arm64`, `riscv64`, …). The
meta-flutter engine SDK ships a host-x86_64 **simulator** `gen_snapshot`
(`linux_simarm64`) that emits target code and carries its own loader/libc in
`clang_x64/lib64`, so it runs on any host glibc — **no qemu required**. `emb`
selects and runs it automatically; `--gen-snapshot` / `--glibc-sysroot` override
if you need to. Building **on** an arm64 host targets arm64 natively.
`--arch` accepts machine names or Flutter tokens; they normalize to one of four
**engine arch tokens** used in paths and bundle names:
---
## Commands
| Command | What it does |
| You pass | Normalized token |
|---|---|
| `emb doctor` | Host detection (os/arch/distro) + package-backend availability. |
| `emb setup` | One-shot provision: deps → repos → Flutter SDK → engine, writes `setup_env.sh`. Phases are skippable (`--skip-deps`, …). |
| `emb deps` | Coalesce host deps across manifests, filter to what's missing, install in one transaction. `--dry-run`, `--yes`. |
| `emb sync` | Clone/update source repos into `<workspace>/app` (concurrent). |
| `emb flutter` | Clone/checkout the Flutter SDK into `<workspace>/flutter`. |
| `emb engine` | Fetch prebuilt engine artifacts for `--arch`/`--mode`. `--check` to test availability. |
| `emb aot` | Build `libapp.so` (profile/release) for an app — the AOT primitive. |
| `emb bundle` | Build (with `--build`) + assemble an ivi-homescreen bundle. |
| `emb build` | Build a self-describing package (manifest `build:`) into bundles. |
| `emb env` | Write `setup_env.sh` (`--print` to stdout). |
| `x64`, `x86_64`, `amd64` | `x86_64` |
| `arm64`, `aarch64` | `arm64` |
| `arm`, `armv7`, `armv7hf`, `armhf` | `armv7hf` |
| `riscv64` | `riscv64` |
Run `emb help <command>` for the full flag list.
---
## Workspace layout
```
<workspace>/ # $FLUTTER_WORKSPACE
app/ # cloned source repos
flutter/ # Flutter SDK
bundle/ # built bundles (default output)
setup_env.sh # generated env
.config/flutter_workspace/
flutter-engine/<commit>/... # downloaded + extracted engine SDKs
flutter-engine/bundle-<mode>-<arch>/ # staged engine halves
```
Cross-compile is **x86_64 host → target arch**. The meta-flutter engine SDK
ships a host-x86_64 **simulator** `gen_snapshot` that emits target code and
carries its own loader/libc in `clang_x64/lib64`, so it runs on any host glibc —
**no qemu required**. `emb` selects and runs it automatically; `--gen-snapshot`
/ `--glibc-sysroot` override if needed. Building **on** an arm64 host targets
arm64 natively.
---
@@ -184,13 +406,14 @@ Run `emb help <command>` for the full flag list.
```sh
dart pub get
dart analyze # very_good_analysis
dart analyze
dart test
```
> Note: the Linux PackageKit backend loads `packagekit_dart`'s native bridge.
> `emb` locates it automatically; set `PK_NC_LIB=/path/to/libpackagekit_nc.so`
> to override.
The macOS/Windows package backends live in `lib/src/pkg/_platform/` and are
excluded from the default (Linux) build — see that folder's `README.md`. Dart
has no OS-conditional dependencies, so they're wired in only on per-OS builds
that add `brew_dart` / `winget_dart`.
[license_badge]: https://img.shields.io/badge/license-Apache%202.0-blue.svg
[license_link]: https://opensource.org/licenses/Apache-2.0
+69
View File
@@ -0,0 +1,69 @@
# emb examples
`emb` is a CLI. Install it, then run the commands below. See the
[top-level README](../README.md) for the full command/flag reference.
```sh
# from the package root
dart pub get
dart pub global activate --source=path . # puts `emb` on PATH
# or run without activating: dart run bin/emb.dart <command>
```
## 1. Inspect the host
```sh
emb doctor
```
```
Host
os: linux
arch: x86_64 (flutter: x64, engine: x86_64)
host type: fedora
version: 43
Package backend
selected: packagekit
✓ packagekit is available
```
## 2. Provision a workspace
Installs host dependencies (one transaction), clones repos, installs the
Flutter SDK, and fetches the prebuilt engine — then writes `setup_env.sh`.
```sh
emb setup --config ../configs --yes
. ./setup_env.sh # Flutter/Dart on PATH, FLUTTER_WORKSPACE, …
```
Run phases individually if you prefer: `emb deps`, `emb sync`, `emb flutter`,
`emb engine`.
## 3. Build an ivi-homescreen bundle
```sh
# Release AOT bundle for a Raspberry Pi (arm64), cross-compiled from x86_64:
emb bundle --app-path ./app/my_app --arch arm64 --build
# Debug (JIT) bundle for the host:
emb bundle --app-path ./app/my_app --mode debug --build
```
The bundle directory (`data/flutter_assets`, `data/icudtl.dat`,
`lib/libflutter_engine.so`, and `lib/libapp.so` for profile/release) is what
ivi-homescreen runs:
```sh
ivi-homescreen --b=<workspace>/bundle/my_app-release-arm64
```
## 4. Self-describing packages
A package that ships an `emb.yaml` (or an `emb:` key in `pubspec.yaml`) builds
itself with no flags:
```sh
emb build ./app/my_app # builds the manifest's arch × mode matrix
```
+84 -16
View File
@@ -4,37 +4,105 @@ import 'dart:isolate';
import 'package:packagekit_dart/packagekit_dart.dart';
import 'package:path/path.dart' as p;
/// Best-effort: point `packagekit_dart`'s loader at the package's own prebuilt
/// Best-effort: point `packagekit_dart`'s loader at a prebuilt
/// `libpackagekit_nc.so` so the Linux backend works without the user having to
/// export `PK_NC_LIB`.
///
/// The package's loader is synchronous and cannot resolve its own location, so
/// we do it here (async) via [Isolate.resolvePackageUri] and hand the path to
/// [setPackagekitLibraryPath] before the first `PkClient` use. No-ops on
/// non-Linux hosts, when `PK_NC_LIB` is already set, or when the package can't
/// be resolved (e.g. fully AOT-compiled) — in which case the loader's own
/// we do it here (async) and hand the path to [setPackagekitLibraryPath] before
/// the first `PkClient` use. Two layouts are handled:
///
/// 1. **Path / vendored dependency** — the `.so` sits in the package itself
/// (`<pkg>/build/` or `<pkg>/native/_build/`), found via
/// [Isolate.resolvePackageUri].
/// 2. **Hosted (pub.dev) dependency** — the package dir in the pub cache is
/// read-only, so the `package:hooks` build hook compiles the `.so` into the
/// *consuming* project's `.dart_tool/hooks_runner/...` output instead. We
/// locate it there.
///
/// No-ops on non-Linux hosts, when `PK_NC_LIB` is already set, or when nothing
/// is found (e.g. a fully AOT-compiled binary) — in which case the loader's own
/// env/next-to-exe/system fallbacks still apply.
Future<void> configurePackageKitLibrary() async {
if (!Platform.isLinux) return;
if ((Platform.environment['PK_NC_LIB'] ?? '').isNotEmpty) return;
try {
// (1) The package's own prebuilt locations (path / vendored builds).
final uri = await Isolate.resolvePackageUri(
Uri.parse('package:packagekit_dart/packagekit_dart.dart'),
);
if (uri == null || !uri.isScheme('file')) return;
// <pkg>/lib/packagekit_dart.dart → <pkg>
final pkgRoot = p.dirname(p.dirname(uri.toFilePath()));
final candidates = [
p.join(pkgRoot, 'build', 'libpackagekit_nc.so'),
p.join(pkgRoot, 'native', '_build', 'libpackagekit_nc.so'),
];
for (final c in candidates) {
if (File(c).existsSync()) {
setPackagekitLibraryPath(c);
return;
if (uri != null && uri.isScheme('file')) {
// <pkg>/lib/packagekit_dart.dart → <pkg>
final pkgRoot = p.dirname(p.dirname(uri.toFilePath()));
for (final c in [
p.join(pkgRoot, 'build', 'libpackagekit_nc.so'),
p.join(pkgRoot, 'native', '_build', 'libpackagekit_nc.so'),
]) {
if (File(c).existsSync()) {
setPackagekitLibraryPath(c);
return;
}
}
}
// (2) The build hook's output under the consuming project's .dart_tool.
final hookBuilt = _findHookBuiltLibrary();
if (hookBuilt != null) {
setPackagekitLibraryPath(hookBuilt);
return;
}
} on Object {
// Best-effort only; fall through to the loader's other strategies.
}
}
/// Locate the `libpackagekit_nc.so` produced by the `package:hooks` build hook,
/// which lands under `.dart_tool/hooks_runner/shared/packagekit_dart/build/
/// <hash>/<cmake-build-dir>/libpackagekit_nc.so` in the consuming project.
///
/// Returns the most-recently-built match, or `null` if none is found.
String? _findHookBuiltLibrary() {
final dartTool = _locateDartTool();
if (dartTool == null) return null;
final buildRoot = Directory(
p.join(dartTool, 'hooks_runner', 'shared', 'packagekit_dart', 'build'),
);
if (!buildRoot.existsSync()) return null;
File? best;
// Layout is shallow: build/<hash>/<cmake-build-dir>/libpackagekit_nc.so.
for (final hashDir in buildRoot.listSync().whereType<Directory>()) {
for (final cmakeDir in hashDir.listSync().whereType<Directory>()) {
final so = File(p.join(cmakeDir.path, 'libpackagekit_nc.so'));
if (!so.existsSync()) continue;
if (best == null ||
so.statSync().modified.isAfter(best.statSync().modified)) {
best = so;
}
}
}
return best?.path;
}
/// Best-effort discovery of the consuming project's `.dart_tool` directory:
/// prefer the running package config, else walk up from the current directory.
String? _locateDartTool() {
final pc = Platform.packageConfig;
if (pc != null && pc.isNotEmpty) {
final path = pc.startsWith('file:') ? Uri.parse(pc).toFilePath() : pc;
// .../.dart_tool/package_config.json → .../.dart_tool
final dir = p.dirname(path);
if (p.basename(dir) == '.dart_tool' && Directory(dir).existsSync()) {
return dir;
}
}
var dir = Directory.current;
for (var i = 0; i < 8; i++) {
final dartTool = p.join(dir.path, '.dart_tool');
if (Directory(dartTool).existsSync()) return dartTool;
final parent = dir.parent;
if (parent.path == dir.path) break;
dir = parent;
}
return null;
}
+2 -6
View File
@@ -4,10 +4,7 @@ description: >-
ivi-homescreen bundles (cross-compiled AOT for arm64/riscv64).
version: 0.1.0
repository: https://github.com/toyota-connected/emb_cli
# Not published to pub.dev yet: depends on sibling packages via path.
# To publish, see README/CHANGELOG — the path deps must become hosted.
publish_to: none
issue_tracker: https://github.com/toyota-connected/emb_cli/issues
environment:
sdk: ">=3.10.1 <4.0.0"
@@ -21,8 +18,7 @@ dependencies:
# backends live in lib/src/pkg/_platform/ and are wired in per-OS builds —
# Dart has no OS-conditional dependencies, so depending on them here would
# force them to compile on every platform. See lib/src/pkg/_platform/README.
packagekit_dart:
path: ../app/packagekit_dart
packagekit_dart: ^0.3.2
path: ^1.9.0
pub_updater: ">=0.4.0 <0.6.0"
yaml: ^3.1.0