@typecad/framework-zephyr 1.0.0-alpha.10 → 1.0.0-alpha.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,40 +1,40 @@
1
1
  declare const _default: {
2
+ hal: {
3
+ [x: string]: {
4
+ supported: boolean;
5
+ ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
6
+ partialCoverage: boolean;
7
+ unsupportedReason?: string | undefined;
8
+ };
9
+ raw?: unknown;
10
+ } & {
11
+ [k: string]: {
12
+ supported: boolean;
13
+ ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
14
+ partialCoverage: boolean;
15
+ unsupportedReason?: string | undefined;
16
+ };
17
+ };
18
+ packageName: string;
2
19
  schemaVersion: 1;
3
20
  frameworkId: string;
4
- packageName: string;
5
21
  canonical: boolean;
6
22
  displayName: string;
7
23
  description: string;
8
24
  implementationMode: "from-scratch" | "extends-canonical" | "extends-other";
9
25
  entrypoint: {
26
+ sourceExtension: "cpp" | "ino" | "cc" | "h";
10
27
  entrypointFunctionName: string;
11
28
  requiresLoopFunction: boolean;
12
- sourceExtension: "ino" | "cc" | "cpp" | "h";
13
29
  generateHeaderFile: boolean;
14
30
  overrideBaseName?: string | undefined;
15
31
  outputSubdirectory?: string | undefined;
16
32
  customBridgeShim?: string | undefined;
17
33
  };
18
34
  profile: {
19
- targets: string[];
20
35
  forcedIncludes: string[];
21
36
  symbolAliases: Record<string, string>;
22
- };
23
- hal: {
24
- [x: string]: {
25
- supported: boolean;
26
- ops: Record<string, "supported" | "stub" | "unsupported" | "probe-inconclusive" | "polyfill">;
27
- partialCoverage: boolean;
28
- unsupportedReason?: string | undefined;
29
- };
30
- raw?: unknown;
31
- } & {
32
- [k: string]: {
33
- supported: boolean;
34
- ops: Record<string, "supported" | "stub" | "unsupported" | "probe-inconclusive" | "polyfill">;
35
- partialCoverage: boolean;
36
- unsupportedReason?: string | undefined;
37
- };
37
+ targets: string[];
38
38
  };
39
39
  polyfills: {
40
40
  emitted: {
@@ -58,12 +58,12 @@ declare const _default: {
58
58
  reexportedFrom?: string | undefined;
59
59
  };
60
60
  typeEmission: {
61
- normalizeCppType: boolean;
62
- mathHeader: "none" | "<math.h>" | "<Arduino.h>";
61
+ needsIostream: boolean;
63
62
  needsStdString: boolean;
64
63
  needsStdVector: boolean;
65
- needsIostream: boolean;
66
64
  needsStdFunction: boolean;
65
+ mathHeader: "none" | "<math.h>" | "<Arduino.h>";
66
+ normalizeCppType: boolean;
67
67
  stdlibSupport: {
68
68
  hasVector: boolean;
69
69
  hasString: boolean;
@@ -79,6 +79,12 @@ declare const _default: {
79
79
  hardwareTestGroups: string[];
80
80
  halResolutionTests: string[];
81
81
  };
82
+ doctor?: {
83
+ available: boolean;
84
+ } | undefined;
85
+ licenses?: {
86
+ available: boolean;
87
+ } | undefined;
82
88
  basedOn?: string | undefined;
83
89
  inheritsStrategyId?: string | undefined;
84
90
  libraryResolution?: {
@@ -88,14 +94,5 @@ declare const _default: {
88
94
  tryGenerateLibDecl: boolean;
89
95
  reexportedFrom?: string | undefined;
90
96
  } | undefined;
91
- doctor?: {
92
- available: boolean;
93
- } | undefined;
94
- licenses?: {
95
- available: boolean;
96
- } | undefined;
97
- compat?: {
98
- zephyr?: string | undefined;
99
- } | undefined;
100
97
  };
101
98
  export default _default;
@@ -522,15 +522,4 @@ export default defineFrameworkManifest({
522
522
  'uart', 'wdt', 'worker',
523
523
  ],
524
524
  },
525
- // Declared compatibility range for the installed Zephyr RTOS. The framework's
526
- // build-time version check (toolchain/compat.ts) reads this and fails fast
527
- // with a clear message when the user's Zephyr is outside the range, instead
528
- // of letting west/CMake surface a cryptic board-target error (the class of
529
- // breakage behind the HWMv2 qualifier requirement in Zephyr 4.3+).
530
- compat: {
531
- zephyr: '>=4.3 <5.0',
532
- },
533
- // `cuttlefish doctor` prints the detected Zephyr version + compat result and
534
- // previews how the configured board target resolves for that version.
535
- doctor: { available: true },
536
525
  });
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export { ZephyrStrategy as FrameworkStrategy } from './strategy.js';
2
2
  export { ZephyrStrategy } from './strategy.js';
3
3
  export { Toolchain } from './toolchain/index.js';
4
- export { runDoctor as doctor } from './doctor.js';
5
4
  export { chipForTarget, setActiveChip, getActiveChip, XIAO_BLE, } from './chips/index.js';
6
5
  export type { ZephyrChipDescriptor, ZephyrGpioDtSpec, } from './chips/types.js';
package/dist/index.js CHANGED
@@ -8,10 +8,5 @@
8
8
  export { ZephyrStrategy as FrameworkStrategy } from './strategy.js';
9
9
  export { ZephyrStrategy } from './strategy.js';
10
10
  export { Toolchain } from './toolchain/index.js';
11
- // `cuttlefish doctor` — verify the installed Zephyr is reachable + inside the
12
- // declared compat range, and preview board-target normalization. Re-exported
13
- // under the dispatcher-facing alias `doctor` so the loader picks it up as
14
- // mod.doctor (see framework-package.ts).
15
- export { runDoctor as doctor } from './doctor.js';
16
11
  // Chip descriptor registry (for downstream tooling / additional boards).
17
12
  export { chipForTarget, setActiveChip, getActiveChip, XIAO_BLE, } from './chips/index.js';
@@ -28,7 +28,6 @@ import { writeDebugConfig, resolveDebugLocations } from './debug-config.js';
28
28
  import { ZephyrStrategy } from '../strategy.js';
29
29
  import { generateOverlay } from '../dt-config/overlay.js';
30
30
  import { chipForTarget } from '../chips/index.js';
31
- import { detectZephyrVersion, checkZephyrCompat, resolveBoardTarget } from './compat.js';
32
31
  import { DEFAULT_ZEPHYR_DISPLAY_PROFILE } from '../display/profiles.js';
33
32
  /** Default board target — the framework's MVP canonical board. */
34
33
  const DEFAULT_BOARD = 'xiao_ble';
@@ -198,23 +197,7 @@ export const Toolchain = {
198
197
  },
199
198
  compile(o) {
200
199
  const projectRoot = projectRootFromOptions(o);
201
- const rawBoard = targetFromOptions(o);
202
- // Fail fast on an incompatible Zephyr (clear message vs. a cryptic west/
203
- // CMake board error), then normalize the board target for the installed
204
- // version — Zephyr 4.3+ rejects bare multi-core board names, so a stale
205
- // config (esp32s3_devkitc) is rewritten to the qualified form
206
- // (esp32s3_devkitc/esp32s3/procpu). See toolchain/compat.ts.
207
- const zephyrVersion = detectZephyrVersion();
208
- const compat = checkZephyrCompat(zephyrVersion);
209
- if (compat.status === 'out-of-range') {
210
- throw new Error(`Zephyr ${zephyrVersion} is outside the supported range (${compat.range}) for @typecad/framework-zephyr. ` +
211
- `Set ZEPHYR_BASE to a compatible Zephyr checkout, or install one via '@typecad/zephyr-installer'.`);
212
- }
213
- if (compat.status === 'undetectable') {
214
- console.warn(`! Could not detect the installed Zephyr version (is ZEPHYR_BASE set?). ` +
215
- `Skipping compat check; declared range is ${compat.range}.`);
216
- }
217
- const board = resolveBoardTarget(rawBoard, zephyrVersion);
200
+ const board = targetFromOptions(o);
218
201
  const debugMode = new ZephyrStrategy().debugMode(board);
219
202
  const isGdbDebug = o.debug === true && debugMode === 'gdb';
220
203
  const zc = o.zephyrConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typecad/framework-zephyr",
3
- "version": "1.0.0-alpha.10",
3
+ "version": "1.0.0-alpha.9",
4
4
  "description": "TypeCAD framework package for the Zephyr RTOS — west/CMake build, devicetree-driven GPIO",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -38,11 +38,11 @@
38
38
  "test:hw:mqtt": "cd ../../tests/hardware && npm exec -- cuttlefish-test mqtt-client.test.ts"
39
39
  },
40
40
  "dependencies": {
41
- "@typecad/cuttlefish": "1.0.0-alpha.10"
41
+ "@typecad/cuttlefish": "1.0.0-alpha.9"
42
42
  },
43
43
  "devDependencies": {
44
- "@typecad/expect": "1.0.0-alpha.10",
45
- "@typecad/board-xiao-nrf52840": "1.0.0-alpha.10",
44
+ "@typecad/expect": "1.0.0-alpha.9",
45
+ "@typecad/board-xiao-nrf52840": "1.0.0-alpha.9",
46
46
  "typescript": "^5.7.3"
47
47
  },
48
48
  "license": "MIT",
@@ -542,17 +542,4 @@ export default defineFrameworkManifest({
542
542
  'uart', 'wdt', 'worker',
543
543
  ],
544
544
  },
545
-
546
- // Declared compatibility range for the installed Zephyr RTOS. The framework's
547
- // build-time version check (toolchain/compat.ts) reads this and fails fast
548
- // with a clear message when the user's Zephyr is outside the range, instead
549
- // of letting west/CMake surface a cryptic board-target error (the class of
550
- // breakage behind the HWMv2 qualifier requirement in Zephyr 4.3+).
551
- compat: {
552
- zephyr: '>=4.3 <5.0',
553
- },
554
-
555
- // `cuttlefish doctor` prints the detected Zephyr version + compat result and
556
- // previews how the configured board target resolves for that version.
557
- doctor: { available: true },
558
545
  });
package/src/index.ts CHANGED
@@ -10,12 +10,6 @@ export { ZephyrStrategy as FrameworkStrategy } from './strategy.js';
10
10
  export { ZephyrStrategy } from './strategy.js';
11
11
  export { Toolchain } from './toolchain/index.js';
12
12
 
13
- // `cuttlefish doctor` — verify the installed Zephyr is reachable + inside the
14
- // declared compat range, and preview board-target normalization. Re-exported
15
- // under the dispatcher-facing alias `doctor` so the loader picks it up as
16
- // mod.doctor (see framework-package.ts).
17
- export { runDoctor as doctor } from './doctor.js';
18
-
19
13
  // Chip descriptor registry (for downstream tooling / additional boards).
20
14
  export {
21
15
  chipForTarget,
@@ -30,7 +30,6 @@ import { writeDebugConfig, resolveDebugLocations } from './debug-config.js';
30
30
  import { ZephyrStrategy } from '../strategy.js';
31
31
  import { generateOverlay, type DisplayWiring, type TouchWiring } from '../dt-config/overlay.js';
32
32
  import { chipForTarget } from '../chips/index.js';
33
- import { detectZephyrVersion, checkZephyrCompat, resolveBoardTarget } from './compat.js';
34
33
  import { DEFAULT_ZEPHYR_DISPLAY_PROFILE } from '../display/profiles.js';
35
34
 
36
35
  /** Default board target — the framework's MVP canonical board. */
@@ -220,29 +219,7 @@ export const Toolchain = {
220
219
 
221
220
  compile(o: ToolchainOptions): CompileResult {
222
221
  const projectRoot = projectRootFromOptions(o);
223
- const rawBoard = targetFromOptions(o);
224
-
225
- // Fail fast on an incompatible Zephyr (clear message vs. a cryptic west/
226
- // CMake board error), then normalize the board target for the installed
227
- // version — Zephyr 4.3+ rejects bare multi-core board names, so a stale
228
- // config (esp32s3_devkitc) is rewritten to the qualified form
229
- // (esp32s3_devkitc/esp32s3/procpu). See toolchain/compat.ts.
230
- const zephyrVersion = detectZephyrVersion();
231
- const compat = checkZephyrCompat(zephyrVersion);
232
- if (compat.status === 'out-of-range') {
233
- throw new Error(
234
- `Zephyr ${zephyrVersion} is outside the supported range (${compat.range}) for @typecad/framework-zephyr. ` +
235
- `Set ZEPHYR_BASE to a compatible Zephyr checkout, or install one via '@typecad/zephyr-installer'.`,
236
- );
237
- }
238
- if (compat.status === 'undetectable') {
239
- console.warn(
240
- `! Could not detect the installed Zephyr version (is ZEPHYR_BASE set?). ` +
241
- `Skipping compat check; declared range is ${compat.range}.`,
242
- );
243
- }
244
- const board = resolveBoardTarget(rawBoard, zephyrVersion);
245
-
222
+ const board = targetFromOptions(o);
246
223
  const debugMode = new ZephyrStrategy().debugMode(board);
247
224
  const isGdbDebug = o.debug === true && debugMode === 'gdb';
248
225
  const zc = o.zephyrConfig as Record<string, unknown> | undefined;
package/dist/doctor.d.ts DELETED
@@ -1,6 +0,0 @@
1
- /**
2
- * Verify the installed Zephyr is reachable + inside the supported range, and
3
- * preview board-target normalization for the configured target. Sets
4
- * process.exitCode = 1 on an out-of-range Zephyr.
5
- */
6
- export declare function runDoctor(): void;
package/dist/doctor.js DELETED
@@ -1,48 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // @typecad/framework-zephyr — Zephyr environment doctor
3
- //
4
- // `cuttlefish doctor` (Zephyr framework) — verify the installed Zephyr RTOS is
5
- // reachable and inside the framework's declared compat range, and preview how
6
- // the configured board target resolves for that version. Exits 0 if the
7
- // environment is OK, non-zero otherwise. Mirrors framework-arduino's doctor
8
- // shape (dispatched via the framework's `doctor` export).
9
- // ---------------------------------------------------------------------------
10
- import * as ui from '@typecad/cuttlefish/utils/ui';
11
- import { loadCuttlefishConfig } from '@typecad/cuttlefish/config-loader';
12
- import { detectZephyrVersion, checkZephyrCompat, resolveBoardTarget } from './toolchain/compat.js';
13
- /**
14
- * Verify the installed Zephyr is reachable + inside the supported range, and
15
- * preview board-target normalization for the configured target. Sets
16
- * process.exitCode = 1 on an out-of-range Zephyr.
17
- */
18
- export function runDoctor() {
19
- ui.printHeader();
20
- ui.printStep('Checking Zephyr environment...');
21
- const base = process.env.ZEPHYR_BASE;
22
- const version = detectZephyrVersion();
23
- const result = checkZephyrCompat(version);
24
- ui.printInfo(`ZEPHYR_BASE ..... ${base ?? '(not set)'}`);
25
- ui.printInfo(`Zephyr version .. ${version ?? 'unknown (could not read ZEPHYR_BASE/VERSION)'}`);
26
- ui.printInfo(`Supported range . ${result.range ?? '(none declared)'}`);
27
- if (result.status === 'out-of-range') {
28
- ui.printError(`Zephyr ${version} is OUTSIDE the supported range (${result.range}).`);
29
- ui.printInfo("Set ZEPHYR_BASE to a compatible Zephyr checkout, or install one via '@typecad/zephyr-installer'.");
30
- process.exitCode = 1;
31
- return;
32
- }
33
- if (result.status === 'undetectable') {
34
- ui.printWarning('Could not detect the Zephyr version (is ZEPHYR_BASE set?) — compat check skipped.');
35
- }
36
- else {
37
- ui.printInfo('Zephyr compat ... OK');
38
- }
39
- // Preview how the configured board target resolves for this Zephyr version
40
- // (e.g. a stale bare id would be qualified at build time). The loader extracts
41
- // frameworkData.buildTarget to a top-level field.
42
- const config = loadCuttlefishConfig(process.cwd());
43
- const buildTarget = config?.buildTarget;
44
- if (buildTarget) {
45
- const resolved = resolveBoardTarget(buildTarget, version);
46
- ui.printInfo(`Board target .... ${buildTarget}${resolved === buildTarget ? '' : ` → ${resolved}`}`);
47
- }
48
- }
@@ -1,38 +0,0 @@
1
- /** Parse "4.3.99" / "v4.3" / "4.3.99-rc1" → [4, 3, 99]. Undefined if unparseable. */
2
- export declare function parseVersion(v: string | undefined): [number, number, number] | undefined;
3
- /** Compare two version strings: -1 if a<b, 0 if equal, 1 if a>b. */
4
- export declare function compareVersion(a: string, b: string): number;
5
- /**
6
- * Evaluate a simple range of space-separated comparators against a version.
7
- * Supported operators: >= <= > < =. Each comparator glues the operator to its
8
- * version (">=4.3", "<5.0"); spaces between comparators are AND.
9
- * satisfiesRange("4.3.99", ">=4.3 <5.0") === true
10
- */
11
- export declare function satisfiesRange(version: string, range: string): boolean;
12
- /**
13
- * Detect the installed Zephyr version from ZEPHYR_BASE/VERSION. Returns
14
- * undefined when ZEPHYR_BASE is unset or VERSION can't be read/parsed. Handles
15
- * the CMake-style file (VERSION_MAJOR = 4 / VERSION_MINOR = 3 / PATCHLEVEL = 99)
16
- * and a bare "4.3.99".
17
- */
18
- export declare function detectZephyrVersion(): string | undefined;
19
- export type CompatStatus = 'ok' | 'undetectable' | 'out-of-range';
20
- export interface CompatResult {
21
- version: string | undefined;
22
- /** Declared supported range (manifest.compat.zephyr), if any. */
23
- range: string | undefined;
24
- status: CompatStatus;
25
- }
26
- /**
27
- * Compare the detected Zephyr version against the declared compat range. Pure:
28
- * returns a status; the caller decides whether to throw/warn. 'ok' covers both
29
- * "in range" and "no range declared".
30
- */
31
- export declare function checkZephyrCompat(version: string | undefined): CompatResult;
32
- /**
33
- * Normalize a board target for the installed Zephyr version. Rewrites a stale
34
- * bare id (esp32s3_devkitc) to the qualified form on Zephyr >=4.3; idempotent
35
- * if the target is already qualified. Older Zephyr, single-core boards
36
- * (xiao_ble), and unknown boards pass through unchanged.
37
- */
38
- export declare function resolveBoardTarget(boardTarget: string, version: string | undefined): string;
@@ -1,133 +0,0 @@
1
- // Zephyr version compatibility + board-target normalization.
2
- //
3
- // Two mechanisms absorb Zephyr version churn:
4
- // 1. checkZephyrCompat() — compare the installed Zephyr version against the
5
- // declared range (manifest.compat.zephyr) so an incompatible Zephyr fails
6
- // fast with a clear message instead of a cryptic west/CMake board error.
7
- // 2. resolveBoardTarget() — normalize the board target for the installed
8
- // Zephyr version. Zephyr 4.3+ rejects bare multi-core board names
9
- // (esp32s3_devkitc) and requires a qualified target
10
- // (esp32s3_devkitc/esp32s3/procpu). This rewrites stale configs at build
11
- // time so users don't have to regenerate them after a Zephyr upgrade.
12
- //
13
- // This module is intentionally pure (no chalk/ui/console) so it unit-tests
14
- // cleanly; the doctor and the toolchain decide how to present results.
15
- import { readFileSync } from 'node:fs';
16
- import { join } from 'node:path';
17
- import manifest from '../framework.manifest.js';
18
- // ── minimal semver ──────────────────────────────────────────────────────────
19
- /** Parse "4.3.99" / "v4.3" / "4.3.99-rc1" → [4, 3, 99]. Undefined if unparseable. */
20
- export function parseVersion(v) {
21
- if (!v)
22
- return undefined;
23
- const m = v.replace(/^v/i, '').match(/^(\d+)(?:\.(\d+))?(?:\.(\d+))?/);
24
- if (!m)
25
- return undefined;
26
- return [Number(m[1]), Number(m[2] ?? 0), Number(m[3] ?? 0)];
27
- }
28
- /** Compare two version strings: -1 if a<b, 0 if equal, 1 if a>b. */
29
- export function compareVersion(a, b) {
30
- const pa = parseVersion(a) ?? [0, 0, 0];
31
- const pb = parseVersion(b) ?? [0, 0, 0];
32
- for (let i = 0; i < 3; i++) {
33
- if (pa[i] < pb[i])
34
- return -1;
35
- if (pa[i] > pb[i])
36
- return 1;
37
- }
38
- return 0;
39
- }
40
- /**
41
- * Evaluate a simple range of space-separated comparators against a version.
42
- * Supported operators: >= <= > < =. Each comparator glues the operator to its
43
- * version (">=4.3", "<5.0"); spaces between comparators are AND.
44
- * satisfiesRange("4.3.99", ">=4.3 <5.0") === true
45
- */
46
- export function satisfiesRange(version, range) {
47
- return range
48
- .trim()
49
- .split(/\s+/)
50
- .every((clause) => {
51
- const m = clause.replace(/\s+/g, '').match(/^(>=|<=|>|<|=)(.+)$/);
52
- if (!m)
53
- return true; // ignore anything that isn't a comparator
54
- const cmp = compareVersion(version, m[2]);
55
- switch (m[1]) {
56
- case '>=': return cmp >= 0;
57
- case '<=': return cmp <= 0;
58
- case '>': return cmp > 0;
59
- case '<': return cmp < 0;
60
- case '=': return cmp === 0;
61
- default: return true;
62
- }
63
- });
64
- }
65
- // ── Zephyr version detection ────────────────────────────────────────────────
66
- /**
67
- * Detect the installed Zephyr version from ZEPHYR_BASE/VERSION. Returns
68
- * undefined when ZEPHYR_BASE is unset or VERSION can't be read/parsed. Handles
69
- * the CMake-style file (VERSION_MAJOR = 4 / VERSION_MINOR = 3 / PATCHLEVEL = 99)
70
- * and a bare "4.3.99".
71
- */
72
- export function detectZephyrVersion() {
73
- const base = process.env.ZEPHYR_BASE;
74
- if (!base)
75
- return undefined;
76
- let content;
77
- try {
78
- content = readFileSync(join(base, 'VERSION'), 'utf-8');
79
- }
80
- catch {
81
- return undefined;
82
- }
83
- const field = (key) => {
84
- const m = content.match(new RegExp(`^\\s*${key}\\s*=\\s*(\\d+)`, 'm'));
85
- return m ? m[1] : undefined;
86
- };
87
- const major = field('VERSION_MAJOR');
88
- if (major !== undefined) {
89
- return `${major}.${field('VERSION_MINOR') ?? 0}.${field('PATCHLEVEL') ?? 0}`;
90
- }
91
- const m = content.match(/(\d+\.\d+(?:\.\d+)?)/);
92
- return m ? m[1] : undefined;
93
- }
94
- /**
95
- * Compare the detected Zephyr version against the declared compat range. Pure:
96
- * returns a status; the caller decides whether to throw/warn. 'ok' covers both
97
- * "in range" and "no range declared".
98
- */
99
- export function checkZephyrCompat(version) {
100
- const range = manifest.compat?.zephyr;
101
- if (!range)
102
- return { version, range, status: 'ok' };
103
- if (version === undefined)
104
- return { version, range, status: 'undetectable' };
105
- return { version, range, status: satisfiesRange(version, range) ? 'ok' : 'out-of-range' };
106
- }
107
- // ── board-target normalization ──────────────────────────────────────────────
108
- /**
109
- * For Zephyr >=4.3, multi-core ESP32 boards require a qualified board target
110
- * (board/<soc>/<core>) — the bare id is rejected with "Board qualifiers … not
111
- * found". Map each known multi-core board id to its procpu (main app core)
112
- * qualified form. procpu is the core that runs application firmware; appcpu is
113
- * the secondary core (selected explicitly only when offloading to it).
114
- */
115
- const QUALIFIED_TARGETS_GE_4_3 = {
116
- esp32_devkitc: 'esp32_devkitc/esp32/procpu',
117
- esp32s3_devkitc: 'esp32s3_devkitc/esp32s3/procpu',
118
- };
119
- /**
120
- * Normalize a board target for the installed Zephyr version. Rewrites a stale
121
- * bare id (esp32s3_devkitc) to the qualified form on Zephyr >=4.3; idempotent
122
- * if the target is already qualified. Older Zephyr, single-core boards
123
- * (xiao_ble), and unknown boards pass through unchanged.
124
- */
125
- export function resolveBoardTarget(boardTarget, version) {
126
- const boardId = boardTarget.split('/')[0];
127
- if (version !== undefined && satisfiesRange(version, '>=4.3')) {
128
- const qualified = QUALIFIED_TARGETS_GE_4_3[boardId];
129
- if (qualified)
130
- return qualified;
131
- }
132
- return boardTarget;
133
- }
package/src/doctor.ts DELETED
@@ -1,56 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // @typecad/framework-zephyr — Zephyr environment doctor
3
- //
4
- // `cuttlefish doctor` (Zephyr framework) — verify the installed Zephyr RTOS is
5
- // reachable and inside the framework's declared compat range, and preview how
6
- // the configured board target resolves for that version. Exits 0 if the
7
- // environment is OK, non-zero otherwise. Mirrors framework-arduino's doctor
8
- // shape (dispatched via the framework's `doctor` export).
9
- // ---------------------------------------------------------------------------
10
-
11
- import * as ui from '@typecad/cuttlefish/utils/ui';
12
- import { loadCuttlefishConfig } from '@typecad/cuttlefish/config-loader';
13
- import { detectZephyrVersion, checkZephyrCompat, resolveBoardTarget } from './toolchain/compat.js';
14
-
15
- /**
16
- * Verify the installed Zephyr is reachable + inside the supported range, and
17
- * preview board-target normalization for the configured target. Sets
18
- * process.exitCode = 1 on an out-of-range Zephyr.
19
- */
20
- export function runDoctor(): void {
21
- ui.printHeader();
22
- ui.printStep('Checking Zephyr environment...');
23
-
24
- const base = process.env.ZEPHYR_BASE;
25
- const version = detectZephyrVersion();
26
- const result = checkZephyrCompat(version);
27
-
28
- ui.printInfo(`ZEPHYR_BASE ..... ${base ?? '(not set)'}`);
29
- ui.printInfo(`Zephyr version .. ${version ?? 'unknown (could not read ZEPHYR_BASE/VERSION)'}`);
30
- ui.printInfo(`Supported range . ${result.range ?? '(none declared)'}`);
31
-
32
- if (result.status === 'out-of-range') {
33
- ui.printError(`Zephyr ${version} is OUTSIDE the supported range (${result.range}).`);
34
- ui.printInfo(
35
- "Set ZEPHYR_BASE to a compatible Zephyr checkout, or install one via '@typecad/zephyr-installer'.",
36
- );
37
- process.exitCode = 1;
38
- return;
39
- }
40
-
41
- if (result.status === 'undetectable') {
42
- ui.printWarning('Could not detect the Zephyr version (is ZEPHYR_BASE set?) — compat check skipped.');
43
- } else {
44
- ui.printInfo('Zephyr compat ... OK');
45
- }
46
-
47
- // Preview how the configured board target resolves for this Zephyr version
48
- // (e.g. a stale bare id would be qualified at build time). The loader extracts
49
- // frameworkData.buildTarget to a top-level field.
50
- const config = loadCuttlefishConfig(process.cwd());
51
- const buildTarget = config?.buildTarget;
52
- if (buildTarget) {
53
- const resolved = resolveBoardTarget(buildTarget, version);
54
- ui.printInfo(`Board target .... ${buildTarget}${resolved === buildTarget ? '' : ` → ${resolved}`}`);
55
- }
56
- }
@@ -1,145 +0,0 @@
1
- // Zephyr version compatibility + board-target normalization.
2
- //
3
- // Two mechanisms absorb Zephyr version churn:
4
- // 1. checkZephyrCompat() — compare the installed Zephyr version against the
5
- // declared range (manifest.compat.zephyr) so an incompatible Zephyr fails
6
- // fast with a clear message instead of a cryptic west/CMake board error.
7
- // 2. resolveBoardTarget() — normalize the board target for the installed
8
- // Zephyr version. Zephyr 4.3+ rejects bare multi-core board names
9
- // (esp32s3_devkitc) and requires a qualified target
10
- // (esp32s3_devkitc/esp32s3/procpu). This rewrites stale configs at build
11
- // time so users don't have to regenerate them after a Zephyr upgrade.
12
- //
13
- // This module is intentionally pure (no chalk/ui/console) so it unit-tests
14
- // cleanly; the doctor and the toolchain decide how to present results.
15
-
16
- import { readFileSync } from 'node:fs';
17
- import { join } from 'node:path';
18
- import manifest from '../framework.manifest.js';
19
-
20
- // ── minimal semver ──────────────────────────────────────────────────────────
21
-
22
- /** Parse "4.3.99" / "v4.3" / "4.3.99-rc1" → [4, 3, 99]. Undefined if unparseable. */
23
- export function parseVersion(v: string | undefined): [number, number, number] | undefined {
24
- if (!v) return undefined;
25
- const m = v.replace(/^v/i, '').match(/^(\d+)(?:\.(\d+))?(?:\.(\d+))?/);
26
- if (!m) return undefined;
27
- return [Number(m[1]), Number(m[2] ?? 0), Number(m[3] ?? 0)];
28
- }
29
-
30
- /** Compare two version strings: -1 if a<b, 0 if equal, 1 if a>b. */
31
- export function compareVersion(a: string, b: string): number {
32
- const pa = parseVersion(a) ?? [0, 0, 0];
33
- const pb = parseVersion(b) ?? [0, 0, 0];
34
- for (let i = 0; i < 3; i++) {
35
- if (pa[i]! < pb[i]!) return -1;
36
- if (pa[i]! > pb[i]!) return 1;
37
- }
38
- return 0;
39
- }
40
-
41
- /**
42
- * Evaluate a simple range of space-separated comparators against a version.
43
- * Supported operators: >= <= > < =. Each comparator glues the operator to its
44
- * version (">=4.3", "<5.0"); spaces between comparators are AND.
45
- * satisfiesRange("4.3.99", ">=4.3 <5.0") === true
46
- */
47
- export function satisfiesRange(version: string, range: string): boolean {
48
- return range
49
- .trim()
50
- .split(/\s+/)
51
- .every((clause) => {
52
- const m = clause.replace(/\s+/g, '').match(/^(>=|<=|>|<|=)(.+)$/);
53
- if (!m) return true; // ignore anything that isn't a comparator
54
- const cmp = compareVersion(version, m[2]!);
55
- switch (m[1]) {
56
- case '>=': return cmp >= 0;
57
- case '<=': return cmp <= 0;
58
- case '>': return cmp > 0;
59
- case '<': return cmp < 0;
60
- case '=': return cmp === 0;
61
- default: return true;
62
- }
63
- });
64
- }
65
-
66
- // ── Zephyr version detection ────────────────────────────────────────────────
67
-
68
- /**
69
- * Detect the installed Zephyr version from ZEPHYR_BASE/VERSION. Returns
70
- * undefined when ZEPHYR_BASE is unset or VERSION can't be read/parsed. Handles
71
- * the CMake-style file (VERSION_MAJOR = 4 / VERSION_MINOR = 3 / PATCHLEVEL = 99)
72
- * and a bare "4.3.99".
73
- */
74
- export function detectZephyrVersion(): string | undefined {
75
- const base = process.env.ZEPHYR_BASE;
76
- if (!base) return undefined;
77
- let content: string;
78
- try {
79
- content = readFileSync(join(base, 'VERSION'), 'utf-8');
80
- } catch {
81
- return undefined;
82
- }
83
- const field = (key: string): string | undefined => {
84
- const m = content.match(new RegExp(`^\\s*${key}\\s*=\\s*(\\d+)`, 'm'));
85
- return m ? m[1] : undefined;
86
- };
87
- const major = field('VERSION_MAJOR');
88
- if (major !== undefined) {
89
- return `${major}.${field('VERSION_MINOR') ?? 0}.${field('PATCHLEVEL') ?? 0}`;
90
- }
91
- const m = content.match(/(\d+\.\d+(?:\.\d+)?)/);
92
- return m ? m[1] : undefined;
93
- }
94
-
95
- // ── compat check ────────────────────────────────────────────────────────────
96
-
97
- export type CompatStatus = 'ok' | 'undetectable' | 'out-of-range';
98
-
99
- export interface CompatResult {
100
- version: string | undefined;
101
- /** Declared supported range (manifest.compat.zephyr), if any. */
102
- range: string | undefined;
103
- status: CompatStatus;
104
- }
105
-
106
- /**
107
- * Compare the detected Zephyr version against the declared compat range. Pure:
108
- * returns a status; the caller decides whether to throw/warn. 'ok' covers both
109
- * "in range" and "no range declared".
110
- */
111
- export function checkZephyrCompat(version: string | undefined): CompatResult {
112
- const range = manifest.compat?.zephyr;
113
- if (!range) return { version, range, status: 'ok' };
114
- if (version === undefined) return { version, range, status: 'undetectable' };
115
- return { version, range, status: satisfiesRange(version, range) ? 'ok' : 'out-of-range' };
116
- }
117
-
118
- // ── board-target normalization ──────────────────────────────────────────────
119
-
120
- /**
121
- * For Zephyr >=4.3, multi-core ESP32 boards require a qualified board target
122
- * (board/<soc>/<core>) — the bare id is rejected with "Board qualifiers … not
123
- * found". Map each known multi-core board id to its procpu (main app core)
124
- * qualified form. procpu is the core that runs application firmware; appcpu is
125
- * the secondary core (selected explicitly only when offloading to it).
126
- */
127
- const QUALIFIED_TARGETS_GE_4_3: Record<string, string> = {
128
- esp32_devkitc: 'esp32_devkitc/esp32/procpu',
129
- esp32s3_devkitc: 'esp32s3_devkitc/esp32s3/procpu',
130
- };
131
-
132
- /**
133
- * Normalize a board target for the installed Zephyr version. Rewrites a stale
134
- * bare id (esp32s3_devkitc) to the qualified form on Zephyr >=4.3; idempotent
135
- * if the target is already qualified. Older Zephyr, single-core boards
136
- * (xiao_ble), and unknown boards pass through unchanged.
137
- */
138
- export function resolveBoardTarget(boardTarget: string, version: string | undefined): string {
139
- const boardId = boardTarget.split('/')[0]!;
140
- if (version !== undefined && satisfiesRange(version, '>=4.3')) {
141
- const qualified = QUALIFIED_TARGETS_GE_4_3[boardId];
142
- if (qualified) return qualified;
143
- }
144
- return boardTarget;
145
- }