feat(cross): capture yocto-recipe facts in emb.lock

Completes emb.lock coverage across all three cross providers (after
arm-gnu and yocto-sdk). The yocto-recipe provider locates a live OE
build tree and downloads nothing, so there is no installer/artifact to
sha; instead it pins the resolved recipe workdir version (the newest
version dir under the recipe, e.g. "1.0-r0"), which is
machine-independent and drifts when the OE tree is rebuilt to a newer
recipe. Combined with the input keys, this catches a changed
toolchain/sysroot under the same manifest.

No model or command changes — reuses the LockedTarget machinery and the
--update-lock / --no-verify flags; CrossResolveResult.lockEntry is now
populated for every provider.

Signed-off-by: Joel Winarske <joel.winarske@gmail.com>
This commit is contained in:
Joel Winarske
2026-06-18 12:36:22 -07:00
parent 54f1e1affa
commit 22de8dbfab
2 changed files with 32 additions and 1 deletions
+12 -1
View File
@@ -4,6 +4,7 @@ import 'package:emb_cli/src/cross/cross_keys.dart';
import 'package:emb_cli/src/cross/cross_profile.dart';
import 'package:emb_cli/src/cross/cross_provider.dart';
import 'package:emb_cli/src/cross/cross_target.dart';
import 'package:emb_cli/src/cross/emb_lock.dart';
import 'package:emb_cli/src/cross/toolchain_emitter.dart';
import 'package:emb_cli/src/host/host_info.dart';
import 'package:emb_cli/src/workspace/workspace.dart';
@@ -133,7 +134,17 @@ class YoctoRecipeCrossProvider implements CrossProvider {
cmakeToolchainFile: cmakeTc,
mesonCrossFile: mesonCross,
);
return CrossResolveResult.ok(profile);
final lockEntry = LockedTarget(
provider: name,
triple: triple,
// The located recipe workdir version (e.g. "1.0-r0"). Nothing is fetched
// here, so there is no artifact to sha; this machine-independent version
// catches an OE tree rebuilt to a newer recipe.
toolchainVersion: p.basename(versionDir.path),
sysrootKey: sysrootKey(target),
buildKey: buildKey(target),
);
return CrossResolveResult.ok(profile, lockEntry: lockEntry);
}
Directory? _newestChild(Directory parent) {
@@ -75,6 +75,26 @@ void main() {
expect(pf.cFlags, contains('-march=armv8-a+crc+crypto'));
});
test('captures a lock entry pinning the located recipe version', () async {
final build = fixtureBuild();
final r = await YoctoRecipeCrossProvider(
makeTarget(build.path),
workspace: Workspace(tmp),
host: _host,
).resolve();
expect(r.ok, isTrue, reason: r.message);
final lock = r.lockEntry!;
expect(lock.provider, 'yocto-recipe');
expect(lock.triple, 'aarch64-poky-linux');
// The newest recipe workdir version — drifts if the OE tree is rebuilt.
expect(lock.toolchainVersion, '1.0');
expect(lock.sysrootKey, isNotEmpty);
expect(lock.buildKey, isNotEmpty);
// Nothing is fetched, so there is no artifact to sha.
expect(lock.artifacts, isEmpty);
});
test('unavailable without yocto_build / machine_tuple', () async {
final r = await YoctoRecipeCrossProvider(
CrossTarget.fromMap({'provider': 'yocto-recipe'}),