@kvell007/embed-labs-cli 0.1.0-alpha.30 → 0.1.0-alpha.32
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.
- package/README.md +8 -1
- package/dist/index.js +88 -16
- package/dist/index.js.map +1 -1
- package/dist/local-toolchain.d.ts +4 -0
- package/dist/local-toolchain.js +305 -42
- package/dist/local-toolchain.js.map +1 -1
- package/package.json +3 -3
|
@@ -83,6 +83,7 @@ export interface LocalToolchainPackageManifest {
|
|
|
83
83
|
family?: string;
|
|
84
84
|
board?: string;
|
|
85
85
|
variant?: string;
|
|
86
|
+
board_id?: string;
|
|
86
87
|
requires?: Array<{
|
|
87
88
|
id: string;
|
|
88
89
|
version: string;
|
|
@@ -148,6 +149,7 @@ export interface LocalToolchainCurrentResult {
|
|
|
148
149
|
}
|
|
149
150
|
export interface LocalToolchainListOptions extends LocalToolchainLatestOptions {
|
|
150
151
|
installRoot?: string;
|
|
152
|
+
installedOnly?: boolean;
|
|
151
153
|
}
|
|
152
154
|
export interface LocalToolchainEnvironmentRecord {
|
|
153
155
|
board_id: string;
|
|
@@ -193,6 +195,7 @@ export interface LocalToolchainEnvironmentComponent {
|
|
|
193
195
|
export interface LocalToolchainListResult {
|
|
194
196
|
host: string;
|
|
195
197
|
channel: string;
|
|
198
|
+
metadata_source: "built_in" | "local_override";
|
|
196
199
|
metadata_root?: string;
|
|
197
200
|
install_root: string;
|
|
198
201
|
registry_path: string;
|
|
@@ -258,6 +261,7 @@ export declare function installLocalToolchain(options?: LocalToolchainInstallOpt
|
|
|
258
261
|
export declare function validateLocalToolchain(input?: string | {
|
|
259
262
|
releaseRoot?: string;
|
|
260
263
|
mode?: string;
|
|
264
|
+
boardId?: string;
|
|
261
265
|
}): Promise<LocalToolchainValidationResult>;
|
|
262
266
|
export declare function compileTaishanPiSingleFile(options: LocalCompileOptions): Promise<LocalCompileResult>;
|
|
263
267
|
export declare function buildTaishanPiQtSmoke(options: LocalQtSmokeBuildOptions): Promise<LocalCompileResult>;
|
package/dist/local-toolchain.js
CHANGED
|
@@ -9,8 +9,9 @@ import { pipeline } from "node:stream/promises";
|
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
const DEFAULT_RELEASE_ROOT = "/Volumes/LLVM-TSPI/tspi-rk3566-llvm-release-minimal";
|
|
11
11
|
const DEFAULT_QT_SMOKE_SOURCE = "/Users/kvell/kk-project/DBT-Agent-Project/llvm-build-tspi/qt-smoke";
|
|
12
|
-
const DEFAULT_METADATA_ROOT = "/Users/kvell/kk-project/DBT-Agent-Project/llvm-build-tspi/embedlabs-release";
|
|
13
12
|
const DEFAULT_BOARD_ID = "taishanpi-1m-rk3566";
|
|
13
|
+
const PICO2W_RP2350_BOARD_ID = "pico2w-rp2350-monitor";
|
|
14
|
+
const COLOREASYPICO2_RP2350_BOARD_ID = "coloreasypico2-rp2350-monitor";
|
|
14
15
|
const DEFAULT_CHANNEL = "stable";
|
|
15
16
|
const DEFAULT_DOWNLOAD_BASE_URL = "https://download.embedboard.com";
|
|
16
17
|
const DOWNLOAD_REQUEST_TIMEOUT_MS = 12_000;
|
|
@@ -24,7 +25,10 @@ const BUILT_IN_CHANNEL = {
|
|
|
24
25
|
{ id: "embedlabs.tools.runtime.qtquick-live-preview", version: "1.0.31", manifest: "" },
|
|
25
26
|
{ id: "embedlabs.tools.runtime.rp2350-monitor", version: "1.0.31", manifest: "" },
|
|
26
27
|
{ id: "embedlabs.family.rk356x", version: "1.0.0", manifest: "" },
|
|
27
|
-
{ id: "embedlabs.
|
|
28
|
+
{ id: "embedlabs.family.rp2350", version: "1.0.0", manifest: "" },
|
|
29
|
+
{ id: "embedlabs.board.taishanpi.1m-rk3566", version: "1.0.31", manifest: "" },
|
|
30
|
+
{ id: "embedlabs.board.pico2w.rp2350-monitor", version: "1.0.31", manifest: "" },
|
|
31
|
+
{ id: "embedlabs.board.coloreasypico2.rp2350-monitor", version: "1.0.31", manifest: "" }
|
|
28
32
|
]
|
|
29
33
|
};
|
|
30
34
|
const BUILT_IN_MANIFESTS = {
|
|
@@ -80,6 +84,16 @@ const BUILT_IN_MANIFESTS = {
|
|
|
80
84
|
{ id: "embedlabs.tools.common.e2fsprogs", version: "^1.0.0" }
|
|
81
85
|
]
|
|
82
86
|
},
|
|
87
|
+
"embedlabs.family.rp2350": {
|
|
88
|
+
schema: "embedlabs.package.v1",
|
|
89
|
+
id: "embedlabs.family.rp2350",
|
|
90
|
+
version: "1.0.0",
|
|
91
|
+
kind: "family",
|
|
92
|
+
family: "rp2350",
|
|
93
|
+
requires: [
|
|
94
|
+
{ id: "embedlabs.tools.runtime.rp2350-monitor", version: "^1.0.31" }
|
|
95
|
+
]
|
|
96
|
+
},
|
|
83
97
|
"embedlabs.board.taishanpi.1m-rk3566": {
|
|
84
98
|
schema: "embedlabs.package.v1",
|
|
85
99
|
id: "embedlabs.board.taishanpi.1m-rk3566",
|
|
@@ -93,10 +107,41 @@ const BUILT_IN_MANIFESTS = {
|
|
|
93
107
|
{ id: "embedlabs.tools.vendor.rockchip", version: "^1.0.0", roles: ["flash", "resource-image"] },
|
|
94
108
|
{ id: "embedlabs.tools.common.llvm", version: "22.x", roles: ["compile"] },
|
|
95
109
|
{ id: "embedlabs.tools.common.e2fsprogs", version: "^1.0.0", roles: ["userdata-image"] },
|
|
96
|
-
{ id: "embedlabs.tools.runtime.qtquick-live-preview", version: "^1.0.31", roles: ["qtquick-preview"] }
|
|
97
|
-
{ id: "embedlabs.tools.runtime.rp2350-monitor", version: "^1.0.31", roles: ["rp2350-monitor"] }
|
|
110
|
+
{ id: "embedlabs.tools.runtime.qtquick-live-preview", version: "^1.0.31", roles: ["qtquick-preview"] }
|
|
98
111
|
],
|
|
99
112
|
build_modes: ["local-llvm"]
|
|
113
|
+
},
|
|
114
|
+
"embedlabs.board.pico2w.rp2350-monitor": {
|
|
115
|
+
schema: "embedlabs.package.v1",
|
|
116
|
+
id: "embedlabs.board.pico2w.rp2350-monitor",
|
|
117
|
+
version: "1.0.31",
|
|
118
|
+
kind: "board",
|
|
119
|
+
display_name: "Pico 2 W / RP2350 Monitor",
|
|
120
|
+
family: "rp2350",
|
|
121
|
+
board: "Pico 2 W",
|
|
122
|
+
variant: "RP2350 Monitor",
|
|
123
|
+
board_id: PICO2W_RP2350_BOARD_ID,
|
|
124
|
+
requires: [
|
|
125
|
+
{ id: "embedlabs.family.rp2350", version: "^1.0.0" },
|
|
126
|
+
{ id: "embedlabs.tools.runtime.rp2350-monitor", version: "^1.0.31", roles: ["hardware-control", "logic-analyzer", "debug-probe"] }
|
|
127
|
+
],
|
|
128
|
+
build_modes: ["local-monitor"]
|
|
129
|
+
},
|
|
130
|
+
"embedlabs.board.coloreasypico2.rp2350-monitor": {
|
|
131
|
+
schema: "embedlabs.package.v1",
|
|
132
|
+
id: "embedlabs.board.coloreasypico2.rp2350-monitor",
|
|
133
|
+
version: "1.0.31",
|
|
134
|
+
kind: "board",
|
|
135
|
+
display_name: "ColorEasyPICO2 / RP2350 Monitor",
|
|
136
|
+
family: "rp2350",
|
|
137
|
+
board: "ColorEasyPICO2",
|
|
138
|
+
variant: "RP2350 Monitor",
|
|
139
|
+
board_id: COLOREASYPICO2_RP2350_BOARD_ID,
|
|
140
|
+
requires: [
|
|
141
|
+
{ id: "embedlabs.family.rp2350", version: "^1.0.0" },
|
|
142
|
+
{ id: "embedlabs.tools.runtime.rp2350-monitor", version: "^1.0.31", roles: ["hardware-control", "logic-analyzer", "debug-probe"] }
|
|
143
|
+
],
|
|
144
|
+
build_modes: ["local-monitor"]
|
|
100
145
|
}
|
|
101
146
|
};
|
|
102
147
|
const INSTALL_COPY_PATHS = [
|
|
@@ -109,7 +154,6 @@ const INSTALL_COPY_PATHS = [
|
|
|
109
154
|
"tools/mac",
|
|
110
155
|
"toolkit-runtime/qtquick-live-preview",
|
|
111
156
|
"toolkit-runtime/rp2350-monitor",
|
|
112
|
-
"toolkit-runtime/RP2350-Monitor",
|
|
113
157
|
"images/current",
|
|
114
158
|
"userdata/rootfs",
|
|
115
159
|
"boot-workspace",
|
|
@@ -119,7 +163,7 @@ const INSTALL_COPY_PATHS = [
|
|
|
119
163
|
"support",
|
|
120
164
|
"third_party"
|
|
121
165
|
];
|
|
122
|
-
const LOCAL_TOOLCHAIN_INSTALL_MODES = ["minimal", "compile", "qt", "full", "images"];
|
|
166
|
+
const LOCAL_TOOLCHAIN_INSTALL_MODES = ["minimal", "runtime", "compile", "qt", "firmware", "full", "images"];
|
|
123
167
|
export function defaultLocalReleaseRoot() {
|
|
124
168
|
return process.env.EMBEDLABS_LOCAL_RELEASE_ROOT?.trim()
|
|
125
169
|
|| process.env.EMBEDLABS_RELEASE_ROOT?.trim()
|
|
@@ -134,12 +178,13 @@ export async function latestLocalToolchain(options = {}) {
|
|
|
134
178
|
if (!board) {
|
|
135
179
|
throw new Error(`No local toolchain board package found for ${boardId}.`);
|
|
136
180
|
}
|
|
181
|
+
const canonicalBoardId = boardIdForPackageManifest(board);
|
|
137
182
|
const packages = resolvePackageRefs(boardPackageId, channel, manifests);
|
|
138
183
|
let download;
|
|
139
184
|
let downloadError;
|
|
140
185
|
try {
|
|
141
186
|
download = await resolveLocalToolchainDownloadPlan({
|
|
142
|
-
boardId,
|
|
187
|
+
boardId: canonicalBoardId,
|
|
143
188
|
channel: channelName,
|
|
144
189
|
host: hostId(),
|
|
145
190
|
toolchain: "llvm"
|
|
@@ -149,7 +194,7 @@ export async function latestLocalToolchain(options = {}) {
|
|
|
149
194
|
downloadError = error instanceof Error ? error.message : String(error);
|
|
150
195
|
}
|
|
151
196
|
return {
|
|
152
|
-
board_id:
|
|
197
|
+
board_id: canonicalBoardId,
|
|
153
198
|
channel: channel.channel,
|
|
154
199
|
host: hostId(),
|
|
155
200
|
version: download?.version ?? board.version,
|
|
@@ -164,10 +209,34 @@ export async function currentLocalToolchain(installRoot, boardId = DEFAULT_BOARD
|
|
|
164
209
|
const registryPath = localToolchainRegistryPath(root);
|
|
165
210
|
try {
|
|
166
211
|
const registry = JSON.parse(await readFile(registryPath, "utf8"));
|
|
212
|
+
const environments = registry.environments;
|
|
213
|
+
const boardInstall = environments?.[normalizeBoardId(boardId)];
|
|
214
|
+
if (boardInstall?.release_root) {
|
|
215
|
+
return {
|
|
216
|
+
installed: true,
|
|
217
|
+
board_id: typeof boardInstall.board_id === "string" ? boardInstall.board_id : boardId,
|
|
218
|
+
version: typeof boardInstall.version === "string" ? boardInstall.version : undefined,
|
|
219
|
+
mode: typeof boardInstall.mode === "string" ? boardInstall.mode : undefined,
|
|
220
|
+
release_root: boardInstall.release_root,
|
|
221
|
+
registry_path: registryPath,
|
|
222
|
+
install_root: root,
|
|
223
|
+
channel: typeof boardInstall.channel === "string" ? boardInstall.channel : undefined,
|
|
224
|
+
packages: Array.isArray(boardInstall.packages) ? boardInstall.packages : undefined
|
|
225
|
+
};
|
|
226
|
+
}
|
|
167
227
|
const releaseRoot = typeof registry.release_root === "string" ? registry.release_root : undefined;
|
|
228
|
+
const registryBoardId = typeof registry.board_id === "string" ? registry.board_id : boardId;
|
|
229
|
+
if (releaseRoot && normalizeBoardId(registryBoardId) !== normalizeBoardId(boardId)) {
|
|
230
|
+
return {
|
|
231
|
+
installed: false,
|
|
232
|
+
board_id: boardId,
|
|
233
|
+
registry_path: registryPath,
|
|
234
|
+
install_root: root
|
|
235
|
+
};
|
|
236
|
+
}
|
|
168
237
|
return {
|
|
169
238
|
installed: !!releaseRoot,
|
|
170
|
-
board_id:
|
|
239
|
+
board_id: registryBoardId,
|
|
171
240
|
version: typeof registry.version === "string" ? registry.version : undefined,
|
|
172
241
|
mode: typeof registry.mode === "string" ? registry.mode : undefined,
|
|
173
242
|
release_root: releaseRoot,
|
|
@@ -186,6 +255,58 @@ export async function currentLocalToolchain(installRoot, boardId = DEFAULT_BOARD
|
|
|
186
255
|
};
|
|
187
256
|
}
|
|
188
257
|
}
|
|
258
|
+
async function discoverInstalledLocalToolchains(installRoot, current) {
|
|
259
|
+
const installed = new Map();
|
|
260
|
+
if (current.installed && current.release_root) {
|
|
261
|
+
installed.set(normalizeBoardId(current.board_id), {
|
|
262
|
+
board_id: current.board_id,
|
|
263
|
+
version: current.version,
|
|
264
|
+
channel: current.channel,
|
|
265
|
+
mode: current.mode,
|
|
266
|
+
release_root: current.release_root
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
const toolchainsRoot = join(installRoot, "toolchains");
|
|
270
|
+
let boardEntries;
|
|
271
|
+
try {
|
|
272
|
+
boardEntries = await readdir(toolchainsRoot, { withFileTypes: true });
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
return installed;
|
|
276
|
+
}
|
|
277
|
+
for (const boardEntry of boardEntries) {
|
|
278
|
+
if (!boardEntry.isDirectory()) {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
const boardId = normalizeBoardId(boardEntry.name);
|
|
282
|
+
if (installed.has(boardId)) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
const boardRoot = join(toolchainsRoot, boardEntry.name);
|
|
286
|
+
let versionEntries;
|
|
287
|
+
try {
|
|
288
|
+
versionEntries = await readdir(boardRoot, { withFileTypes: true });
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
const versions = versionEntries
|
|
294
|
+
.filter((entry) => entry.isDirectory())
|
|
295
|
+
.map((entry) => entry.name)
|
|
296
|
+
.sort(compareVersionLike)
|
|
297
|
+
.reverse();
|
|
298
|
+
const version = versions[0];
|
|
299
|
+
if (!version) {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
installed.set(boardId, {
|
|
303
|
+
board_id: boardId,
|
|
304
|
+
version,
|
|
305
|
+
release_root: join(boardRoot, version)
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
return installed;
|
|
309
|
+
}
|
|
189
310
|
export async function listLocalToolchainEnvironments(options = {}) {
|
|
190
311
|
const channelName = options.channel ?? DEFAULT_CHANNEL;
|
|
191
312
|
const host = hostId();
|
|
@@ -193,6 +314,7 @@ export async function listLocalToolchainEnvironments(options = {}) {
|
|
|
193
314
|
const registryPath = localToolchainRegistryPath(installRoot);
|
|
194
315
|
const { channel, manifests, metadataRoot } = await loadLocalToolchainMetadata(options.metadataRoot, channelName);
|
|
195
316
|
const current = await currentLocalToolchain(installRoot);
|
|
317
|
+
const installedByBoard = await discoverInstalledLocalToolchains(installRoot, current);
|
|
196
318
|
const boardManifests = [...manifests.values()]
|
|
197
319
|
.filter((manifest) => manifest.kind === "board")
|
|
198
320
|
.filter((manifest) => {
|
|
@@ -224,12 +346,16 @@ export async function listLocalToolchainEnvironments(options = {}) {
|
|
|
224
346
|
downloadError = error instanceof Error ? error.message : String(error);
|
|
225
347
|
}
|
|
226
348
|
const latestVersion = download?.version ?? board.version;
|
|
227
|
-
const
|
|
349
|
+
const currentForBoard = await currentLocalToolchain(installRoot, boardId);
|
|
350
|
+
const installedCandidate = currentForBoard.installed
|
|
351
|
+
? currentForBoard
|
|
352
|
+
: installedByBoard.get(normalizeBoardId(boardId));
|
|
353
|
+
const installed = installedCandidate
|
|
228
354
|
? {
|
|
229
|
-
version:
|
|
230
|
-
channel:
|
|
231
|
-
mode:
|
|
232
|
-
release_root:
|
|
355
|
+
version: installedCandidate.version,
|
|
356
|
+
channel: installedCandidate.channel,
|
|
357
|
+
mode: installedCandidate.mode,
|
|
358
|
+
release_root: installedCandidate.release_root
|
|
233
359
|
}
|
|
234
360
|
: undefined;
|
|
235
361
|
const updateAvailable = !!installed?.version && installed.version !== latestVersion;
|
|
@@ -241,6 +367,7 @@ export async function listLocalToolchainEnvironments(options = {}) {
|
|
|
241
367
|
? "installed"
|
|
242
368
|
: "available";
|
|
243
369
|
const mode = download?.default_mode ?? "qt";
|
|
370
|
+
const installModes = localToolchainInstallModesForDownload(download);
|
|
244
371
|
environments.push({
|
|
245
372
|
board_id: boardId,
|
|
246
373
|
package_id: board.id,
|
|
@@ -252,7 +379,7 @@ export async function listLocalToolchainEnvironments(options = {}) {
|
|
|
252
379
|
status,
|
|
253
380
|
supported_host: hostSupport.supported,
|
|
254
381
|
unsupported_packages: hostSupport.unsupportedPackages,
|
|
255
|
-
install_modes:
|
|
382
|
+
install_modes: installModes,
|
|
256
383
|
installed,
|
|
257
384
|
latest: {
|
|
258
385
|
version: latestVersion,
|
|
@@ -266,16 +393,20 @@ export async function listLocalToolchainEnvironments(options = {}) {
|
|
|
266
393
|
components: download?.components?.map(localToolchainEnvironmentComponent),
|
|
267
394
|
install_command: `embedlabs local toolchain install --board ${boardId} --mode ${mode}`,
|
|
268
395
|
update_command: `embedlabs local toolchain install --board ${boardId} --mode ${mode} --force`,
|
|
269
|
-
notes: environmentNotes({ status, downloadError, unsupportedPackages: hostSupport.unsupportedPackages })
|
|
396
|
+
notes: environmentNotes({ boardId, status, downloadError, unsupportedPackages: hostSupport.unsupportedPackages })
|
|
270
397
|
});
|
|
271
398
|
}
|
|
399
|
+
const filteredEnvironments = options.installedOnly
|
|
400
|
+
? environments.filter((environment) => !!environment.installed)
|
|
401
|
+
: environments;
|
|
272
402
|
return {
|
|
273
403
|
host,
|
|
274
404
|
channel: channel.channel,
|
|
405
|
+
metadata_source: metadataRoot ? "local_override" : "built_in",
|
|
275
406
|
metadata_root: metadataRoot,
|
|
276
407
|
install_root: installRoot,
|
|
277
408
|
registry_path: registryPath,
|
|
278
|
-
environments
|
|
409
|
+
environments: filteredEnvironments
|
|
279
410
|
};
|
|
280
411
|
}
|
|
281
412
|
export async function installLocalToolchain(options = {}) {
|
|
@@ -284,7 +415,7 @@ export async function installLocalToolchain(options = {}) {
|
|
|
284
415
|
const releaseRoot = resolve(installRoot, "toolchains", latest.board_id, latest.version);
|
|
285
416
|
const installMode = normalizeLocalToolchainInstallMode(options.mode ?? latest.download?.default_mode);
|
|
286
417
|
if (await pathExists(releaseRoot) && !options.force) {
|
|
287
|
-
const validation = await validateLocalToolchain({ releaseRoot, mode: installMode });
|
|
418
|
+
const validation = await validateLocalToolchain({ releaseRoot, mode: installMode, boardId: latest.board_id });
|
|
288
419
|
if (!validation.ok) {
|
|
289
420
|
if (latest.download?.components?.length) {
|
|
290
421
|
// Component installs can upgrade an existing lower-mode install by overlaying
|
|
@@ -320,7 +451,7 @@ export async function installLocalToolchain(options = {}) {
|
|
|
320
451
|
}
|
|
321
452
|
await mkdir(releaseRoot, { recursive: true });
|
|
322
453
|
const installedPaths = [];
|
|
323
|
-
for (const relativePath of
|
|
454
|
+
for (const relativePath of installCopyPathsForBoard(latest.board_id)) {
|
|
324
455
|
const sourcePath = resolve(sourceRoot.path, relativePath);
|
|
325
456
|
if (!await pathExists(sourcePath)) {
|
|
326
457
|
continue;
|
|
@@ -336,7 +467,7 @@ export async function installLocalToolchain(options = {}) {
|
|
|
336
467
|
installedPaths.push(relativePath);
|
|
337
468
|
}
|
|
338
469
|
await writeCurrentRegistry(installRoot, latest, releaseRoot, installMode, sourceRoot.source);
|
|
339
|
-
const validation = await validateLocalToolchain({ releaseRoot, mode: installMode });
|
|
470
|
+
const validation = await validateLocalToolchain({ releaseRoot, mode: installMode, boardId: latest.board_id });
|
|
340
471
|
if (!validation.ok) {
|
|
341
472
|
throw new Error(`Installed local toolchain is incomplete: ${validation.missing_paths.join(", ")}`);
|
|
342
473
|
}
|
|
@@ -362,8 +493,9 @@ export async function installLocalToolchain(options = {}) {
|
|
|
362
493
|
export async function validateLocalToolchain(input) {
|
|
363
494
|
const releaseRoot = typeof input === "string" ? input : input?.releaseRoot;
|
|
364
495
|
const mode = normalizeLocalToolchainInstallMode(typeof input === "string" ? undefined : input?.mode);
|
|
496
|
+
const boardId = normalizeBoardId(typeof input === "string" ? DEFAULT_BOARD_ID : input?.boardId ?? DEFAULT_BOARD_ID);
|
|
365
497
|
const resolvedRoot = await resolveLocalReleaseRoot(releaseRoot);
|
|
366
|
-
const required = requiredLocalToolchainChecks(mode);
|
|
498
|
+
const required = requiredLocalToolchainChecks(mode, boardId);
|
|
367
499
|
const checked_paths = [];
|
|
368
500
|
for (const [label, relativePath] of required) {
|
|
369
501
|
const absolutePath = resolve(resolvedRoot, relativePath);
|
|
@@ -381,13 +513,13 @@ export async function validateLocalToolchain(input) {
|
|
|
381
513
|
platform: platform(),
|
|
382
514
|
arch: arch()
|
|
383
515
|
},
|
|
384
|
-
board_id:
|
|
516
|
+
board_id: boardId,
|
|
385
517
|
release_root: resolvedRoot,
|
|
386
518
|
checked_paths,
|
|
387
519
|
missing_paths,
|
|
388
520
|
notes: [
|
|
389
521
|
"Local build commands require an Embed Labs auth token so local resource use remains account attributable.",
|
|
390
|
-
`This validator checks the
|
|
522
|
+
`This validator checks the ${boardId} local support layout for install mode ${mode}.`
|
|
391
523
|
]
|
|
392
524
|
};
|
|
393
525
|
}
|
|
@@ -401,7 +533,10 @@ function normalizeLocalToolchainInstallMode(mode) {
|
|
|
401
533
|
}
|
|
402
534
|
throw new Error(`Unsupported local toolchain install mode ${normalized}; expected ${LOCAL_TOOLCHAIN_INSTALL_MODES.join(", ")}.`);
|
|
403
535
|
}
|
|
404
|
-
function requiredLocalToolchainChecks(mode) {
|
|
536
|
+
function requiredLocalToolchainChecks(mode, boardId) {
|
|
537
|
+
if (isRp2350MonitorBoardId(boardId)) {
|
|
538
|
+
return requiredRp2350MonitorChecks(mode);
|
|
539
|
+
}
|
|
405
540
|
const base = [
|
|
406
541
|
["release root", "."],
|
|
407
542
|
["Rockchip mkimage", "tools/mac/mkimage"],
|
|
@@ -411,8 +546,6 @@ function requiredLocalToolchainChecks(mode) {
|
|
|
411
546
|
["boot resource image", "boot-workspace/out/resource.img"],
|
|
412
547
|
["boot image", "boot-workspace/out/boot.img"],
|
|
413
548
|
["boot DTB", "boot-workspace/out/tspi-rk3566-user-v10-linux.dtb"],
|
|
414
|
-
["RP2350 Monitor CLI", "toolkit-runtime/rp2350-monitor/tools/rpmon_cli.py"],
|
|
415
|
-
["RP2350 Monitor logic analyzer", "toolkit-runtime/rp2350-monitor/ui/bin/embed-labs-logic-analyzer"],
|
|
416
549
|
["package metadata", "meta"]
|
|
417
550
|
];
|
|
418
551
|
const compile = [
|
|
@@ -452,6 +585,25 @@ function requiredLocalToolchainChecks(mode) {
|
|
|
452
585
|
}
|
|
453
586
|
return [...base, ...compile, ...qt, ...images, ...full];
|
|
454
587
|
}
|
|
588
|
+
function requiredRp2350MonitorChecks(mode) {
|
|
589
|
+
const runtime = [
|
|
590
|
+
["release root", "."],
|
|
591
|
+
["RP2350 Monitor UI", "toolkit-runtime/rp2350-monitor/ui/index.html"],
|
|
592
|
+
["RP2350 Monitor bridge", "toolkit-runtime/rp2350-monitor/ui/bridge/rpmon_bridge.py"],
|
|
593
|
+
["RP2350 Monitor CLI", "toolkit-runtime/rp2350-monitor/tools/rpmon_cli.py"],
|
|
594
|
+
["RP2350 Monitor logic analyzer", "toolkit-runtime/rp2350-monitor/ui/bin/embed-labs-logic-analyzer"],
|
|
595
|
+
["RP2350 Monitor AI operation contract", "toolkit-runtime/rp2350-monitor/ui/docs/ai-operation-contract.md"],
|
|
596
|
+
["package metadata", "meta"]
|
|
597
|
+
];
|
|
598
|
+
const firmware = [
|
|
599
|
+
["RP2350 Monitor UF2", "toolkit-runtime/rp2350-monitor/firmware/rp2350_monitor.uf2"],
|
|
600
|
+
["RP2350 Monitor firmware source", "toolkit-runtime/rp2350-monitor/firmware/src/main.cpp"]
|
|
601
|
+
];
|
|
602
|
+
if (mode === "firmware" || mode === "full") {
|
|
603
|
+
return [...runtime, ...firmware];
|
|
604
|
+
}
|
|
605
|
+
return runtime;
|
|
606
|
+
}
|
|
455
607
|
export async function compileTaishanPiSingleFile(options) {
|
|
456
608
|
assertAuthenticated(options.auth);
|
|
457
609
|
const releaseRoot = await resolveLocalReleaseRoot(options.releaseRoot);
|
|
@@ -521,15 +673,14 @@ export async function buildTaishanPiQtSmoke(options) {
|
|
|
521
673
|
}
|
|
522
674
|
async function loadLocalToolchainMetadata(metadataRoot, channelName) {
|
|
523
675
|
const explicitRoot = metadataRoot || process.env.EMBEDLABS_METADATA_ROOT?.trim();
|
|
524
|
-
|
|
525
|
-
if (!candidateRoot) {
|
|
676
|
+
if (!explicitRoot) {
|
|
526
677
|
return {
|
|
527
678
|
channel: BUILT_IN_CHANNEL,
|
|
528
679
|
manifests: new Map(Object.entries(BUILT_IN_MANIFESTS)),
|
|
529
680
|
metadataRoot: undefined
|
|
530
681
|
};
|
|
531
682
|
}
|
|
532
|
-
const root = resolve(
|
|
683
|
+
const root = resolve(explicitRoot);
|
|
533
684
|
const channelPath = join(root, "channels", channelName, "index.json");
|
|
534
685
|
const channel = JSON.parse(await readFile(channelPath, "utf8"));
|
|
535
686
|
if (channel.schema !== "embedlabs.channel.v1") {
|
|
@@ -552,17 +703,26 @@ async function loadLocalToolchainMetadata(metadataRoot, channelName) {
|
|
|
552
703
|
return { channel, manifests, metadataRoot: root };
|
|
553
704
|
}
|
|
554
705
|
async function resolveLocalToolchainDownloadPlan(input) {
|
|
706
|
+
const requestedBoardId = normalizeBoardId(input.boardId);
|
|
707
|
+
const compatibleBoardIds = compatibleDownloadBoardIds(requestedBoardId);
|
|
555
708
|
const channelUrl = downloadChannelUrl(input.channel);
|
|
556
709
|
const channel = await fetchJson(channelUrl);
|
|
557
710
|
if (channel.schema !== "embedlabs.download-channel.v1") {
|
|
558
711
|
throw new Error(`Unexpected download channel schema ${channel.schema}.`);
|
|
559
712
|
}
|
|
560
|
-
const
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
713
|
+
const entries = channel.packages ?? [];
|
|
714
|
+
let entry;
|
|
715
|
+
for (const boardId of compatibleBoardIds) {
|
|
716
|
+
entry = entries.find((item) => {
|
|
717
|
+
return normalizeBoardId(item.board_id ?? "") === boardId
|
|
718
|
+
&& item.host === input.host
|
|
719
|
+
&& item.toolchain === input.toolchain
|
|
720
|
+
&& (item.kind === undefined || item.kind === "toolchain-archive" || item.kind === "board-support-archive");
|
|
721
|
+
});
|
|
722
|
+
if (entry) {
|
|
723
|
+
break;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
566
726
|
if (!entry?.manifest) {
|
|
567
727
|
return undefined;
|
|
568
728
|
}
|
|
@@ -571,8 +731,9 @@ async function resolveLocalToolchainDownloadPlan(input) {
|
|
|
571
731
|
if (manifest.id !== entry.id || manifest.version !== entry.version) {
|
|
572
732
|
throw new Error(`Download manifest mismatch for ${entry.id}@${entry.version}.`);
|
|
573
733
|
}
|
|
574
|
-
|
|
575
|
-
|
|
734
|
+
const manifestBoardId = normalizeBoardId(manifest.board_id ?? "");
|
|
735
|
+
if (!compatibleBoardIds.includes(manifestBoardId) || manifest.host !== input.host || manifest.toolchain !== input.toolchain) {
|
|
736
|
+
throw new Error(`Download manifest does not match requested ${requestedBoardId}/${input.host}/${input.toolchain}.`);
|
|
576
737
|
}
|
|
577
738
|
if ((!manifest.archive?.file || !manifest.archive.sha256 || !Number.isFinite(manifest.archive.size_bytes))
|
|
578
739
|
&& (!Array.isArray(manifest.components) || manifest.components.length === 0)) {
|
|
@@ -589,7 +750,7 @@ async function resolveLocalToolchainDownloadPlan(input) {
|
|
|
589
750
|
size_bytes: Number.isFinite(mirror.size_bytes) ? mirror.size_bytes : manifest.archive?.size_bytes
|
|
590
751
|
})), manifest.download_policy?.preferred_order)
|
|
591
752
|
: [];
|
|
592
|
-
const components = (manifest.components ?? []).map((component) => normalizeDownloadComponent(component, manifest, manifestUrl));
|
|
753
|
+
const components = downloadComponentsForBoard(requestedBoardId, (manifest.components ?? []).map((component) => normalizeDownloadComponent(component, manifest, manifestUrl)));
|
|
593
754
|
const first = mirrors[0];
|
|
594
755
|
if (!first && components.length === 0) {
|
|
595
756
|
return undefined;
|
|
@@ -599,7 +760,7 @@ async function resolveLocalToolchainDownloadPlan(input) {
|
|
|
599
760
|
manifest_url: manifestUrl,
|
|
600
761
|
package_id: manifest.id,
|
|
601
762
|
version: manifest.version,
|
|
602
|
-
board_id:
|
|
763
|
+
board_id: requestedBoardId,
|
|
603
764
|
host: input.host,
|
|
604
765
|
toolchain: input.toolchain,
|
|
605
766
|
source_url: first?.url,
|
|
@@ -706,10 +867,27 @@ function resolvePackageRefs(packageId, channel, manifests, seen = new Set()) {
|
|
|
706
867
|
return refs;
|
|
707
868
|
}
|
|
708
869
|
function boardPackageIdFor(boardId) {
|
|
709
|
-
|
|
870
|
+
const normalized = normalizeBoardId(boardId);
|
|
871
|
+
if (normalized === DEFAULT_BOARD_ID || normalized === "taishanpi" || normalized === "taishanpi-1m-rk3566") {
|
|
710
872
|
return "embedlabs.board.taishanpi.1m-rk3566";
|
|
711
873
|
}
|
|
712
|
-
if (
|
|
874
|
+
if (normalized === COLOREASYPICO2_RP2350_BOARD_ID
|
|
875
|
+
|| normalized === "coloreasypico2"
|
|
876
|
+
|| normalized === "color-easy-pico2"
|
|
877
|
+
|| normalized === "color-easy-pico-2"
|
|
878
|
+
|| normalized === "color-easy-pico-2-rp2350-monitor"
|
|
879
|
+
|| normalized === "ce-pico2") {
|
|
880
|
+
return "embedlabs.board.coloreasypico2.rp2350-monitor";
|
|
881
|
+
}
|
|
882
|
+
if (normalized === PICO2W_RP2350_BOARD_ID
|
|
883
|
+
|| normalized === "pico2w"
|
|
884
|
+
|| normalized === "pico-2-w"
|
|
885
|
+
|| normalized === "pico2"
|
|
886
|
+
|| normalized === "rp2350"
|
|
887
|
+
|| normalized === "rp2350-monitor") {
|
|
888
|
+
return "embedlabs.board.pico2w.rp2350-monitor";
|
|
889
|
+
}
|
|
890
|
+
if (normalized.startsWith("embedlabs.board.")) {
|
|
713
891
|
return boardId;
|
|
714
892
|
}
|
|
715
893
|
throw new Error(`Unsupported local toolchain board ${boardId}.`);
|
|
@@ -738,6 +916,44 @@ function boardIdForPackageManifest(manifest) {
|
|
|
738
916
|
function normalizeBoardId(boardId) {
|
|
739
917
|
return boardId.trim().toLowerCase().replaceAll("_", "-");
|
|
740
918
|
}
|
|
919
|
+
function isRp2350MonitorBoardId(boardId) {
|
|
920
|
+
const normalized = normalizeBoardId(boardId);
|
|
921
|
+
return normalized === PICO2W_RP2350_BOARD_ID
|
|
922
|
+
|| normalized === COLOREASYPICO2_RP2350_BOARD_ID
|
|
923
|
+
|| normalized === "pico2w"
|
|
924
|
+
|| normalized === "pico-2-w"
|
|
925
|
+
|| normalized === "pico2"
|
|
926
|
+
|| normalized === "rp2350"
|
|
927
|
+
|| normalized === "rp2350-monitor"
|
|
928
|
+
|| normalized === "coloreasypico2"
|
|
929
|
+
|| normalized === "color-easy-pico2"
|
|
930
|
+
|| normalized === "color-easy-pico-2"
|
|
931
|
+
|| normalized === "ce-pico2";
|
|
932
|
+
}
|
|
933
|
+
function compatibleDownloadBoardIds(boardId) {
|
|
934
|
+
const normalized = normalizeBoardId(boardId);
|
|
935
|
+
if (normalized === COLOREASYPICO2_RP2350_BOARD_ID) {
|
|
936
|
+
return [COLOREASYPICO2_RP2350_BOARD_ID, PICO2W_RP2350_BOARD_ID];
|
|
937
|
+
}
|
|
938
|
+
return [normalized];
|
|
939
|
+
}
|
|
940
|
+
function downloadComponentsForBoard(boardId, components) {
|
|
941
|
+
const normalized = normalizeBoardId(boardId);
|
|
942
|
+
if (normalized === DEFAULT_BOARD_ID) {
|
|
943
|
+
return components.filter((component) => !isRp2350MonitorComponent(component));
|
|
944
|
+
}
|
|
945
|
+
return components;
|
|
946
|
+
}
|
|
947
|
+
function isRp2350MonitorComponent(component) {
|
|
948
|
+
const text = `${component.id} ${component.role ?? ""} ${component.archive.file}`.toLowerCase();
|
|
949
|
+
return text.includes("rp2350-monitor") || text.includes("pico2w-rp2350-monitor");
|
|
950
|
+
}
|
|
951
|
+
function compareVersionLike(left, right) {
|
|
952
|
+
return left.localeCompare(right, undefined, { numeric: true, sensitivity: "base" });
|
|
953
|
+
}
|
|
954
|
+
function isRecord(value) {
|
|
955
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
956
|
+
}
|
|
741
957
|
function packageHostSupport(packages, manifests, host) {
|
|
742
958
|
const unsupportedPackages = [];
|
|
743
959
|
for (const item of packages) {
|
|
@@ -753,6 +969,9 @@ function packageHostSupport(packages, manifests, host) {
|
|
|
753
969
|
}
|
|
754
970
|
function environmentNotes(input) {
|
|
755
971
|
const notes = [];
|
|
972
|
+
if (normalizeBoardId(input.boardId) === COLOREASYPICO2_RP2350_BOARD_ID) {
|
|
973
|
+
notes.push("ColorEasyPICO2 uses the same RP2350 Monitor runtime and UF2 firmware package; Wi-Fi operations are not declared for this no-Wi-Fi board.");
|
|
974
|
+
}
|
|
756
975
|
if (input.status === "available") {
|
|
757
976
|
notes.push("Environment is available but not installed on this computer.");
|
|
758
977
|
}
|
|
@@ -767,6 +986,20 @@ function environmentNotes(input) {
|
|
|
767
986
|
}
|
|
768
987
|
return notes;
|
|
769
988
|
}
|
|
989
|
+
function localToolchainInstallModesForDownload(download) {
|
|
990
|
+
if (!download?.components?.length) {
|
|
991
|
+
return [...LOCAL_TOOLCHAIN_INSTALL_MODES];
|
|
992
|
+
}
|
|
993
|
+
const modes = new Set();
|
|
994
|
+
for (const component of download.components) {
|
|
995
|
+
for (const mode of component.install_modes ?? []) {
|
|
996
|
+
modes.add(mode);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
return modes.size > 0
|
|
1000
|
+
? [...modes].filter((mode) => LOCAL_TOOLCHAIN_INSTALL_MODES.includes(mode))
|
|
1001
|
+
: [...LOCAL_TOOLCHAIN_INSTALL_MODES];
|
|
1002
|
+
}
|
|
770
1003
|
function localToolchainEnvironmentComponent(component) {
|
|
771
1004
|
return {
|
|
772
1005
|
id: component.id,
|
|
@@ -798,7 +1031,14 @@ function localToolchainRegistryPath(installRoot) {
|
|
|
798
1031
|
async function writeCurrentRegistry(installRoot, latest, releaseRoot, mode, source) {
|
|
799
1032
|
const registryPath = localToolchainRegistryPath(installRoot);
|
|
800
1033
|
await mkdir(dirname(registryPath), { recursive: true });
|
|
801
|
-
|
|
1034
|
+
let existing = {};
|
|
1035
|
+
try {
|
|
1036
|
+
existing = JSON.parse(await readFile(registryPath, "utf8"));
|
|
1037
|
+
}
|
|
1038
|
+
catch {
|
|
1039
|
+
existing = {};
|
|
1040
|
+
}
|
|
1041
|
+
const entry = {
|
|
802
1042
|
installed: true,
|
|
803
1043
|
board_id: latest.board_id,
|
|
804
1044
|
version: latest.version,
|
|
@@ -810,6 +1050,19 @@ async function writeCurrentRegistry(installRoot, latest, releaseRoot, mode, sour
|
|
|
810
1050
|
source,
|
|
811
1051
|
installed_components: source?.components,
|
|
812
1052
|
updated_at: new Date().toISOString()
|
|
1053
|
+
};
|
|
1054
|
+
const environments = isRecord(existing.environments)
|
|
1055
|
+
? { ...existing.environments }
|
|
1056
|
+
: {};
|
|
1057
|
+
environments[normalizeBoardId(latest.board_id)] = entry;
|
|
1058
|
+
const preserveTopLevel = latest.board_id !== DEFAULT_BOARD_ID
|
|
1059
|
+
&& typeof existing.release_root === "string"
|
|
1060
|
+
&& normalizeBoardId(String(existing.board_id ?? DEFAULT_BOARD_ID)) === DEFAULT_BOARD_ID;
|
|
1061
|
+
const topLevel = preserveTopLevel ? existing : entry;
|
|
1062
|
+
await writeFile(registryPath, `${JSON.stringify({
|
|
1063
|
+
...topLevel,
|
|
1064
|
+
environments,
|
|
1065
|
+
updated_at: new Date().toISOString()
|
|
813
1066
|
}, null, 2)}\n`, "utf8");
|
|
814
1067
|
}
|
|
815
1068
|
async function resolveLocalReleaseRoot(releaseRoot) {
|
|
@@ -963,6 +1216,16 @@ function selectedDownloadComponents(components, mode) {
|
|
|
963
1216
|
return component.install_modes.includes(mode);
|
|
964
1217
|
});
|
|
965
1218
|
}
|
|
1219
|
+
function installCopyPathsForBoard(boardId) {
|
|
1220
|
+
if (normalizeBoardId(boardId) === DEFAULT_BOARD_ID) {
|
|
1221
|
+
return INSTALL_COPY_PATHS.filter((relativePath) => {
|
|
1222
|
+
const normalized = relativePath.toLowerCase();
|
|
1223
|
+
return normalized !== "toolkit-runtime/rp2350-monitor"
|
|
1224
|
+
&& normalized !== "toolkit-runtime/rp2350-monitor/";
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
return INSTALL_COPY_PATHS;
|
|
1228
|
+
}
|
|
966
1229
|
async function downloadToolchainArchive(sourceUrl, installRoot, expected) {
|
|
967
1230
|
const downloadsDir = join(installRoot, "cache", "downloads");
|
|
968
1231
|
await mkdir(downloadsDir, { recursive: true });
|