mirror of
https://github.com/toyota-connected/emb_cli.git
synced 2026-06-21 07:19:37 -07:00
feat(manifest): read cross: into EmbManifest.cross
EmbManifest.fromMap now parses the cross: block into a typed EmbManifest.cross (CrossTarget?), so a package's own emb.yaml can self-describe its cross toolchain/sysroot instead of needing a separate file. Parsing never throws — a malformed cross: (e.g. a multi-target block whose provider lives only under targets) yields null rather than breaking the manifest load for unrelated commands (deps/sync/doctor). For a multi-target block it surfaces the shared base; CrossProjectResolver still does per-target merging. Closes the 'Surfacing cross:' gap noted in examples/cross/README.md. Signed-off-by: Joel Winarske <joel.winarske@gmail.com>
This commit is contained in:
+10
-12
@@ -192,16 +192,14 @@ concerns (the `DepRule` shape), not toolchain identity, so they live outside
|
||||
|
||||
## Surfacing `cross:` on the manifest
|
||||
|
||||
`EmbManifest.fromMap` doesn't read `cross:` yet. One field wires it in:
|
||||
`EmbManifest.fromMap` reads the `cross:` block into a typed
|
||||
`EmbManifest.cross` (`CrossTarget?`), so a package's own `emb.yaml` can
|
||||
self-describe its cross toolchain/sysroot. It's null when there's no `cross:`
|
||||
block or it has no resolvable provider, and parsing never throws — a malformed
|
||||
`cross:` won't break unrelated commands (`deps`/`sync`/`doctor`). For a
|
||||
multi-target block (`cross.targets`) it's the shared base; `CrossProjectResolver`
|
||||
does per-target merging.
|
||||
|
||||
```dart
|
||||
// in EmbManifest
|
||||
final CrossTarget? cross;
|
||||
|
||||
// in fromMap(...)
|
||||
cross: map['cross'] is Map
|
||||
? CrossTarget.fromMap(map['cross'] as Map<dynamic, dynamic>)
|
||||
: null,
|
||||
```
|
||||
|
||||
Until then, the test reads `cross:` directly from the YAML.
|
||||
The example test above parses each `cross:` block directly with
|
||||
`CrossTarget.fromMap` to assert per-field; consumers should prefer
|
||||
`EmbManifest.cross`.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:emb_cli/src/cross/cross_target.dart';
|
||||
import 'package:emb_cli/src/manifest/build_config.dart';
|
||||
import 'package:emb_cli/src/manifest/host_deps.dart';
|
||||
import 'package:emb_cli/src/manifest/source_repo.dart';
|
||||
@@ -22,6 +23,7 @@ class EmbManifest {
|
||||
required this.src,
|
||||
required this.deps,
|
||||
this.build,
|
||||
this.cross,
|
||||
this.flutterVersion,
|
||||
this.sourcePath,
|
||||
this.raw = const {},
|
||||
@@ -53,6 +55,7 @@ class EmbManifest {
|
||||
build: map['build'] is Map
|
||||
? BuildConfig.fromMap(map['build'] as Map<dynamic, dynamic>)
|
||||
: null,
|
||||
cross: _parseCross(map),
|
||||
flutterVersion: map['flutter_version'] as String?,
|
||||
sourcePath: sourcePath,
|
||||
raw: map,
|
||||
@@ -87,6 +90,13 @@ class EmbManifest {
|
||||
/// Build configuration (`build:`), when this package is buildable.
|
||||
final BuildConfig? build;
|
||||
|
||||
/// Parsed `cross:` block, when present and well-formed — so a package's own
|
||||
/// `emb.yaml` can self-describe its cross toolchain/sysroot. For a
|
||||
/// multi-target block (`cross.targets`) this is the shared base (provider +
|
||||
/// shared fields); per-target merging is done by `CrossProjectResolver`.
|
||||
/// Null when there is no `cross:` block, or it has no resolvable provider.
|
||||
final CrossTarget? cross;
|
||||
|
||||
/// Pinned Flutter version, when declared.
|
||||
final String? flutterVersion;
|
||||
|
||||
@@ -97,6 +107,22 @@ class EmbManifest {
|
||||
/// gclient_config, qemu/docker/remote platform blocks).
|
||||
final Map<String, dynamic> raw;
|
||||
|
||||
/// Parse the `cross:` block into a [CrossTarget], or null when absent or
|
||||
/// without a resolvable provider (e.g. a `cross.targets` block whose provider
|
||||
/// lives only under a target). Never throws — a malformed `cross:` must not
|
||||
/// break the manifest load for unrelated commands (deps, sync, doctor, …).
|
||||
static CrossTarget? _parseCross(Map<String, dynamic> map) {
|
||||
final block = map['cross'];
|
||||
if (block is! Map) return null;
|
||||
try {
|
||||
return CrossTarget.fromMap(Map<dynamic, dynamic>.from(block));
|
||||
// fromMap throws ArgumentError on a missing/unknown provider token.
|
||||
// ignore: avoid_catching_errors
|
||||
} on ArgumentError {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static HostDeps _parseDeps(Map<String, dynamic> map) {
|
||||
// New structured schema takes precedence when present.
|
||||
final structured = map['deps'];
|
||||
|
||||
@@ -152,4 +152,48 @@ emb:
|
||||
expect(loadOf(1), isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('cross: block', () {
|
||||
test('a well-formed cross: block parses into EmbManifest.cross', () {
|
||||
final m = EmbManifest.fromMap(<String, dynamic>{
|
||||
'id': 'ivi-homescreen',
|
||||
'cross': {
|
||||
'provider': 'yocto-sdk',
|
||||
'triple': 'aarch64-agl-linux',
|
||||
'host_build_tools': true,
|
||||
},
|
||||
});
|
||||
expect(m.cross, isNotNull);
|
||||
expect(m.cross!.provider.token, 'yocto-sdk');
|
||||
expect(m.cross!.triple, 'aarch64-agl-linux');
|
||||
expect(m.cross!.hostTools, isTrue);
|
||||
});
|
||||
|
||||
test('a multi-target block exposes the shared base (provider)', () {
|
||||
final m = EmbManifest.fromMap(<String, dynamic>{
|
||||
'id': 'rpi',
|
||||
'cross': {
|
||||
'provider': 'arm-gnu',
|
||||
'targets': {
|
||||
'rpi5': {'image_url': 'https://example/x.img.xz'},
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(m.cross?.provider.token, 'arm-gnu');
|
||||
});
|
||||
|
||||
test('no cross: block → null', () {
|
||||
expect(EmbManifest.fromMap(<String, dynamic>{'id': 'x'}).cross, isNull);
|
||||
});
|
||||
|
||||
test('a malformed cross: (no provider) → null, does not throw', () {
|
||||
expect(
|
||||
EmbManifest.fromMap(<String, dynamic>{
|
||||
'id': 'x',
|
||||
'cross': {'triple': 'aarch64-agl-linux'},
|
||||
}).cross,
|
||||
isNull,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user