@kvell007/embed-labs-cli 0.1.0-alpha.10 → 0.1.0-alpha.12
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/dist/index.js +40 -11
- package/dist/index.js.map +1 -1
- package/dist/local-toolchain.d.ts +40 -5
- package/dist/local-toolchain.js +268 -57
- package/dist/local-toolchain.js.map +1 -1
- package/package.json +3 -3
|
@@ -6,6 +6,7 @@ export interface LocalToolchainAuthContext {
|
|
|
6
6
|
}
|
|
7
7
|
export interface LocalToolchainValidationResult {
|
|
8
8
|
ok: boolean;
|
|
9
|
+
mode: string;
|
|
9
10
|
host: {
|
|
10
11
|
platform: string;
|
|
11
12
|
arch: string;
|
|
@@ -107,6 +108,7 @@ export interface LocalToolchainInstallOptions extends LocalToolchainLatestOption
|
|
|
107
108
|
sourceReleaseRoot?: string;
|
|
108
109
|
sourceUrl?: string;
|
|
109
110
|
installRoot?: string;
|
|
111
|
+
mode?: string;
|
|
110
112
|
force?: boolean;
|
|
111
113
|
}
|
|
112
114
|
export interface LocalToolchainInstallResult {
|
|
@@ -114,16 +116,18 @@ export interface LocalToolchainInstallResult {
|
|
|
114
116
|
version: string;
|
|
115
117
|
channel: string;
|
|
116
118
|
host: string;
|
|
119
|
+
mode: string;
|
|
117
120
|
install_root: string;
|
|
118
121
|
release_root: string;
|
|
119
122
|
registry_path: string;
|
|
120
123
|
source: {
|
|
121
|
-
kind: "directory" | "url";
|
|
124
|
+
kind: "directory" | "url" | "components";
|
|
122
125
|
value: string;
|
|
123
126
|
downloaded_path?: string;
|
|
124
127
|
mirror_kind?: string;
|
|
125
128
|
sha256?: string;
|
|
126
129
|
size_bytes?: number;
|
|
130
|
+
components?: LocalToolchainInstalledComponent[];
|
|
127
131
|
};
|
|
128
132
|
installed_paths: string[];
|
|
129
133
|
packages: LocalToolchainPackageRef[];
|
|
@@ -133,6 +137,7 @@ export interface LocalToolchainCurrentResult {
|
|
|
133
137
|
installed: boolean;
|
|
134
138
|
board_id: string;
|
|
135
139
|
version?: string;
|
|
140
|
+
mode?: string;
|
|
136
141
|
release_root?: string;
|
|
137
142
|
registry_path: string;
|
|
138
143
|
install_root: string;
|
|
@@ -147,15 +152,17 @@ export interface LocalToolchainDownloadPlan {
|
|
|
147
152
|
board_id: string;
|
|
148
153
|
host: string;
|
|
149
154
|
toolchain: string;
|
|
150
|
-
source_url
|
|
151
|
-
mirror_kind
|
|
152
|
-
archive
|
|
155
|
+
source_url?: string;
|
|
156
|
+
mirror_kind?: string;
|
|
157
|
+
archive?: {
|
|
153
158
|
file: string;
|
|
154
159
|
size_bytes: number;
|
|
155
160
|
sha256: string;
|
|
156
161
|
content_type?: string;
|
|
157
162
|
};
|
|
158
163
|
mirrors: LocalToolchainDownloadMirror[];
|
|
164
|
+
components?: LocalToolchainDownloadComponent[];
|
|
165
|
+
default_mode?: string;
|
|
159
166
|
}
|
|
160
167
|
export interface LocalToolchainDownloadMirror {
|
|
161
168
|
kind: string;
|
|
@@ -164,10 +171,38 @@ export interface LocalToolchainDownloadMirror {
|
|
|
164
171
|
sha256?: string;
|
|
165
172
|
size_bytes?: number;
|
|
166
173
|
}
|
|
174
|
+
export interface LocalToolchainDownloadComponent {
|
|
175
|
+
id: string;
|
|
176
|
+
version: string;
|
|
177
|
+
role?: string;
|
|
178
|
+
install_modes?: string[];
|
|
179
|
+
archive: {
|
|
180
|
+
file: string;
|
|
181
|
+
size_bytes: number;
|
|
182
|
+
sha256: string;
|
|
183
|
+
content_type?: string;
|
|
184
|
+
};
|
|
185
|
+
source_url?: string;
|
|
186
|
+
mirror_kind?: string;
|
|
187
|
+
mirrors: LocalToolchainDownloadMirror[];
|
|
188
|
+
}
|
|
189
|
+
export interface LocalToolchainInstalledComponent {
|
|
190
|
+
id: string;
|
|
191
|
+
version: string;
|
|
192
|
+
role?: string;
|
|
193
|
+
archive_file: string;
|
|
194
|
+
mirror_kind?: string;
|
|
195
|
+
downloaded_path: string;
|
|
196
|
+
size_bytes: number;
|
|
197
|
+
sha256: string;
|
|
198
|
+
}
|
|
167
199
|
export declare function defaultLocalReleaseRoot(): string;
|
|
168
200
|
export declare function latestLocalToolchain(options?: LocalToolchainLatestOptions): Promise<LocalToolchainLatestResult>;
|
|
169
201
|
export declare function currentLocalToolchain(installRoot?: string, boardId?: string): Promise<LocalToolchainCurrentResult>;
|
|
170
202
|
export declare function installLocalToolchain(options?: LocalToolchainInstallOptions): Promise<LocalToolchainInstallResult>;
|
|
171
|
-
export declare function validateLocalToolchain(
|
|
203
|
+
export declare function validateLocalToolchain(input?: string | {
|
|
204
|
+
releaseRoot?: string;
|
|
205
|
+
mode?: string;
|
|
206
|
+
}): Promise<LocalToolchainValidationResult>;
|
|
172
207
|
export declare function compileTaishanPiSingleFile(options: LocalCompileOptions): Promise<LocalCompileResult>;
|
|
173
208
|
export declare function buildTaishanPiQtSmoke(options: LocalQtSmokeBuildOptions): Promise<LocalCompileResult>;
|
package/dist/local-toolchain.js
CHANGED
|
@@ -21,6 +21,8 @@ const BUILT_IN_CHANNEL = {
|
|
|
21
21
|
{ id: "embedlabs.tools.vendor.rockchip", version: "1.0.0", manifest: "" },
|
|
22
22
|
{ id: "embedlabs.tools.common.llvm", version: "22.1.3", manifest: "" },
|
|
23
23
|
{ id: "embedlabs.tools.common.e2fsprogs", version: "1.0.0", manifest: "" },
|
|
24
|
+
{ id: "embedlabs.tools.runtime.qtquick-live-preview", version: "1.0.31", manifest: "" },
|
|
25
|
+
{ id: "embedlabs.tools.runtime.rp2350-monitor", version: "1.0.31", manifest: "" },
|
|
24
26
|
{ id: "embedlabs.family.rk356x", version: "1.0.0", manifest: "" },
|
|
25
27
|
{ id: "embedlabs.board.taishanpi.1m-rk3566", version: "1.0.31", manifest: "" }
|
|
26
28
|
]
|
|
@@ -50,6 +52,22 @@ const BUILT_IN_MANIFESTS = {
|
|
|
50
52
|
hosts: ["darwin-arm64", "linux-x86_64"],
|
|
51
53
|
provides: ["ext4.mke2fs", "ext4.resize2fs", "fakeroot"]
|
|
52
54
|
},
|
|
55
|
+
"embedlabs.tools.runtime.qtquick-live-preview": {
|
|
56
|
+
schema: "embedlabs.package.v1",
|
|
57
|
+
id: "embedlabs.tools.runtime.qtquick-live-preview",
|
|
58
|
+
version: "1.0.31",
|
|
59
|
+
kind: "tools",
|
|
60
|
+
hosts: ["darwin-arm64", "linux-x86_64"],
|
|
61
|
+
provides: ["qtquick.live_preview", "qtquick.live_preview.inspector", "qtquick.live_preview.feedback"]
|
|
62
|
+
},
|
|
63
|
+
"embedlabs.tools.runtime.rp2350-monitor": {
|
|
64
|
+
schema: "embedlabs.package.v1",
|
|
65
|
+
id: "embedlabs.tools.runtime.rp2350-monitor",
|
|
66
|
+
version: "1.0.31",
|
|
67
|
+
kind: "tools",
|
|
68
|
+
hosts: ["darwin-arm64", "linux-x86_64"],
|
|
69
|
+
provides: ["rp2350.monitor.cli", "rp2350.monitor.logic_analyzer", "rp2350.monitor.logic_decode"]
|
|
70
|
+
},
|
|
53
71
|
"embedlabs.family.rk356x": {
|
|
54
72
|
schema: "embedlabs.package.v1",
|
|
55
73
|
id: "embedlabs.family.rk356x",
|
|
@@ -74,7 +92,9 @@ const BUILT_IN_MANIFESTS = {
|
|
|
74
92
|
{ id: "embedlabs.family.rk356x", version: "^1.0.0" },
|
|
75
93
|
{ id: "embedlabs.tools.vendor.rockchip", version: "^1.0.0", roles: ["flash", "resource-image"] },
|
|
76
94
|
{ id: "embedlabs.tools.common.llvm", version: "22.x", roles: ["compile"] },
|
|
77
|
-
{ id: "embedlabs.tools.common.e2fsprogs", version: "^1.0.0", roles: ["userdata-image"] }
|
|
95
|
+
{ 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"] }
|
|
78
98
|
],
|
|
79
99
|
build_modes: ["local-llvm"]
|
|
80
100
|
}
|
|
@@ -83,12 +103,23 @@ const INSTALL_COPY_PATHS = [
|
|
|
83
103
|
"toolchain/llvm-cross",
|
|
84
104
|
"toolchain/host",
|
|
85
105
|
"toolchain/host-tools",
|
|
106
|
+
"toolchain/qt6-rk3566-llvm-toolchain.cmake",
|
|
86
107
|
"qt-target/qt6-rk3566-llvm-6.8.3",
|
|
108
|
+
"qt-host/qt6-host-macos-6.8.3",
|
|
87
109
|
"tools/mac",
|
|
110
|
+
"toolkit-runtime/qtquick-live-preview",
|
|
111
|
+
"toolkit-runtime/rp2350-monitor",
|
|
112
|
+
"toolkit-runtime/RP2350-Monitor",
|
|
88
113
|
"images/current",
|
|
89
114
|
"userdata/rootfs",
|
|
90
|
-
"boot-workspace
|
|
115
|
+
"boot-workspace",
|
|
116
|
+
"README.md",
|
|
117
|
+
"meta",
|
|
118
|
+
"scripts",
|
|
119
|
+
"support",
|
|
120
|
+
"third_party"
|
|
91
121
|
];
|
|
122
|
+
const LOCAL_TOOLCHAIN_INSTALL_MODES = ["minimal", "compile", "qt", "full", "images"];
|
|
92
123
|
export function defaultLocalReleaseRoot() {
|
|
93
124
|
return process.env.EMBEDLABS_LOCAL_RELEASE_ROOT?.trim()
|
|
94
125
|
|| process.env.EMBEDLABS_RELEASE_ROOT?.trim()
|
|
@@ -121,7 +152,7 @@ export async function latestLocalToolchain(options = {}) {
|
|
|
121
152
|
board_id: boardId,
|
|
122
153
|
channel: channel.channel,
|
|
123
154
|
host: hostId(),
|
|
124
|
-
version: board.version,
|
|
155
|
+
version: download?.version ?? board.version,
|
|
125
156
|
metadata_root: metadataRoot,
|
|
126
157
|
packages,
|
|
127
158
|
download,
|
|
@@ -138,6 +169,7 @@ export async function currentLocalToolchain(installRoot, boardId = DEFAULT_BOARD
|
|
|
138
169
|
installed: !!releaseRoot,
|
|
139
170
|
board_id: typeof registry.board_id === "string" ? registry.board_id : boardId,
|
|
140
171
|
version: typeof registry.version === "string" ? registry.version : undefined,
|
|
172
|
+
mode: typeof registry.mode === "string" ? registry.mode : undefined,
|
|
141
173
|
release_root: releaseRoot,
|
|
142
174
|
registry_path: registryPath,
|
|
143
175
|
install_root: root,
|
|
@@ -158,30 +190,42 @@ export async function installLocalToolchain(options = {}) {
|
|
|
158
190
|
const latest = await latestLocalToolchain(options);
|
|
159
191
|
const installRoot = resolveInstallRoot(options.installRoot);
|
|
160
192
|
const releaseRoot = resolve(installRoot, "toolchains", latest.board_id, latest.version);
|
|
193
|
+
const installMode = normalizeLocalToolchainInstallMode(options.mode ?? latest.download?.default_mode);
|
|
161
194
|
if (await pathExists(releaseRoot) && !options.force) {
|
|
162
|
-
const validation = await validateLocalToolchain(releaseRoot);
|
|
195
|
+
const validation = await validateLocalToolchain({ releaseRoot, mode: installMode });
|
|
163
196
|
if (!validation.ok) {
|
|
164
|
-
|
|
197
|
+
if (latest.download?.components?.length) {
|
|
198
|
+
// Component installs can upgrade an existing lower-mode install by overlaying
|
|
199
|
+
// only the newly selected components instead of deleting the whole tree.
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
throw new Error(`Existing local toolchain is incomplete at ${releaseRoot}; rerun with --force.`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
await writeCurrentRegistry(installRoot, latest, releaseRoot, installMode);
|
|
207
|
+
return {
|
|
208
|
+
board_id: latest.board_id,
|
|
209
|
+
version: latest.version,
|
|
210
|
+
channel: latest.channel,
|
|
211
|
+
host: latest.host,
|
|
212
|
+
mode: installMode,
|
|
213
|
+
install_root: installRoot,
|
|
214
|
+
release_root: releaseRoot,
|
|
215
|
+
registry_path: localToolchainRegistryPath(installRoot),
|
|
216
|
+
source: { kind: "directory", value: releaseRoot },
|
|
217
|
+
installed_paths: [],
|
|
218
|
+
packages: latest.packages,
|
|
219
|
+
validation
|
|
220
|
+
};
|
|
165
221
|
}
|
|
166
|
-
await writeCurrentRegistry(installRoot, latest, releaseRoot);
|
|
167
|
-
return {
|
|
168
|
-
board_id: latest.board_id,
|
|
169
|
-
version: latest.version,
|
|
170
|
-
channel: latest.channel,
|
|
171
|
-
host: latest.host,
|
|
172
|
-
install_root: installRoot,
|
|
173
|
-
release_root: releaseRoot,
|
|
174
|
-
registry_path: localToolchainRegistryPath(installRoot),
|
|
175
|
-
source: { kind: "directory", value: releaseRoot },
|
|
176
|
-
installed_paths: [],
|
|
177
|
-
packages: latest.packages,
|
|
178
|
-
validation
|
|
179
|
-
};
|
|
180
222
|
}
|
|
181
223
|
const tempDir = await mkdtemp(join(tmpdir(), "embedlabs-local-toolchain-install-"));
|
|
182
224
|
try {
|
|
183
225
|
const sourceRoot = await sourceReleaseRootForInstall(options, latest, installRoot, tempDir);
|
|
184
|
-
await
|
|
226
|
+
if (options.force || !await pathExists(releaseRoot) || sourceRoot.source.kind !== "components") {
|
|
227
|
+
await rm(releaseRoot, { recursive: true, force: true });
|
|
228
|
+
}
|
|
185
229
|
await mkdir(releaseRoot, { recursive: true });
|
|
186
230
|
const installedPaths = [];
|
|
187
231
|
for (const relativePath of INSTALL_COPY_PATHS) {
|
|
@@ -199,8 +243,8 @@ export async function installLocalToolchain(options = {}) {
|
|
|
199
243
|
});
|
|
200
244
|
installedPaths.push(relativePath);
|
|
201
245
|
}
|
|
202
|
-
await writeCurrentRegistry(installRoot, latest, releaseRoot);
|
|
203
|
-
const validation = await validateLocalToolchain(releaseRoot);
|
|
246
|
+
await writeCurrentRegistry(installRoot, latest, releaseRoot, installMode, sourceRoot.source);
|
|
247
|
+
const validation = await validateLocalToolchain({ releaseRoot, mode: installMode });
|
|
204
248
|
if (!validation.ok) {
|
|
205
249
|
throw new Error(`Installed local toolchain is incomplete: ${validation.missing_paths.join(", ")}`);
|
|
206
250
|
}
|
|
@@ -209,6 +253,7 @@ export async function installLocalToolchain(options = {}) {
|
|
|
209
253
|
version: latest.version,
|
|
210
254
|
channel: latest.channel,
|
|
211
255
|
host: latest.host,
|
|
256
|
+
mode: installMode,
|
|
212
257
|
install_root: installRoot,
|
|
213
258
|
release_root: releaseRoot,
|
|
214
259
|
registry_path: localToolchainRegistryPath(installRoot),
|
|
@@ -222,23 +267,11 @@ export async function installLocalToolchain(options = {}) {
|
|
|
222
267
|
await rm(tempDir, { recursive: true, force: true });
|
|
223
268
|
}
|
|
224
269
|
}
|
|
225
|
-
export async function validateLocalToolchain(
|
|
270
|
+
export async function validateLocalToolchain(input) {
|
|
271
|
+
const releaseRoot = typeof input === "string" ? input : input?.releaseRoot;
|
|
272
|
+
const mode = normalizeLocalToolchainInstallMode(typeof input === "string" ? undefined : input?.mode);
|
|
226
273
|
const resolvedRoot = await resolveLocalReleaseRoot(releaseRoot);
|
|
227
|
-
const required =
|
|
228
|
-
["release root", "."],
|
|
229
|
-
["C compiler", "toolchain/llvm-cross/bin/aarch64-linux-gnu-gcc"],
|
|
230
|
-
["C++ compiler", "toolchain/llvm-cross/bin/aarch64-linux-gnu-g++"],
|
|
231
|
-
["readelf", "toolchain/llvm-cross/bin/aarch64-linux-gnu-readelf"],
|
|
232
|
-
["host clang wrapper", "toolchain/host-tools/bin/clang-aarch64-linux-gnu"],
|
|
233
|
-
["host GCC libraries", "toolchain/host/lib/gcc"],
|
|
234
|
-
["Qt CMake", "qt-target/qt6-rk3566-llvm-6.8.3/bin/qt-cmake"],
|
|
235
|
-
["target sysroot", "toolchain/host/aarch64-buildroot-linux-gnu/sysroot"],
|
|
236
|
-
["Qt target libraries", "qt-target/qt6-rk3566-llvm-6.8.3/lib"],
|
|
237
|
-
["Rockchip mkimage", "tools/mac/mkimage"],
|
|
238
|
-
["Rockchip dumpimage", "tools/mac/dumpimage"],
|
|
239
|
-
["Rockchip resource_tool", "tools/mac/resource_tool"],
|
|
240
|
-
["Rockchip rkdeveloptool", "tools/mac/rkdeveloptool"]
|
|
241
|
-
];
|
|
274
|
+
const required = requiredLocalToolchainChecks(mode);
|
|
242
275
|
const checked_paths = [];
|
|
243
276
|
for (const [label, relativePath] of required) {
|
|
244
277
|
const absolutePath = resolve(resolvedRoot, relativePath);
|
|
@@ -251,6 +284,7 @@ export async function validateLocalToolchain(releaseRoot) {
|
|
|
251
284
|
const missing_paths = checked_paths.filter((item) => !item.exists).map((item) => item.path);
|
|
252
285
|
return {
|
|
253
286
|
ok: missing_paths.length === 0,
|
|
287
|
+
mode,
|
|
254
288
|
host: {
|
|
255
289
|
platform: platform(),
|
|
256
290
|
arch: arch()
|
|
@@ -261,10 +295,71 @@ export async function validateLocalToolchain(releaseRoot) {
|
|
|
261
295
|
missing_paths,
|
|
262
296
|
notes: [
|
|
263
297
|
"Local build commands require an Embed Labs auth token so local resource use remains account attributable.",
|
|
264
|
-
|
|
298
|
+
`This validator checks the TaishanPi LLVM local support layout for install mode ${mode}.`
|
|
265
299
|
]
|
|
266
300
|
};
|
|
267
301
|
}
|
|
302
|
+
function normalizeLocalToolchainInstallMode(mode) {
|
|
303
|
+
const normalized = mode?.trim();
|
|
304
|
+
if (!normalized) {
|
|
305
|
+
return "qt";
|
|
306
|
+
}
|
|
307
|
+
if (LOCAL_TOOLCHAIN_INSTALL_MODES.includes(normalized)) {
|
|
308
|
+
return normalized;
|
|
309
|
+
}
|
|
310
|
+
throw new Error(`Unsupported local toolchain install mode ${normalized}; expected ${LOCAL_TOOLCHAIN_INSTALL_MODES.join(", ")}.`);
|
|
311
|
+
}
|
|
312
|
+
function requiredLocalToolchainChecks(mode) {
|
|
313
|
+
const base = [
|
|
314
|
+
["release root", "."],
|
|
315
|
+
["Rockchip mkimage", "tools/mac/mkimage"],
|
|
316
|
+
["Rockchip dumpimage", "tools/mac/dumpimage"],
|
|
317
|
+
["Rockchip resource_tool", "tools/mac/resource_tool"],
|
|
318
|
+
["Rockchip rkdeveloptool", "tools/mac/rkdeveloptool"],
|
|
319
|
+
["boot resource image", "boot-workspace/out/resource.img"],
|
|
320
|
+
["boot image", "boot-workspace/out/boot.img"],
|
|
321
|
+
["boot DTB", "boot-workspace/out/tspi-rk3566-user-v10-linux.dtb"],
|
|
322
|
+
["RP2350 Monitor CLI", "toolkit-runtime/rp2350-monitor/tools/rpmon_cli.py"],
|
|
323
|
+
["RP2350 Monitor logic analyzer", "toolkit-runtime/rp2350-monitor/ui/bin/embed-labs-logic-analyzer"],
|
|
324
|
+
["package metadata", "meta"]
|
|
325
|
+
];
|
|
326
|
+
const compile = [
|
|
327
|
+
["C compiler", "toolchain/llvm-cross/bin/aarch64-linux-gnu-gcc"],
|
|
328
|
+
["C++ compiler", "toolchain/llvm-cross/bin/aarch64-linux-gnu-g++"],
|
|
329
|
+
["readelf", "toolchain/llvm-cross/bin/aarch64-linux-gnu-readelf"],
|
|
330
|
+
["host clang wrapper", "toolchain/host-tools/bin/clang-aarch64-linux-gnu"],
|
|
331
|
+
["host GCC libraries", "toolchain/host/lib/gcc"],
|
|
332
|
+
["target sysroot", "toolchain/host/aarch64-buildroot-linux-gnu/sysroot"],
|
|
333
|
+
["target include directory", "toolchain/host/aarch64-buildroot-linux-gnu/include"]
|
|
334
|
+
];
|
|
335
|
+
const qt = [
|
|
336
|
+
["Qt CMake", "qt-target/qt6-rk3566-llvm-6.8.3/bin/qt-cmake"],
|
|
337
|
+
["Qt target libraries", "qt-target/qt6-rk3566-llvm-6.8.3/lib"],
|
|
338
|
+
["Qt host tools", "qt-host/qt6-host-macos-6.8.3"],
|
|
339
|
+
["QtQuick live preview", "toolkit-runtime/qtquick-live-preview/bin/embed-qml-live-preview"]
|
|
340
|
+
];
|
|
341
|
+
const images = [
|
|
342
|
+
["base boot image", "images/current/boot.img"],
|
|
343
|
+
["base rootfs image", "images/current/rootfs.img"],
|
|
344
|
+
["base image parameter", "images/current/parameter.txt"]
|
|
345
|
+
];
|
|
346
|
+
const full = [
|
|
347
|
+
["rootfs overlay", "userdata/rootfs"]
|
|
348
|
+
];
|
|
349
|
+
if (mode === "minimal") {
|
|
350
|
+
return base;
|
|
351
|
+
}
|
|
352
|
+
if (mode === "compile") {
|
|
353
|
+
return [...base, ...compile];
|
|
354
|
+
}
|
|
355
|
+
if (mode === "qt") {
|
|
356
|
+
return [...base, ...compile, ...qt];
|
|
357
|
+
}
|
|
358
|
+
if (mode === "images") {
|
|
359
|
+
return [...base, ...images];
|
|
360
|
+
}
|
|
361
|
+
return [...base, ...compile, ...qt, ...images, ...full];
|
|
362
|
+
}
|
|
268
363
|
export async function compileTaishanPiSingleFile(options) {
|
|
269
364
|
assertAuthenticated(options.auth);
|
|
270
365
|
const releaseRoot = await resolveLocalReleaseRoot(options.releaseRoot);
|
|
@@ -374,7 +469,7 @@ async function resolveLocalToolchainDownloadPlan(input) {
|
|
|
374
469
|
return item.board_id === input.boardId
|
|
375
470
|
&& item.host === input.host
|
|
376
471
|
&& item.toolchain === input.toolchain
|
|
377
|
-
&& (item.kind === undefined || item.kind === "toolchain-archive");
|
|
472
|
+
&& (item.kind === undefined || item.kind === "toolchain-archive" || item.kind === "board-support-archive");
|
|
378
473
|
});
|
|
379
474
|
if (!entry?.manifest) {
|
|
380
475
|
return undefined;
|
|
@@ -387,20 +482,24 @@ async function resolveLocalToolchainDownloadPlan(input) {
|
|
|
387
482
|
if (manifest.board_id !== input.boardId || manifest.host !== input.host || manifest.toolchain !== input.toolchain) {
|
|
388
483
|
throw new Error(`Download manifest does not match requested ${input.boardId}/${input.host}/${input.toolchain}.`);
|
|
389
484
|
}
|
|
390
|
-
if (!manifest.archive?.file || !manifest.archive.sha256 || !Number.isFinite(manifest.archive.size_bytes))
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
485
|
+
if ((!manifest.archive?.file || !manifest.archive.sha256 || !Number.isFinite(manifest.archive.size_bytes))
|
|
486
|
+
&& (!Array.isArray(manifest.components) || manifest.components.length === 0)) {
|
|
487
|
+
throw new Error(`Download manifest ${manifest.id}@${manifest.version} is missing archive/components metadata.`);
|
|
488
|
+
}
|
|
489
|
+
const mirrors = manifest.archive
|
|
490
|
+
? orderDownloadMirrors((manifest.mirrors ?? [])
|
|
491
|
+
.filter((mirror) => mirror.enabled !== false && typeof mirror.url === "string" && mirror.url.length > 0)
|
|
492
|
+
.map((mirror) => ({
|
|
493
|
+
kind: mirror.kind || "unknown",
|
|
494
|
+
enabled: mirror.enabled !== false,
|
|
495
|
+
url: mirror.url,
|
|
496
|
+
sha256: typeof mirror.sha256 === "string" ? mirror.sha256 : manifest.archive?.sha256,
|
|
497
|
+
size_bytes: Number.isFinite(mirror.size_bytes) ? mirror.size_bytes : manifest.archive?.size_bytes
|
|
498
|
+
})), manifest.download_policy?.preferred_order)
|
|
499
|
+
: [];
|
|
500
|
+
const components = (manifest.components ?? []).map((component) => normalizeDownloadComponent(component, manifest, manifestUrl));
|
|
402
501
|
const first = mirrors[0];
|
|
403
|
-
if (!first) {
|
|
502
|
+
if (!first && components.length === 0) {
|
|
404
503
|
return undefined;
|
|
405
504
|
}
|
|
406
505
|
return {
|
|
@@ -411,14 +510,52 @@ async function resolveLocalToolchainDownloadPlan(input) {
|
|
|
411
510
|
board_id: input.boardId,
|
|
412
511
|
host: input.host,
|
|
413
512
|
toolchain: input.toolchain,
|
|
414
|
-
source_url: first
|
|
415
|
-
mirror_kind: first
|
|
416
|
-
archive: {
|
|
513
|
+
source_url: first?.url,
|
|
514
|
+
mirror_kind: first?.kind,
|
|
515
|
+
archive: manifest.archive ? {
|
|
417
516
|
file: manifest.archive.file,
|
|
418
517
|
size_bytes: manifest.archive.size_bytes,
|
|
419
518
|
sha256: manifest.archive.sha256,
|
|
420
519
|
content_type: manifest.archive.content_type
|
|
520
|
+
} : undefined,
|
|
521
|
+
mirrors,
|
|
522
|
+
components: components.length > 0 ? components : undefined,
|
|
523
|
+
default_mode: manifest.download_policy?.default_mode
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
function normalizeDownloadComponent(component, manifest, manifestUrl) {
|
|
527
|
+
if (!component.id || !component.version) {
|
|
528
|
+
throw new Error(`Download manifest ${manifest.id}@${manifest.version} contains a component without id/version.`);
|
|
529
|
+
}
|
|
530
|
+
if (!component.archive?.file || !component.archive.sha256 || !Number.isFinite(component.archive.size_bytes)) {
|
|
531
|
+
throw new Error(`Download manifest ${manifest.id}@${manifest.version} component ${component.id} is missing archive metadata.`);
|
|
532
|
+
}
|
|
533
|
+
const mirrors = orderDownloadMirrors((component.mirrors ?? [])
|
|
534
|
+
.filter((mirror) => mirror.enabled !== false && typeof mirror.url === "string" && mirror.url.length > 0)
|
|
535
|
+
.map((mirror) => ({
|
|
536
|
+
kind: mirror.kind || "unknown",
|
|
537
|
+
enabled: mirror.enabled !== false,
|
|
538
|
+
url: new URL(mirror.url, manifestUrl).toString(),
|
|
539
|
+
sha256: typeof mirror.sha256 === "string" ? mirror.sha256 : component.archive?.sha256,
|
|
540
|
+
size_bytes: Number.isFinite(mirror.size_bytes) ? mirror.size_bytes : component.archive?.size_bytes
|
|
541
|
+
})), manifest.download_policy?.preferred_order);
|
|
542
|
+
const first = mirrors[0];
|
|
543
|
+
if (!first) {
|
|
544
|
+
throw new Error(`Download manifest ${manifest.id}@${manifest.version} component ${component.id} has no enabled mirrors.`);
|
|
545
|
+
}
|
|
546
|
+
return {
|
|
547
|
+
id: component.id,
|
|
548
|
+
version: component.version,
|
|
549
|
+
role: component.role,
|
|
550
|
+
install_modes: Array.isArray(component.install_modes) ? component.install_modes : undefined,
|
|
551
|
+
archive: {
|
|
552
|
+
file: component.archive.file,
|
|
553
|
+
size_bytes: component.archive.size_bytes,
|
|
554
|
+
sha256: component.archive.sha256,
|
|
555
|
+
content_type: component.archive.content_type
|
|
421
556
|
},
|
|
557
|
+
source_url: first.url,
|
|
558
|
+
mirror_kind: first.kind,
|
|
422
559
|
mirrors
|
|
423
560
|
};
|
|
424
561
|
}
|
|
@@ -502,7 +639,7 @@ function resolveInstallRoot(installRoot) {
|
|
|
502
639
|
function localToolchainRegistryPath(installRoot) {
|
|
503
640
|
return join(installRoot, "registry", "local-toolchains.json");
|
|
504
641
|
}
|
|
505
|
-
async function writeCurrentRegistry(installRoot, latest, releaseRoot) {
|
|
642
|
+
async function writeCurrentRegistry(installRoot, latest, releaseRoot, mode, source) {
|
|
506
643
|
const registryPath = localToolchainRegistryPath(installRoot);
|
|
507
644
|
await mkdir(dirname(registryPath), { recursive: true });
|
|
508
645
|
await writeFile(registryPath, `${JSON.stringify({
|
|
@@ -511,8 +648,11 @@ async function writeCurrentRegistry(installRoot, latest, releaseRoot) {
|
|
|
511
648
|
version: latest.version,
|
|
512
649
|
channel: latest.channel,
|
|
513
650
|
host: latest.host,
|
|
651
|
+
mode,
|
|
514
652
|
release_root: releaseRoot,
|
|
515
653
|
packages: latest.packages,
|
|
654
|
+
source,
|
|
655
|
+
installed_components: source?.components,
|
|
516
656
|
updated_at: new Date().toISOString()
|
|
517
657
|
}, null, 2)}\n`, "utf8");
|
|
518
658
|
}
|
|
@@ -550,12 +690,18 @@ async function sourceReleaseRootForInstall(options, latest, installRoot, tempDir
|
|
|
550
690
|
source: { kind: "url", value: options.sourceUrl, downloaded_path: downloadedPath }
|
|
551
691
|
};
|
|
552
692
|
}
|
|
693
|
+
if (latest.download?.components?.length) {
|
|
694
|
+
return await componentSourceRootForInstall(options, latest.download, installRoot, tempDir);
|
|
695
|
+
}
|
|
553
696
|
if (latest.download) {
|
|
554
697
|
const failures = [];
|
|
555
698
|
for (const mirror of latest.download.mirrors) {
|
|
556
699
|
if (!mirror.enabled) {
|
|
557
700
|
continue;
|
|
558
701
|
}
|
|
702
|
+
if (!latest.download.archive) {
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
559
705
|
const extractRoot = join(tempDir, `extract-${safeFileToken(mirror.kind)}`);
|
|
560
706
|
try {
|
|
561
707
|
const downloadedPath = await downloadToolchainArchive(mirror.url, installRoot, {
|
|
@@ -596,6 +742,71 @@ async function sourceReleaseRootForInstall(options, latest, installRoot, tempDir
|
|
|
596
742
|
}
|
|
597
743
|
throw new Error("Local toolchain install could not resolve a source. Pass --source-url <tar.gz>, --source-release-root <path>, or configure EMBED_DOWNLOAD_BASE_URL.");
|
|
598
744
|
}
|
|
745
|
+
async function componentSourceRootForInstall(options, download, installRoot, tempDir) {
|
|
746
|
+
const mode = normalizeLocalToolchainInstallMode(options.mode ?? download.default_mode);
|
|
747
|
+
const components = selectedDownloadComponents(download.components ?? [], mode);
|
|
748
|
+
if (components.length === 0) {
|
|
749
|
+
throw new Error(`No local toolchain components selected for mode ${mode}.`);
|
|
750
|
+
}
|
|
751
|
+
const extractRoot = join(tempDir, "extract-components");
|
|
752
|
+
await mkdir(extractRoot, { recursive: true });
|
|
753
|
+
const installedComponents = [];
|
|
754
|
+
const failures = [];
|
|
755
|
+
for (const component of components) {
|
|
756
|
+
let installed = false;
|
|
757
|
+
for (const mirror of component.mirrors) {
|
|
758
|
+
if (!mirror.enabled) {
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
try {
|
|
762
|
+
const downloadedPath = await downloadToolchainArchive(mirror.url, installRoot, {
|
|
763
|
+
sha256: mirror.sha256 ?? component.archive.sha256,
|
|
764
|
+
size_bytes: mirror.size_bytes ?? component.archive.size_bytes
|
|
765
|
+
});
|
|
766
|
+
const extracted = await runCommand(["tar", "-xzf", downloadedPath, "-C", extractRoot], tempDir);
|
|
767
|
+
if (extracted.exit_code !== 0) {
|
|
768
|
+
throw new Error(`Could not extract component ${component.id}: ${extracted.stderr_tail.join("\n")}`);
|
|
769
|
+
}
|
|
770
|
+
installedComponents.push({
|
|
771
|
+
id: component.id,
|
|
772
|
+
version: component.version,
|
|
773
|
+
role: component.role,
|
|
774
|
+
archive_file: component.archive.file,
|
|
775
|
+
mirror_kind: mirror.kind,
|
|
776
|
+
downloaded_path: downloadedPath,
|
|
777
|
+
size_bytes: mirror.size_bytes ?? component.archive.size_bytes,
|
|
778
|
+
sha256: mirror.sha256 ?? component.archive.sha256
|
|
779
|
+
});
|
|
780
|
+
installed = true;
|
|
781
|
+
break;
|
|
782
|
+
}
|
|
783
|
+
catch (error) {
|
|
784
|
+
failures.push(`${component.id}@${component.version}/${mirror.kind}: ${error instanceof Error ? error.message : String(error)}`);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
if (!installed) {
|
|
788
|
+
throw new Error(`Could not install component ${component.id}@${component.version}: ${failures.join("; ")}`);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
return {
|
|
792
|
+
path: extractRoot,
|
|
793
|
+
source: {
|
|
794
|
+
kind: "components",
|
|
795
|
+
value: download.manifest_url,
|
|
796
|
+
mirror_kind: "components",
|
|
797
|
+
size_bytes: installedComponents.reduce((total, component) => total + component.size_bytes, 0),
|
|
798
|
+
components: installedComponents
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
function selectedDownloadComponents(components, mode) {
|
|
803
|
+
return components.filter((component) => {
|
|
804
|
+
if (!component.install_modes?.length) {
|
|
805
|
+
return true;
|
|
806
|
+
}
|
|
807
|
+
return component.install_modes.includes(mode);
|
|
808
|
+
});
|
|
809
|
+
}
|
|
599
810
|
async function downloadToolchainArchive(sourceUrl, installRoot, expected) {
|
|
600
811
|
const downloadsDir = join(installRoot, "cache", "downloads");
|
|
601
812
|
await mkdir(downloadsDir, { recursive: true });
|