@oniroproject/core 0.7.0 → 0.8.0
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 +18 -6
- package/dist/index.cjs +717 -390
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +211 -16
- package/dist/index.d.ts +211 -16
- package/dist/index.js +698 -390
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -105,9 +105,9 @@ var CmdToolsNotInstalledError = class extends OniroError {
|
|
|
105
105
|
expectedPath;
|
|
106
106
|
};
|
|
107
107
|
var UnsupportedPlatformError = class extends OniroError {
|
|
108
|
-
constructor(
|
|
109
|
-
super(`Unsupported platform: ${
|
|
110
|
-
this.platform =
|
|
108
|
+
constructor(platform7) {
|
|
109
|
+
super(`Unsupported platform: ${platform7}.`);
|
|
110
|
+
this.platform = platform7;
|
|
111
111
|
this.name = "UnsupportedPlatformError";
|
|
112
112
|
}
|
|
113
113
|
platform;
|
|
@@ -122,6 +122,14 @@ var ChecksumMismatchError = class extends OniroError {
|
|
|
122
122
|
expected;
|
|
123
123
|
actual;
|
|
124
124
|
};
|
|
125
|
+
var InsufficientSpaceError = class extends OniroError {
|
|
126
|
+
constructor(message, dir, cause) {
|
|
127
|
+
super(message, cause);
|
|
128
|
+
this.dir = dir;
|
|
129
|
+
this.name = "InsufficientSpaceError";
|
|
130
|
+
}
|
|
131
|
+
dir;
|
|
132
|
+
};
|
|
125
133
|
var CancelledError = class extends OniroError {
|
|
126
134
|
constructor(message = "Operation cancelled.") {
|
|
127
135
|
super(message);
|
|
@@ -168,29 +176,29 @@ var OHOS_URL_BASE = "https://repo.huaweicloud.com/openharmony/os";
|
|
|
168
176
|
// src/sdk/platform.ts
|
|
169
177
|
import * as os2 from "os";
|
|
170
178
|
function getOsFolder() {
|
|
171
|
-
const
|
|
172
|
-
if (
|
|
173
|
-
if (
|
|
174
|
-
if (
|
|
175
|
-
throw new UnsupportedPlatformError(
|
|
179
|
+
const platform7 = os2.platform();
|
|
180
|
+
if (platform7 === "linux") return "linux";
|
|
181
|
+
if (platform7 === "darwin") return "darwin";
|
|
182
|
+
if (platform7 === "win32") return "windows";
|
|
183
|
+
throw new UnsupportedPlatformError(platform7);
|
|
176
184
|
}
|
|
177
185
|
function getSdkFilename(version) {
|
|
178
|
-
const
|
|
186
|
+
const platform7 = os2.platform();
|
|
179
187
|
const v = version ?? ALL_SDKS[ALL_SDKS.length - 1].version;
|
|
180
188
|
const strips = ALL_SDKS.find((r) => r.version === v)?.tarballStrip ?? { linuxWindows: 1, darwin: 3 };
|
|
181
|
-
if (
|
|
189
|
+
if (platform7 === "linux") {
|
|
182
190
|
return { filename: "ohos-sdk-windows_linux-public.tar.gz", osFolder: "linux", strip: strips.linuxWindows };
|
|
183
191
|
}
|
|
184
|
-
if (
|
|
192
|
+
if (platform7 === "darwin") {
|
|
185
193
|
if (os2.arch() === "arm64") {
|
|
186
194
|
return { filename: "L2-SDK-MAC-M1-PUBLIC.tar.gz", osFolder: "darwin", strip: strips.darwin };
|
|
187
195
|
}
|
|
188
196
|
return { filename: "ohos-sdk-mac-public.tar.gz", osFolder: "darwin", strip: strips.darwin };
|
|
189
197
|
}
|
|
190
|
-
if (
|
|
198
|
+
if (platform7 === "win32") {
|
|
191
199
|
return { filename: "ohos-sdk-windows_linux-public.tar.gz", osFolder: "windows", strip: strips.linuxWindows };
|
|
192
200
|
}
|
|
193
|
-
throw new UnsupportedPlatformError(
|
|
201
|
+
throw new UnsupportedPlatformError(platform7);
|
|
194
202
|
}
|
|
195
203
|
|
|
196
204
|
// src/sdk/paths.ts
|
|
@@ -231,6 +239,8 @@ function getOhpmPath(config) {
|
|
|
231
239
|
return getCmdToolsBin(config, "ohpm");
|
|
232
240
|
}
|
|
233
241
|
function getHdcPath(config) {
|
|
242
|
+
const override = config.get("hdcPath", "");
|
|
243
|
+
if (override) return override;
|
|
234
244
|
const base = path2.join(getCmdToolsPath(config), "sdk", "default", "openharmony", "toolchains");
|
|
235
245
|
if (os3.platform() === "win32") {
|
|
236
246
|
return pickExisting([
|
|
@@ -281,8 +291,164 @@ function detectProjectSdkVersion(projectRoot, logger = noopLogger) {
|
|
|
281
291
|
}
|
|
282
292
|
}
|
|
283
293
|
|
|
284
|
-
// src/sdk/
|
|
294
|
+
// src/sdk/tmp.ts
|
|
285
295
|
import * as fs3 from "fs";
|
|
296
|
+
import * as os4 from "os";
|
|
297
|
+
import * as path4 from "path";
|
|
298
|
+
var INSTALL_TMP_DIRNAME = ".oniro-tmp";
|
|
299
|
+
var TMPFS_MAGIC = 16914836;
|
|
300
|
+
var RAMFS_MAGIC = 2240043254;
|
|
301
|
+
var HINT_EXAMPLE = os4.platform() === "win32" ? "D:\\oniro-tmp" : "~/.cache/oniro-tmp";
|
|
302
|
+
var TMP_DIR_HINT = `Point the scratch directory at a filesystem with more room using --tmp-dir <path> (or the ONIRO_TMP_DIR environment variable), e.g. --tmp-dir ${HINT_EXAMPLE}.`;
|
|
303
|
+
function resolveTmpRoot(config, override, installRoot) {
|
|
304
|
+
const explicit = (override ?? "").trim();
|
|
305
|
+
if (explicit) return path4.resolve(explicit);
|
|
306
|
+
const configured = config.get("tmpDir", "").trim();
|
|
307
|
+
if (configured) return path4.resolve(configured);
|
|
308
|
+
if (installRoot) return path4.join(path4.resolve(installRoot), INSTALL_TMP_DIRNAME);
|
|
309
|
+
return os4.tmpdir();
|
|
310
|
+
}
|
|
311
|
+
function createTempWorkDir(root, prefix) {
|
|
312
|
+
try {
|
|
313
|
+
fs3.mkdirSync(root, { recursive: true });
|
|
314
|
+
return fs3.mkdtempSync(path4.join(root, prefix));
|
|
315
|
+
} catch (err) {
|
|
316
|
+
if (isOutOfSpaceError(err)) throw toSpaceError(err, root, "a temporary directory");
|
|
317
|
+
throw new OniroError(
|
|
318
|
+
`Cannot create a temporary directory in '${root}': ${err instanceof Error ? err.message : String(err)}
|
|
319
|
+
${TMP_DIR_HINT}`,
|
|
320
|
+
err
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function removeTempWorkDir(dir, root) {
|
|
325
|
+
fs3.rmSync(dir, { recursive: true, force: true });
|
|
326
|
+
if (!root || path4.basename(root) !== INSTALL_TMP_DIRNAME) return;
|
|
327
|
+
try {
|
|
328
|
+
fs3.rmdirSync(root);
|
|
329
|
+
} catch {
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function createInstallTempDir(opts) {
|
|
333
|
+
const requested = (opts.override ?? "").trim() || opts.config.get("tmpDir", "").trim();
|
|
334
|
+
const root = resolveTmpRoot(opts.config, opts.override, opts.installRoot);
|
|
335
|
+
try {
|
|
336
|
+
return { dir: createTempWorkDir(root, opts.prefix), root };
|
|
337
|
+
} catch (err) {
|
|
338
|
+
const fallback = os4.tmpdir();
|
|
339
|
+
if (requested || isOutOfSpaceError(err) || path4.resolve(fallback) === path4.resolve(root)) throw err;
|
|
340
|
+
const reason = err instanceof Error ? err.message.split("\n")[0] : String(err);
|
|
341
|
+
(opts.logger ?? noopLogger).warn(`${reason} Falling back to '${fallback}'.`);
|
|
342
|
+
return { dir: createTempWorkDir(fallback, opts.prefix), root: fallback };
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
function nearestExisting(dir) {
|
|
346
|
+
let current = path4.resolve(dir);
|
|
347
|
+
for (; ; ) {
|
|
348
|
+
if (fs3.existsSync(current)) return current;
|
|
349
|
+
const parent = path4.dirname(current);
|
|
350
|
+
if (parent === current) return null;
|
|
351
|
+
current = parent;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
function getFreeSpaceBytes(dir) {
|
|
355
|
+
const target = nearestExisting(dir);
|
|
356
|
+
if (!target) return null;
|
|
357
|
+
try {
|
|
358
|
+
const stats = fs3.statfsSync(target);
|
|
359
|
+
return Number(stats.bsize) * Number(stats.bavail);
|
|
360
|
+
} catch {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function isRamBackedPath(dir) {
|
|
365
|
+
const target = nearestExisting(dir);
|
|
366
|
+
if (!target) return false;
|
|
367
|
+
try {
|
|
368
|
+
const type = Number(fs3.statfsSync(target).type);
|
|
369
|
+
return type === TMPFS_MAGIC || type === RAMFS_MAGIC;
|
|
370
|
+
} catch {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function formatBytes(bytes) {
|
|
375
|
+
const units = ["B", "KiB", "MiB", "GiB", "TiB"];
|
|
376
|
+
let value = Math.max(0, bytes);
|
|
377
|
+
let unit = 0;
|
|
378
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
379
|
+
value /= 1024;
|
|
380
|
+
unit++;
|
|
381
|
+
}
|
|
382
|
+
return `${value >= 10 || unit === 0 ? Math.round(value) : value.toFixed(1)} ${units[unit]}`;
|
|
383
|
+
}
|
|
384
|
+
function isOutOfSpaceError(err) {
|
|
385
|
+
if (err instanceof InsufficientSpaceError) return true;
|
|
386
|
+
const seen = /* @__PURE__ */ new Set();
|
|
387
|
+
let current = err;
|
|
388
|
+
while (current && typeof current === "object" && !seen.has(current)) {
|
|
389
|
+
seen.add(current);
|
|
390
|
+
const e = current;
|
|
391
|
+
if (e.code === "ENOSPC" || e.code === "EDQUOT") return true;
|
|
392
|
+
if (typeof e.message === "string" && /ENOSPC|EDQUOT|no space left on device/i.test(e.message)) return true;
|
|
393
|
+
current = e.cause;
|
|
394
|
+
}
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
function spaceMessage(dir, what, opts) {
|
|
398
|
+
const need = typeof opts.requiredBytes === "number" ? `needs about ${formatBytes(opts.requiredBytes)}` : null;
|
|
399
|
+
const free = typeof opts.freeBytes === "number" ? `${formatBytes(opts.freeBytes)} available` : null;
|
|
400
|
+
const amounts = [need, free].filter(Boolean).join(", ");
|
|
401
|
+
const lines = [`Not enough free space in '${dir}' for ${what}${amounts ? ` (${amounts})` : ""}.`];
|
|
402
|
+
if (isRamBackedPath(dir)) {
|
|
403
|
+
lines.push(
|
|
404
|
+
`'${dir}' is on a RAM-backed filesystem (tmpfs), so its capacity is limited by free memory, not by free disk.`
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
if (opts.tmpDirHint) lines.push(TMP_DIR_HINT);
|
|
408
|
+
return lines.join("\n");
|
|
409
|
+
}
|
|
410
|
+
function ensureFreeSpace(dir, requiredBytes, what) {
|
|
411
|
+
if (!Number.isFinite(requiredBytes) || requiredBytes <= 0) return;
|
|
412
|
+
const freeBytes = getFreeSpaceBytes(dir);
|
|
413
|
+
if (freeBytes === null || freeBytes >= requiredBytes) return;
|
|
414
|
+
throw new InsufficientSpaceError(
|
|
415
|
+
spaceMessage(dir, what, { requiredBytes, freeBytes, tmpDirHint: true }),
|
|
416
|
+
dir
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
function errorPath(err) {
|
|
420
|
+
const seen = /* @__PURE__ */ new Set();
|
|
421
|
+
let current = err;
|
|
422
|
+
while (current && typeof current === "object" && !seen.has(current)) {
|
|
423
|
+
seen.add(current);
|
|
424
|
+
const e = current;
|
|
425
|
+
if (typeof e.path === "string" && e.path) return e.path;
|
|
426
|
+
if (typeof e.dest === "string" && e.dest) return e.dest;
|
|
427
|
+
current = e.cause;
|
|
428
|
+
}
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
function isInside(child, parent) {
|
|
432
|
+
const rel = path4.relative(path4.resolve(parent), path4.resolve(child));
|
|
433
|
+
return rel === "" || !rel.startsWith("..") && !path4.isAbsolute(rel);
|
|
434
|
+
}
|
|
435
|
+
function toSpaceError(err, tmpRoot, what) {
|
|
436
|
+
if (err instanceof InsufficientSpaceError) return err;
|
|
437
|
+
if (!isOutOfSpaceError(err)) return err;
|
|
438
|
+
const failed = errorPath(err);
|
|
439
|
+
const failedDir = failed ? path4.dirname(path4.resolve(failed)) : path4.resolve(tmpRoot);
|
|
440
|
+
const inTmp = isInside(failedDir, tmpRoot);
|
|
441
|
+
const dir = inTmp ? path4.resolve(tmpRoot) : failedDir;
|
|
442
|
+
return new InsufficientSpaceError(
|
|
443
|
+
spaceMessage(dir, what, { freeBytes: getFreeSpaceBytes(dir), tmpDirHint: inTmp }),
|
|
444
|
+
dir,
|
|
445
|
+
err
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// src/sdk/download.ts
|
|
450
|
+
import * as fs4 from "fs";
|
|
451
|
+
import * as path5 from "path";
|
|
286
452
|
import * as crypto from "crypto";
|
|
287
453
|
import { pipeline } from "stream";
|
|
288
454
|
import { promisify } from "util";
|
|
@@ -292,36 +458,57 @@ var pipelineAsync = promisify(pipeline);
|
|
|
292
458
|
async function downloadFile(opts) {
|
|
293
459
|
const { url, dest, progress, abortSignal } = opts;
|
|
294
460
|
const proto = url.startsWith("https") ? https : http;
|
|
295
|
-
return new Promise((
|
|
461
|
+
return new Promise((resolve4, reject) => {
|
|
296
462
|
const s = Math.max(0, Math.min(100, opts.start ?? 0));
|
|
297
463
|
const r = Math.max(0, Math.min(100 - s, opts.range ?? 100));
|
|
298
464
|
let settled = false;
|
|
299
465
|
const done = (err) => {
|
|
300
466
|
if (settled) return;
|
|
301
467
|
settled = true;
|
|
302
|
-
err ? reject(err) :
|
|
468
|
+
err ? reject(err) : resolve4();
|
|
303
469
|
};
|
|
304
470
|
if (abortSignal?.aborted) {
|
|
305
471
|
done(new CancelledError("Download cancelled before start."));
|
|
306
472
|
return;
|
|
307
473
|
}
|
|
308
|
-
const
|
|
474
|
+
const destDir = path5.dirname(path5.resolve(dest));
|
|
475
|
+
const what = opts.what ?? `the download of '${url}'`;
|
|
476
|
+
const writeFailed = (err) => isOutOfSpaceError(err) ? toSpaceError(err, opts.tmpRoot ?? destDir, what) : new OniroError(`Error writing '${dest}': ${err instanceof Error ? err.message : String(err)}`, err);
|
|
477
|
+
const file = fs4.createWriteStream(dest);
|
|
478
|
+
let activeResponse;
|
|
479
|
+
const failWith = (err) => {
|
|
480
|
+
file.close(() => fs4.rm(dest, { force: true }, () => done(err)));
|
|
481
|
+
};
|
|
482
|
+
file.on("error", (err) => {
|
|
483
|
+
try {
|
|
484
|
+
activeResponse?.destroy?.();
|
|
485
|
+
} catch {
|
|
486
|
+
}
|
|
487
|
+
failWith(writeFailed(err));
|
|
488
|
+
});
|
|
309
489
|
const req = proto.get(url, (response) => {
|
|
490
|
+
activeResponse = response;
|
|
310
491
|
if (response.statusCode !== 200) {
|
|
311
492
|
try {
|
|
312
493
|
response.destroy();
|
|
313
494
|
} catch {
|
|
314
495
|
}
|
|
315
|
-
|
|
316
|
-
file.close();
|
|
317
|
-
} catch {
|
|
318
|
-
}
|
|
319
|
-
fs3.unlink(dest, () => {
|
|
320
|
-
});
|
|
321
|
-
done(new OniroError(`Failed to download '${url}' (HTTP ${response.statusCode})`));
|
|
496
|
+
failWith(new OniroError(`Failed to download '${url}' (HTTP ${response.statusCode})`));
|
|
322
497
|
return;
|
|
323
498
|
}
|
|
324
499
|
const total = parseInt(response.headers["content-length"] || "0", 10);
|
|
500
|
+
if (total > 0) {
|
|
501
|
+
try {
|
|
502
|
+
ensureFreeSpace(opts.tmpRoot ?? destDir, total, what);
|
|
503
|
+
} catch (err) {
|
|
504
|
+
try {
|
|
505
|
+
response.destroy();
|
|
506
|
+
} catch {
|
|
507
|
+
}
|
|
508
|
+
failWith(err);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
325
512
|
let downloaded = 0;
|
|
326
513
|
let lastOverall = Math.round(s);
|
|
327
514
|
response.on("data", (chunk) => {
|
|
@@ -345,38 +532,26 @@ async function downloadFile(opts) {
|
|
|
345
532
|
const inc = endOverall - lastOverall;
|
|
346
533
|
if (inc > 0) progress.report({ message: "Downloading: 100%", increment: inc });
|
|
347
534
|
}
|
|
348
|
-
file.close((err) => err ? done(err) : done());
|
|
535
|
+
file.close((err) => err ? done(writeFailed(err)) : done());
|
|
349
536
|
});
|
|
350
537
|
abortSignal?.addEventListener("abort", () => {
|
|
351
538
|
response.destroy();
|
|
352
|
-
|
|
353
|
-
fs3.unlink(dest, () => {
|
|
354
|
-
});
|
|
355
|
-
done(new CancelledError("Download cancelled."));
|
|
539
|
+
failWith(new CancelledError("Download cancelled."));
|
|
356
540
|
});
|
|
357
541
|
});
|
|
358
542
|
req.on("error", (err) => {
|
|
359
|
-
|
|
360
|
-
file.close();
|
|
361
|
-
} catch {
|
|
362
|
-
}
|
|
363
|
-
fs3.unlink(dest, () => {
|
|
364
|
-
});
|
|
365
|
-
done(new OniroError(`Error downloading '${url}': ${err.message}`, err));
|
|
543
|
+
failWith(new OniroError(`Error downloading '${url}': ${err.message}`, err));
|
|
366
544
|
});
|
|
367
545
|
abortSignal?.addEventListener("abort", () => {
|
|
368
546
|
req.destroy();
|
|
369
|
-
|
|
370
|
-
fs3.unlink(dest, () => {
|
|
371
|
-
});
|
|
372
|
-
done(new CancelledError("Download cancelled."));
|
|
547
|
+
failWith(new CancelledError("Download cancelled."));
|
|
373
548
|
});
|
|
374
549
|
});
|
|
375
550
|
}
|
|
376
551
|
async function verifySha256(filePath, sha256Path) {
|
|
377
|
-
const expected =
|
|
552
|
+
const expected = fs4.readFileSync(sha256Path, "utf8").split(/\s+/)[0];
|
|
378
553
|
const hash = crypto.createHash("sha256");
|
|
379
|
-
await pipelineAsync(
|
|
554
|
+
await pipelineAsync(fs4.createReadStream(filePath), hash);
|
|
380
555
|
const actual = hash.digest("hex");
|
|
381
556
|
if (actual !== expected) {
|
|
382
557
|
throw new ChecksumMismatchError(expected, actual);
|
|
@@ -384,9 +559,9 @@ async function verifySha256(filePath, sha256Path) {
|
|
|
384
559
|
}
|
|
385
560
|
|
|
386
561
|
// src/sdk/extract.ts
|
|
387
|
-
import * as
|
|
388
|
-
import * as
|
|
389
|
-
import * as
|
|
562
|
+
import * as fs5 from "fs";
|
|
563
|
+
import * as os5 from "os";
|
|
564
|
+
import * as path6 from "path";
|
|
390
565
|
import { pipeline as pipeline2 } from "stream";
|
|
391
566
|
import { promisify as promisify2 } from "util";
|
|
392
567
|
import * as tar from "tar";
|
|
@@ -403,12 +578,12 @@ async function extractZipWithProgress(opts) {
|
|
|
403
578
|
const r = Math.max(0, Math.min(100 - s, opts.range ?? 100));
|
|
404
579
|
let processed = 0;
|
|
405
580
|
let lastOverall = Math.round(s);
|
|
406
|
-
await
|
|
407
|
-
const destRoot =
|
|
581
|
+
await fs5.promises.mkdir(dest, { recursive: true });
|
|
582
|
+
const destRoot = path6.resolve(dest);
|
|
408
583
|
const safeResolveTarget = (entryName) => {
|
|
409
|
-
const resolved =
|
|
410
|
-
const rel =
|
|
411
|
-
if (rel.startsWith("..") ||
|
|
584
|
+
const resolved = path6.resolve(destRoot, entryName);
|
|
585
|
+
const rel = path6.relative(destRoot, resolved);
|
|
586
|
+
if (rel.startsWith("..") || path6.isAbsolute(rel)) {
|
|
412
587
|
throw new OniroError(`Blocked ZIP entry with illegal path: ${entryName}`);
|
|
413
588
|
}
|
|
414
589
|
return resolved;
|
|
@@ -420,25 +595,25 @@ async function extractZipWithProgress(opts) {
|
|
|
420
595
|
const attr = file.attr ? file.attr >>> 16 : 0;
|
|
421
596
|
const isSymlink = "isSymbolicLink" in file ? Boolean(file.isSymbolicLink) : (attr & 61440) === 40960;
|
|
422
597
|
if (file.isDirectory) {
|
|
423
|
-
await
|
|
598
|
+
await fs5.promises.mkdir(targetPath, { recursive: true });
|
|
424
599
|
} else if (isSymlink) {
|
|
425
|
-
await
|
|
600
|
+
await fs5.promises.mkdir(path6.dirname(targetPath), { recursive: true });
|
|
426
601
|
const linkTargetBuffer = await zip.entryData(file);
|
|
427
602
|
const linkTarget = linkTargetBuffer.toString("utf8");
|
|
428
603
|
try {
|
|
429
|
-
const isWin =
|
|
430
|
-
await
|
|
604
|
+
const isWin = os5.platform() === "win32";
|
|
605
|
+
await fs5.promises.symlink(linkTarget, targetPath, isWin ? "file" : void 0);
|
|
431
606
|
} catch (e) {
|
|
432
607
|
logger.warn(`Failed to create symlink at ${targetPath}: ${String(e)}`);
|
|
433
608
|
}
|
|
434
609
|
} else {
|
|
435
|
-
await
|
|
610
|
+
await fs5.promises.mkdir(path6.dirname(targetPath), { recursive: true });
|
|
436
611
|
const readStream = await zip.stream(file);
|
|
437
|
-
const writeStream =
|
|
612
|
+
const writeStream = fs5.createWriteStream(targetPath);
|
|
438
613
|
await pipelineAsync2(readStream, writeStream);
|
|
439
614
|
if (attr > 0) {
|
|
440
615
|
try {
|
|
441
|
-
await
|
|
616
|
+
await fs5.promises.chmod(targetPath, attr & 511);
|
|
442
617
|
} catch {
|
|
443
618
|
}
|
|
444
619
|
}
|
|
@@ -470,41 +645,41 @@ async function extractTarball(tarPath, dest, strip = 0) {
|
|
|
470
645
|
}
|
|
471
646
|
|
|
472
647
|
// src/sdk/list.ts
|
|
473
|
-
import * as
|
|
474
|
-
import * as
|
|
648
|
+
import * as fs6 from "fs";
|
|
649
|
+
import * as path7 from "path";
|
|
475
650
|
function getSupportedSdksForUi(config) {
|
|
476
|
-
const base =
|
|
651
|
+
const base = path7.join(getSdkRootDir(config), getOsFolder());
|
|
477
652
|
return ALL_SDKS.map((sdk) => ({
|
|
478
653
|
version: sdk.version,
|
|
479
654
|
api: sdk.api,
|
|
480
|
-
installed:
|
|
655
|
+
installed: fs6.existsSync(path7.join(base, sdk.api))
|
|
481
656
|
})).sort((a, b) => Number(b.api) - Number(a.api));
|
|
482
657
|
}
|
|
483
658
|
function getInstalledSdks(config) {
|
|
484
659
|
const sdkRoot = getSdkRootDir(config);
|
|
485
660
|
const versions = /* @__PURE__ */ new Set();
|
|
486
|
-
if (!
|
|
661
|
+
if (!fs6.existsSync(sdkRoot)) return [];
|
|
487
662
|
for (const osFolder of ["linux", "darwin", "windows"]) {
|
|
488
|
-
const osPath =
|
|
489
|
-
if (!
|
|
490
|
-
for (const api of
|
|
491
|
-
const apiPath =
|
|
492
|
-
if (
|
|
663
|
+
const osPath = path7.join(sdkRoot, osFolder);
|
|
664
|
+
if (!fs6.existsSync(osPath) || !fs6.statSync(osPath).isDirectory()) continue;
|
|
665
|
+
for (const api of fs6.readdirSync(osPath)) {
|
|
666
|
+
const apiPath = path7.join(osPath, api);
|
|
667
|
+
if (fs6.statSync(apiPath).isDirectory()) versions.add(api);
|
|
493
668
|
}
|
|
494
669
|
}
|
|
495
670
|
return ALL_SDKS.filter((sdk) => versions.has(sdk.api)).map((sdk) => sdk.version);
|
|
496
671
|
}
|
|
497
672
|
|
|
498
673
|
// src/sdk/remove.ts
|
|
499
|
-
import * as
|
|
500
|
-
import * as
|
|
674
|
+
import * as fs7 from "fs";
|
|
675
|
+
import * as path8 from "path";
|
|
501
676
|
function removeSdk(config, api) {
|
|
502
677
|
const sdkRoot = getSdkRootDir(config);
|
|
503
678
|
let removed = false;
|
|
504
679
|
for (const osFolder of ["linux", "darwin", "windows"]) {
|
|
505
|
-
const sdkPath =
|
|
506
|
-
if (
|
|
507
|
-
|
|
680
|
+
const sdkPath = path8.join(sdkRoot, osFolder, api);
|
|
681
|
+
if (fs7.existsSync(sdkPath)) {
|
|
682
|
+
fs7.rmSync(sdkPath, { recursive: true, force: true });
|
|
508
683
|
removed = true;
|
|
509
684
|
}
|
|
510
685
|
}
|
|
@@ -512,19 +687,18 @@ function removeSdk(config, api) {
|
|
|
512
687
|
}
|
|
513
688
|
|
|
514
689
|
// src/sdk/install.ts
|
|
515
|
-
import * as
|
|
516
|
-
import * as
|
|
517
|
-
import * as path7 from "path";
|
|
690
|
+
import * as fs9 from "fs";
|
|
691
|
+
import * as path9 from "path";
|
|
518
692
|
|
|
519
693
|
// src/sdk/move.ts
|
|
520
|
-
import * as
|
|
694
|
+
import * as fs8 from "fs";
|
|
521
695
|
function movePath(src, dest) {
|
|
522
696
|
try {
|
|
523
|
-
|
|
697
|
+
fs8.renameSync(src, dest);
|
|
524
698
|
} catch (err) {
|
|
525
699
|
if (err.code !== "EXDEV") throw err;
|
|
526
|
-
|
|
527
|
-
|
|
700
|
+
fs8.cpSync(src, dest, { recursive: true });
|
|
701
|
+
fs8.rmSync(src, { recursive: true, force: true });
|
|
528
702
|
}
|
|
529
703
|
}
|
|
530
704
|
|
|
@@ -536,21 +710,29 @@ async function downloadAndInstallSdk(opts) {
|
|
|
536
710
|
const urlBase = config.get("sdkUrlBase", OHOS_URL_BASE);
|
|
537
711
|
const downloadUrl = `${urlBase}/${version}-Release/${filename}`;
|
|
538
712
|
const sha256Url = `${downloadUrl}.sha256`;
|
|
539
|
-
const
|
|
540
|
-
const
|
|
541
|
-
const
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
713
|
+
const what = `the SDK ${version} install`;
|
|
714
|
+
const sdkRoot = getSdkRootDir(config);
|
|
715
|
+
const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
|
|
716
|
+
config,
|
|
717
|
+
override: opts.tmpDir,
|
|
718
|
+
installRoot: sdkRoot,
|
|
719
|
+
prefix: "oniro-sdk-",
|
|
720
|
+
logger
|
|
721
|
+
});
|
|
722
|
+
const tarPath = path9.join(tmpDir, filename);
|
|
723
|
+
const sha256Path = path9.join(tmpDir, `${filename}.sha256`);
|
|
724
|
+
const extractDir = path9.join(tmpDir, "extract");
|
|
725
|
+
fs9.mkdirSync(extractDir);
|
|
726
|
+
const sdkInstallDir = path9.join(sdkRoot, osFolder, api);
|
|
727
|
+
fs9.mkdirSync(path9.dirname(sdkInstallDir), { recursive: true });
|
|
546
728
|
const checkCancelled = () => {
|
|
547
729
|
if (abortSignal?.aborted) throw new CancelledError();
|
|
548
730
|
};
|
|
549
731
|
try {
|
|
550
732
|
progress?.report({ message: "Downloading SDK archive...", increment: 0 });
|
|
551
|
-
await downloadFile({ url: downloadUrl, dest: tarPath, progress, abortSignal, start: 0, range: 35 });
|
|
733
|
+
await downloadFile({ url: downloadUrl, dest: tarPath, progress, abortSignal, start: 0, range: 35, what, tmpRoot });
|
|
552
734
|
progress?.report({ message: "Downloading checksum...", increment: 0 });
|
|
553
|
-
await downloadFile({ url: sha256Url, dest: sha256Path, progress, abortSignal, start: 35, range: 10 });
|
|
735
|
+
await downloadFile({ url: sha256Url, dest: sha256Path, progress, abortSignal, start: 35, range: 10, what, tmpRoot });
|
|
554
736
|
progress?.report({ message: "Verifying checksum...", increment: 0 });
|
|
555
737
|
await verifySha256(tarPath, sha256Path);
|
|
556
738
|
progress?.report({ message: "Verifying checksum...", increment: 5 });
|
|
@@ -559,13 +741,13 @@ async function downloadAndInstallSdk(opts) {
|
|
|
559
741
|
await extractTarball(tarPath, extractDir, strip);
|
|
560
742
|
progress?.report({ message: "Extracting SDK (this may take a while)...", increment: 10 });
|
|
561
743
|
checkCancelled();
|
|
562
|
-
const osContentPath =
|
|
563
|
-
if (!
|
|
744
|
+
const osContentPath = path9.join(extractDir, osFolder);
|
|
745
|
+
if (!fs9.existsSync(osContentPath)) {
|
|
564
746
|
throw new OniroError(
|
|
565
747
|
`Expected folder '${osFolder}' not found in extracted SDK. Tarball layout may have changed.`
|
|
566
748
|
);
|
|
567
749
|
}
|
|
568
|
-
const zipFiles =
|
|
750
|
+
const zipFiles = fs9.readdirSync(osContentPath).filter((n2) => n2.endsWith(".zip"));
|
|
569
751
|
const componentsStart = 60;
|
|
570
752
|
const componentsBudget = 35;
|
|
571
753
|
const n = zipFiles.length;
|
|
@@ -580,39 +762,40 @@ async function downloadAndInstallSdk(opts) {
|
|
|
580
762
|
for (const zipFile of zipFiles) {
|
|
581
763
|
checkCancelled();
|
|
582
764
|
logger.info(`Extracting component ${zipFile}`);
|
|
583
|
-
const zipPath =
|
|
765
|
+
const zipPath = path9.join(osContentPath, zipFile);
|
|
584
766
|
const thisBudget = base + (rem > 0 ? 1 : 0);
|
|
585
767
|
if (rem > 0) rem--;
|
|
586
768
|
await extractZipWithProgress({ zipPath, dest: osContentPath, progress, start: cursor, range: thisBudget, logger });
|
|
587
769
|
cursor += thisBudget;
|
|
588
|
-
|
|
770
|
+
fs9.unlinkSync(zipPath);
|
|
589
771
|
}
|
|
590
772
|
progress?.report({ message: "Finalizing installation...", increment: 0 });
|
|
591
|
-
if (
|
|
592
|
-
|
|
773
|
+
if (fs9.existsSync(sdkInstallDir)) {
|
|
774
|
+
fs9.rmSync(sdkInstallDir, { recursive: true, force: true });
|
|
593
775
|
}
|
|
594
776
|
movePath(osContentPath, sdkInstallDir);
|
|
595
777
|
progress?.report({ message: "Finalizing installation...", increment: 3 });
|
|
596
778
|
progress?.report({ message: "Cleaning up...", increment: 0 });
|
|
597
|
-
|
|
779
|
+
removeTempWorkDir(tmpDir, tmpRoot);
|
|
598
780
|
progress?.report({ message: "Cleaning up...", increment: 2 });
|
|
599
781
|
} catch (err) {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
782
|
+
const wrapped = toSpaceError(err, tmpRoot, what);
|
|
783
|
+
logger.error(`SDK install failed: ${wrapped instanceof Error ? wrapped.message : String(wrapped)}`);
|
|
784
|
+
removeTempWorkDir(tmpDir, tmpRoot);
|
|
785
|
+
throw wrapped;
|
|
603
786
|
}
|
|
604
787
|
}
|
|
605
788
|
|
|
606
789
|
// src/cmdTools/install.ts
|
|
607
|
-
import * as
|
|
790
|
+
import * as fs10 from "fs";
|
|
608
791
|
import * as os6 from "os";
|
|
609
|
-
import * as
|
|
792
|
+
import * as path10 from "path";
|
|
610
793
|
import { execFileSync } from "child_process";
|
|
611
|
-
function getCmdToolsDownloadUrl(config,
|
|
612
|
-
if (
|
|
794
|
+
function getCmdToolsDownloadUrl(config, platform7 = os6.platform()) {
|
|
795
|
+
if (platform7 === "linux") {
|
|
613
796
|
return config.get("cmdToolsUrlLinux", defaultPaths.cmdToolsUrlLinux);
|
|
614
797
|
}
|
|
615
|
-
if (
|
|
798
|
+
if (platform7 === "win32") {
|
|
616
799
|
const url = config.get("cmdToolsUrlWindows", "");
|
|
617
800
|
if (!url) {
|
|
618
801
|
throw new OniroError(
|
|
@@ -621,7 +804,7 @@ function getCmdToolsDownloadUrl(config, platform6 = os6.platform()) {
|
|
|
621
804
|
}
|
|
622
805
|
return url;
|
|
623
806
|
}
|
|
624
|
-
if (
|
|
807
|
+
if (platform7 === "darwin") {
|
|
625
808
|
const url = config.get("cmdToolsUrlMac", "");
|
|
626
809
|
if (!url) {
|
|
627
810
|
throw new OniroError(
|
|
@@ -630,20 +813,20 @@ function getCmdToolsDownloadUrl(config, platform6 = os6.platform()) {
|
|
|
630
813
|
}
|
|
631
814
|
return url;
|
|
632
815
|
}
|
|
633
|
-
throw new UnsupportedPlatformError(
|
|
816
|
+
throw new UnsupportedPlatformError(platform7);
|
|
634
817
|
}
|
|
635
818
|
function findCmdToolsSourceDir(extractPath) {
|
|
636
819
|
const known = [
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
820
|
+
path10.join(extractPath, "command-line-tools"),
|
|
821
|
+
path10.join(extractPath, "oh-command-line-tools"),
|
|
822
|
+
path10.join(extractPath, "commandline-tools")
|
|
640
823
|
];
|
|
641
824
|
for (const c of known) {
|
|
642
|
-
if (
|
|
825
|
+
if (fs10.existsSync(c)) return c;
|
|
643
826
|
}
|
|
644
|
-
const entries =
|
|
827
|
+
const entries = fs10.readdirSync(extractPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path10.join(extractPath, e.name));
|
|
645
828
|
for (const dir of entries) {
|
|
646
|
-
if (
|
|
829
|
+
if (fs10.existsSync(path10.join(dir, "bin"))) return dir;
|
|
647
830
|
}
|
|
648
831
|
throw new OniroError("Could not locate command line tools folder in the extracted archive.");
|
|
649
832
|
}
|
|
@@ -651,53 +834,62 @@ async function installCmdTools(opts) {
|
|
|
651
834
|
const { config, progress, abortSignal } = opts;
|
|
652
835
|
const logger = opts.logger ?? noopLogger;
|
|
653
836
|
const CMD_PATH = getCmdToolsPath(config);
|
|
654
|
-
const
|
|
655
|
-
const
|
|
656
|
-
|
|
837
|
+
const what = "the command-line tools install";
|
|
838
|
+
const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
|
|
839
|
+
config,
|
|
840
|
+
override: opts.tmpDir,
|
|
841
|
+
installRoot: path10.dirname(CMD_PATH),
|
|
842
|
+
prefix: "oniro-cmdtools-",
|
|
843
|
+
logger
|
|
844
|
+
});
|
|
845
|
+
const zipPath = opts.localZipPath ?? path10.join(tmpDir, "oh-command-line-tools.zip");
|
|
846
|
+
const extractPath = path10.join(tmpDir, "oh-command-line-tools");
|
|
657
847
|
try {
|
|
658
848
|
if (!opts.localZipPath) {
|
|
659
849
|
const url = getCmdToolsDownloadUrl(config);
|
|
660
850
|
progress?.report({ message: "Downloading command line tools...", increment: 0 });
|
|
661
|
-
await downloadFile({ url, dest: zipPath, progress, abortSignal, start: 0, range: 50 });
|
|
851
|
+
await downloadFile({ url, dest: zipPath, progress, abortSignal, start: 0, range: 50, what, tmpRoot });
|
|
662
852
|
}
|
|
663
853
|
progress?.report({ message: "Extracting tools...", increment: 0 });
|
|
664
854
|
await extractZipWithProgress({ zipPath, dest: extractPath, progress, start: 50, range: 45, logger });
|
|
665
|
-
|
|
855
|
+
fs10.mkdirSync(CMD_PATH, { recursive: true });
|
|
666
856
|
const srcDir = findCmdToolsSourceDir(extractPath);
|
|
667
|
-
for (const entry of
|
|
668
|
-
const src =
|
|
669
|
-
const dest =
|
|
670
|
-
if (
|
|
671
|
-
if (
|
|
857
|
+
for (const entry of fs10.readdirSync(srcDir)) {
|
|
858
|
+
const src = path10.join(srcDir, entry);
|
|
859
|
+
const dest = path10.join(CMD_PATH, entry);
|
|
860
|
+
if (fs10.statSync(src).isDirectory()) {
|
|
861
|
+
if (fs10.existsSync(dest)) fs10.rmSync(dest, { recursive: true, force: true });
|
|
672
862
|
movePath(src, dest);
|
|
673
863
|
} else {
|
|
674
|
-
|
|
864
|
+
fs10.copyFileSync(src, dest);
|
|
675
865
|
}
|
|
676
866
|
}
|
|
677
|
-
const binDir =
|
|
678
|
-
if (
|
|
679
|
-
for (const file of
|
|
680
|
-
|
|
867
|
+
const binDir = path10.join(CMD_PATH, "bin");
|
|
868
|
+
if (fs10.existsSync(binDir) && os6.platform() !== "win32") {
|
|
869
|
+
for (const file of fs10.readdirSync(binDir)) {
|
|
870
|
+
fs10.chmodSync(path10.join(binDir, file), 493);
|
|
681
871
|
}
|
|
682
872
|
}
|
|
683
873
|
progress?.report({ message: "Finalizing installation...", increment: 5 });
|
|
684
874
|
progress?.report({ message: "Cleaning up...", increment: 0 });
|
|
875
|
+
} catch (err) {
|
|
876
|
+
throw toSpaceError(err, tmpRoot, what);
|
|
685
877
|
} finally {
|
|
686
|
-
|
|
878
|
+
removeTempWorkDir(tmpDir, tmpRoot);
|
|
687
879
|
}
|
|
688
880
|
}
|
|
689
881
|
function getCmdToolsStatus(config) {
|
|
690
882
|
const cmdPath = getCmdToolsPath(config);
|
|
691
|
-
const binDir =
|
|
883
|
+
const binDir = path10.join(cmdPath, "bin");
|
|
692
884
|
const candidates = os6.platform() === "win32" ? ["ohpm.exe", "ohpm.cmd", "ohpm.bat", "ohpm"] : ["ohpm"];
|
|
693
|
-
const bin = candidates.map((c) =>
|
|
885
|
+
const bin = candidates.map((c) => path10.join(binDir, c)).find((p) => fs10.existsSync(p));
|
|
694
886
|
if (!bin) {
|
|
695
887
|
return { installed: false, status: "Not installed" };
|
|
696
888
|
}
|
|
697
889
|
try {
|
|
698
|
-
const versionFile =
|
|
699
|
-
if (
|
|
700
|
-
const content =
|
|
890
|
+
const versionFile = path10.join(cmdPath, "version.txt");
|
|
891
|
+
if (fs10.existsSync(versionFile)) {
|
|
892
|
+
const content = fs10.readFileSync(versionFile, "utf8");
|
|
701
893
|
for (const line of content.split(/\r?\n/)) {
|
|
702
894
|
const m = line.match(/^\s*#\s*Version:\s*(.+)$/);
|
|
703
895
|
if (m?.[1]) return { installed: true, status: `Installed (${m[1].trim()})` };
|
|
@@ -717,14 +909,14 @@ function isCmdToolsInstalled(config) {
|
|
|
717
909
|
}
|
|
718
910
|
function removeCmdTools(config) {
|
|
719
911
|
const cmdPath = getCmdToolsPath(config);
|
|
720
|
-
if (
|
|
721
|
-
|
|
912
|
+
if (fs10.existsSync(cmdPath)) {
|
|
913
|
+
fs10.rmSync(cmdPath, { recursive: true, force: true });
|
|
722
914
|
}
|
|
723
915
|
}
|
|
724
916
|
|
|
725
917
|
// src/sign/encryptKey.ts
|
|
726
|
-
import * as
|
|
727
|
-
import * as
|
|
918
|
+
import * as fs11 from "fs";
|
|
919
|
+
import * as path11 from "path";
|
|
728
920
|
import * as crypto2 from "crypto";
|
|
729
921
|
var DAEMON_ROOT_KEY_COMPONENT_LENGTH = 16;
|
|
730
922
|
var DAEMON_SALT_KEY_LENGTH = 16;
|
|
@@ -796,33 +988,33 @@ function getRootKey(fdComponents, salt) {
|
|
|
796
988
|
function createAndStoreKey(dir, keyLength) {
|
|
797
989
|
const key = crypto2.randomBytes(keyLength);
|
|
798
990
|
const hash = crypto2.createHash("sha256").update(key).digest("hex");
|
|
799
|
-
const filePath =
|
|
800
|
-
|
|
801
|
-
|
|
991
|
+
const filePath = path11.join(dir, hash);
|
|
992
|
+
fs11.writeFileSync(filePath, key);
|
|
993
|
+
fs11.chmodSync(filePath, KEY_FILE_PERMISSIONS);
|
|
802
994
|
return key;
|
|
803
995
|
}
|
|
804
996
|
function createAndStoreEnKey(rootKey, ceDir) {
|
|
805
997
|
const workKey = crypto2.randomBytes(DAEMON_WORK_KEY_LENGTH);
|
|
806
998
|
const encrypted = encrypt(rootKey, workKey);
|
|
807
999
|
const hash = crypto2.createHash("sha256").update(encrypted).digest("hex");
|
|
808
|
-
const filePath =
|
|
809
|
-
|
|
810
|
-
|
|
1000
|
+
const filePath = path11.join(ceDir, hash);
|
|
1001
|
+
fs11.writeFileSync(filePath, encrypted);
|
|
1002
|
+
fs11.chmodSync(filePath, KEY_FILE_PERMISSIONS);
|
|
811
1003
|
}
|
|
812
1004
|
function createMaterial(materialPath) {
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
const fdDir =
|
|
816
|
-
const acDir =
|
|
817
|
-
const ceDir =
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
1005
|
+
fs11.mkdirSync(materialPath, { recursive: true });
|
|
1006
|
+
fs11.chmodSync(materialPath, KEY_FILE_DIRECTORY_PERMISSIONS);
|
|
1007
|
+
const fdDir = path11.join(materialPath, "fd");
|
|
1008
|
+
const acDir = path11.join(materialPath, "ac");
|
|
1009
|
+
const ceDir = path11.join(materialPath, "ce");
|
|
1010
|
+
fs11.mkdirSync(fdDir, { recursive: true });
|
|
1011
|
+
fs11.mkdirSync(acDir, { recursive: true });
|
|
1012
|
+
fs11.mkdirSync(ceDir, { recursive: true });
|
|
821
1013
|
const fdSubDirs = ["0", "1", "2"];
|
|
822
1014
|
const fdComponents = [];
|
|
823
1015
|
for (const sub of fdSubDirs) {
|
|
824
|
-
const subDir =
|
|
825
|
-
|
|
1016
|
+
const subDir = path11.join(fdDir, sub);
|
|
1017
|
+
fs11.mkdirSync(subDir, { recursive: true });
|
|
826
1018
|
const comp = createAndStoreKey(subDir, DAEMON_ROOT_KEY_COMPONENT_LENGTH);
|
|
827
1019
|
fdComponents.push(comp);
|
|
828
1020
|
}
|
|
@@ -831,24 +1023,24 @@ function createMaterial(materialPath) {
|
|
|
831
1023
|
createAndStoreEnKey(rootKey, ceDir);
|
|
832
1024
|
}
|
|
833
1025
|
function readSingleFile(dir) {
|
|
834
|
-
if (!
|
|
1026
|
+
if (!fs11.existsSync(dir) || !fs11.statSync(dir).isDirectory()) {
|
|
835
1027
|
throw new Error(`Missing signing material directory: ${dir}`);
|
|
836
1028
|
}
|
|
837
|
-
const files =
|
|
1029
|
+
const files = fs11.readdirSync(dir).filter((f) => f !== ".DS_Store");
|
|
838
1030
|
if (files.length !== 1) {
|
|
839
1031
|
throw new Error(`Signing material in ${dir} is illegal (expected exactly one file).`);
|
|
840
1032
|
}
|
|
841
|
-
return
|
|
1033
|
+
return fs11.readFileSync(path11.join(dir, files[0]));
|
|
842
1034
|
}
|
|
843
1035
|
function getKey(materialPath) {
|
|
844
|
-
if (!
|
|
1036
|
+
if (!fs11.existsSync(materialPath) || !fs11.statSync(materialPath).isDirectory()) {
|
|
845
1037
|
throw new Error("Material directory does not exist.");
|
|
846
1038
|
}
|
|
847
|
-
const fdDir =
|
|
848
|
-
const fdComponents = ["0", "1", "2"].map((sub) => readSingleFile(
|
|
849
|
-
const salt = readSingleFile(
|
|
1039
|
+
const fdDir = path11.join(materialPath, "fd");
|
|
1040
|
+
const fdComponents = ["0", "1", "2"].map((sub) => readSingleFile(path11.join(fdDir, sub)));
|
|
1041
|
+
const salt = readSingleFile(path11.join(materialPath, "ac"));
|
|
850
1042
|
const rootKey = getRootKey(fdComponents, salt);
|
|
851
|
-
const workMaterial = readSingleFile(
|
|
1043
|
+
const workMaterial = readSingleFile(path11.join(materialPath, "ce"));
|
|
852
1044
|
return decrypt(rootKey, workMaterial);
|
|
853
1045
|
}
|
|
854
1046
|
function encryptPwd(password, materialPath) {
|
|
@@ -863,8 +1055,8 @@ function decryptPwd(encryptedHex, materialPath) {
|
|
|
863
1055
|
}
|
|
864
1056
|
|
|
865
1057
|
// src/sign/generateSigningConfigs.ts
|
|
866
|
-
import * as
|
|
867
|
-
import * as
|
|
1058
|
+
import * as fs12 from "fs";
|
|
1059
|
+
import * as path12 from "path";
|
|
868
1060
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
869
1061
|
import JSON52 from "json5";
|
|
870
1062
|
|
|
@@ -934,17 +1126,17 @@ var SIGNING_KIND_CONFIG = {
|
|
|
934
1126
|
};
|
|
935
1127
|
function copyFilesToProject(projectDir, paths, kind, logger, applicationCertPath) {
|
|
936
1128
|
logger.info("[sign] Copying signing material into project...");
|
|
937
|
-
const signaturesDir =
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
1129
|
+
const signaturesDir = path12.join(projectDir, "signatures");
|
|
1130
|
+
fs12.mkdirSync(signaturesDir, { recursive: true });
|
|
1131
|
+
fs12.copyFileSync(paths.keystore, path12.join(signaturesDir, "OpenHarmony.p12"));
|
|
1132
|
+
fs12.copyFileSync(paths.profileCert, path12.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
|
|
1133
|
+
fs12.copyFileSync(paths.unsignedProfileTemplate, path12.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json"));
|
|
942
1134
|
if (kind === "application-release") {
|
|
943
|
-
const dest =
|
|
1135
|
+
const dest = path12.join(signaturesDir, "OpenHarmonyApplication.cer");
|
|
944
1136
|
if (applicationCertPath) {
|
|
945
|
-
|
|
1137
|
+
fs12.copyFileSync(applicationCertPath, dest);
|
|
946
1138
|
} else {
|
|
947
|
-
|
|
1139
|
+
fs12.writeFileSync(dest, OPEN_HARMONY_APPLICATION_CERT);
|
|
948
1140
|
}
|
|
949
1141
|
}
|
|
950
1142
|
}
|
|
@@ -953,7 +1145,7 @@ function resolveAppFeature(apl, override) {
|
|
|
953
1145
|
return apl === "normal" ? "hos_normal_app" : "hos_system_app";
|
|
954
1146
|
}
|
|
955
1147
|
function extractLeafCertificate(chainPath) {
|
|
956
|
-
const certContent =
|
|
1148
|
+
const certContent = fs12.readFileSync(chainPath, "utf-8");
|
|
957
1149
|
const parts = certContent.split("-----END CERTIFICATE-----");
|
|
958
1150
|
if (parts.length < 3) {
|
|
959
1151
|
throw new OniroError(`${chainPath} does not contain enough certificates.`);
|
|
@@ -963,20 +1155,20 @@ function extractLeafCertificate(chainPath) {
|
|
|
963
1155
|
function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}) {
|
|
964
1156
|
const kind = options.kind ?? pickSigningKind(apl);
|
|
965
1157
|
logger.info(`[sign] Modifying profile template (apl=${apl}, app-feature=${appFeature}, kind=${kind})...`);
|
|
966
|
-
const appJsonPath =
|
|
967
|
-
const profileTemplatePath =
|
|
968
|
-
if (!
|
|
1158
|
+
const appJsonPath = path12.join(projectDir, "AppScope/app.json5");
|
|
1159
|
+
const profileTemplatePath = path12.join(projectDir, "signatures/UnsgnedReleasedProfileTemplate.json");
|
|
1160
|
+
if (!fs12.existsSync(appJsonPath)) {
|
|
969
1161
|
throw new OniroError(`${appJsonPath} does not exist.`);
|
|
970
1162
|
}
|
|
971
1163
|
let appJson;
|
|
972
1164
|
try {
|
|
973
|
-
appJson = JSON52.parse(
|
|
1165
|
+
appJson = JSON52.parse(fs12.readFileSync(appJsonPath, "utf-8"));
|
|
974
1166
|
} catch (e) {
|
|
975
1167
|
throw new OniroError(`Error parsing ${appJsonPath}: ${e.message}`, e);
|
|
976
1168
|
}
|
|
977
1169
|
let profileTemplate;
|
|
978
1170
|
try {
|
|
979
|
-
profileTemplate = JSON.parse(
|
|
1171
|
+
profileTemplate = JSON.parse(fs12.readFileSync(profileTemplatePath, "utf-8"));
|
|
980
1172
|
} catch (e) {
|
|
981
1173
|
throw new OniroError(`Error parsing ${profileTemplatePath}: ${e.message}`, e);
|
|
982
1174
|
}
|
|
@@ -989,7 +1181,7 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
|
|
|
989
1181
|
profileTemplate["bundle-info"]["bundle-name"] = appJson.app.bundleName;
|
|
990
1182
|
profileTemplate["bundle-info"]["apl"] = apl;
|
|
991
1183
|
profileTemplate["bundle-info"]["app-feature"] = appFeature;
|
|
992
|
-
const distCertChainPath =
|
|
1184
|
+
const distCertChainPath = path12.join(
|
|
993
1185
|
projectDir,
|
|
994
1186
|
"signatures",
|
|
995
1187
|
SIGNING_KIND_CONFIG[kind].certBasename
|
|
@@ -999,13 +1191,13 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
|
|
|
999
1191
|
profileTemplate.acls = profileTemplate.acls ?? {};
|
|
1000
1192
|
profileTemplate.acls["allowed-acls"] = options.acls;
|
|
1001
1193
|
}
|
|
1002
|
-
|
|
1194
|
+
fs12.writeFileSync(profileTemplatePath, JSON.stringify(profileTemplate, null, 2));
|
|
1003
1195
|
}
|
|
1004
1196
|
function generateP7bFile(projectDir, paths, logger, passwords) {
|
|
1005
1197
|
logger.info("[sign] Generating P7b profile via hap-sign-tool...");
|
|
1006
|
-
const signaturesDir =
|
|
1007
|
-
const profileTemplatePath =
|
|
1008
|
-
const outputProfilePath =
|
|
1198
|
+
const signaturesDir = path12.join(projectDir, "signatures");
|
|
1199
|
+
const profileTemplatePath = path12.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
|
|
1200
|
+
const outputProfilePath = path12.join(signaturesDir, "app1-profile.p7b");
|
|
1009
1201
|
const args = [
|
|
1010
1202
|
"-jar",
|
|
1011
1203
|
paths.signTool,
|
|
@@ -1039,11 +1231,11 @@ function generateP7bFile(projectDir, paths, logger, passwords) {
|
|
|
1039
1231
|
}
|
|
1040
1232
|
}
|
|
1041
1233
|
function detectSigningConfigNames(projectDir) {
|
|
1042
|
-
const buildProfilePath =
|
|
1043
|
-
if (!
|
|
1234
|
+
const buildProfilePath = path12.join(projectDir, "build-profile.json5");
|
|
1235
|
+
if (!fs12.existsSync(buildProfilePath)) return ["default"];
|
|
1044
1236
|
let parsed;
|
|
1045
1237
|
try {
|
|
1046
|
-
parsed = JSON52.parse(
|
|
1238
|
+
parsed = JSON52.parse(fs12.readFileSync(buildProfilePath, "utf-8"));
|
|
1047
1239
|
} catch {
|
|
1048
1240
|
return ["default"];
|
|
1049
1241
|
}
|
|
@@ -1060,14 +1252,14 @@ function detectSigningConfigNames(projectDir) {
|
|
|
1060
1252
|
}
|
|
1061
1253
|
function updateBuildProfile(projectDir, kind, logger, passwords) {
|
|
1062
1254
|
logger.info("[sign] Writing signing configs into build-profile.json5...");
|
|
1063
|
-
const materialDir =
|
|
1064
|
-
const buildProfilePath =
|
|
1255
|
+
const materialDir = path12.join(projectDir, "signatures", "material");
|
|
1256
|
+
const buildProfilePath = path12.join(projectDir, "build-profile.json5");
|
|
1065
1257
|
const encryptedStorePassword = encryptPwd(passwords.store, materialDir);
|
|
1066
1258
|
const encryptedKeyPassword = encryptPwd(passwords.key, materialDir);
|
|
1067
1259
|
let buildProfile = { app: {} };
|
|
1068
|
-
if (
|
|
1260
|
+
if (fs12.existsSync(buildProfilePath)) {
|
|
1069
1261
|
try {
|
|
1070
|
-
buildProfile = JSON52.parse(
|
|
1262
|
+
buildProfile = JSON52.parse(fs12.readFileSync(buildProfilePath, "utf-8"));
|
|
1071
1263
|
} catch (e) {
|
|
1072
1264
|
throw new OniroError(`Error parsing ${buildProfilePath}: ${e.message}`, e);
|
|
1073
1265
|
}
|
|
@@ -1088,13 +1280,13 @@ function updateBuildProfile(projectDir, kind, logger, passwords) {
|
|
|
1088
1280
|
storeFile: "./signatures/OpenHarmony.p12"
|
|
1089
1281
|
}
|
|
1090
1282
|
}));
|
|
1091
|
-
|
|
1283
|
+
fs12.writeFileSync(buildProfilePath, JSON.stringify(buildProfile, null, 2));
|
|
1092
1284
|
}
|
|
1093
1285
|
function prepareMaterialDirectory(projectDir, logger) {
|
|
1094
1286
|
logger.info("[sign] Generating fresh signing material...");
|
|
1095
|
-
const materialDir =
|
|
1096
|
-
if (
|
|
1097
|
-
|
|
1287
|
+
const materialDir = path12.join(projectDir, "signatures", "material");
|
|
1288
|
+
if (fs12.existsSync(materialDir)) {
|
|
1289
|
+
fs12.rmSync(materialDir, { recursive: true, force: true });
|
|
1098
1290
|
}
|
|
1099
1291
|
createMaterial(materialDir);
|
|
1100
1292
|
}
|
|
@@ -1117,15 +1309,15 @@ function generateSigningConfigs(options) {
|
|
|
1117
1309
|
"Could not detect project SDK version (compileSdkVersion missing in build-profile.json5)."
|
|
1118
1310
|
);
|
|
1119
1311
|
}
|
|
1120
|
-
const sdkPath =
|
|
1121
|
-
if (!
|
|
1312
|
+
const sdkPath = path12.join(sdkHome, String(sdkVersion));
|
|
1313
|
+
if (!fs12.existsSync(sdkPath)) {
|
|
1122
1314
|
throw new OniroError(`SDK path does not exist: ${sdkPath}`);
|
|
1123
1315
|
}
|
|
1124
1316
|
const paths = {
|
|
1125
|
-
signTool:
|
|
1126
|
-
keystore:
|
|
1127
|
-
profileCert:
|
|
1128
|
-
unsignedProfileTemplate:
|
|
1317
|
+
signTool: path12.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
|
|
1318
|
+
keystore: path12.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
|
|
1319
|
+
profileCert: path12.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
|
|
1320
|
+
unsignedProfileTemplate: path12.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
|
|
1129
1321
|
};
|
|
1130
1322
|
const kind = pickSigningKind(apl);
|
|
1131
1323
|
const passwords = options.passwords ?? DEFAULT_PASSWORDS;
|
|
@@ -1139,46 +1331,54 @@ function generateSigningConfigs(options) {
|
|
|
1139
1331
|
}
|
|
1140
1332
|
|
|
1141
1333
|
// src/emulator/install.ts
|
|
1142
|
-
import * as
|
|
1143
|
-
import * as
|
|
1144
|
-
import * as path11 from "path";
|
|
1334
|
+
import * as fs13 from "fs";
|
|
1335
|
+
import * as path13 from "path";
|
|
1145
1336
|
async function installEmulator(opts) {
|
|
1146
1337
|
const { config, progress, abortSignal } = opts;
|
|
1147
1338
|
const logger = opts.logger ?? noopLogger;
|
|
1148
1339
|
const url = config.get("emulatorUrl", defaultPaths.emulatorUrl);
|
|
1149
1340
|
const emulatorDir = getEmulatorDir(config);
|
|
1150
|
-
const
|
|
1151
|
-
const
|
|
1341
|
+
const what = "the emulator install";
|
|
1342
|
+
const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
|
|
1343
|
+
config,
|
|
1344
|
+
override: opts.tmpDir,
|
|
1345
|
+
installRoot: path13.dirname(emulatorDir),
|
|
1346
|
+
prefix: "oniro-emulator-",
|
|
1347
|
+
logger
|
|
1348
|
+
});
|
|
1349
|
+
const tmpZip = path13.join(tmpDir, "oniro_emulator.zip");
|
|
1152
1350
|
try {
|
|
1153
|
-
|
|
1351
|
+
fs13.mkdirSync(emulatorDir, { recursive: true });
|
|
1154
1352
|
progress?.report({ message: "Downloading emulator...", increment: 0 });
|
|
1155
|
-
await downloadFile({ url, dest: tmpZip, progress, abortSignal, start: 0, range: 50 });
|
|
1353
|
+
await downloadFile({ url, dest: tmpZip, progress, abortSignal, start: 0, range: 50, what, tmpRoot });
|
|
1156
1354
|
progress?.report({ message: "Extracting emulator...", increment: 0 });
|
|
1157
1355
|
await extractZipWithProgress({ zipPath: tmpZip, dest: emulatorDir, progress, start: 50, range: 45, logger });
|
|
1158
|
-
const runSh =
|
|
1159
|
-
if (
|
|
1160
|
-
|
|
1356
|
+
const runSh = path13.join(emulatorDir, "images", "run.sh");
|
|
1357
|
+
if (fs13.existsSync(runSh)) {
|
|
1358
|
+
fs13.chmodSync(runSh, 493);
|
|
1161
1359
|
}
|
|
1162
1360
|
progress?.report({ message: "Finalizing installation...", increment: 5 });
|
|
1361
|
+
} catch (err) {
|
|
1362
|
+
throw toSpaceError(err, tmpRoot, what);
|
|
1163
1363
|
} finally {
|
|
1164
|
-
|
|
1364
|
+
removeTempWorkDir(tmpDir, tmpRoot);
|
|
1165
1365
|
}
|
|
1166
1366
|
}
|
|
1167
1367
|
function isEmulatorInstalled(config) {
|
|
1168
|
-
const imagesDir =
|
|
1169
|
-
return
|
|
1368
|
+
const imagesDir = path13.join(getEmulatorDir(config), "images");
|
|
1369
|
+
return fs13.existsSync(path13.join(imagesDir, "run.sh")) || fs13.existsSync(path13.join(imagesDir, "run.bat"));
|
|
1170
1370
|
}
|
|
1171
1371
|
function removeEmulator(config) {
|
|
1172
1372
|
const emulatorDir = getEmulatorDir(config);
|
|
1173
|
-
if (
|
|
1174
|
-
|
|
1373
|
+
if (fs13.existsSync(emulatorDir)) {
|
|
1374
|
+
fs13.rmSync(emulatorDir, { recursive: true, force: true });
|
|
1175
1375
|
}
|
|
1176
1376
|
}
|
|
1177
1377
|
|
|
1178
1378
|
// src/emulator/lifecycle.ts
|
|
1179
|
-
import * as
|
|
1180
|
-
import * as
|
|
1181
|
-
import * as
|
|
1379
|
+
import * as fs14 from "fs";
|
|
1380
|
+
import * as os7 from "os";
|
|
1381
|
+
import * as path14 from "path";
|
|
1182
1382
|
import { spawn as spawn2 } from "child_process";
|
|
1183
1383
|
|
|
1184
1384
|
// src/hdc/exec.ts
|
|
@@ -1187,7 +1387,7 @@ function runProcess(opts) {
|
|
|
1187
1387
|
const { command, args, cwd, env, onOutput } = opts;
|
|
1188
1388
|
const timeoutMs = opts.timeoutMs ?? 6e4;
|
|
1189
1389
|
const { abortSignal } = opts;
|
|
1190
|
-
return new Promise((
|
|
1390
|
+
return new Promise((resolve4, reject) => {
|
|
1191
1391
|
if (abortSignal?.aborted) {
|
|
1192
1392
|
reject(new CancelledError(`Command aborted before start: ${command}`));
|
|
1193
1393
|
return;
|
|
@@ -1248,7 +1448,7 @@ ${stderr.trim()}` : "")
|
|
|
1248
1448
|
);
|
|
1249
1449
|
return;
|
|
1250
1450
|
}
|
|
1251
|
-
|
|
1451
|
+
resolve4({ code: code ?? -1, stdout, stderr });
|
|
1252
1452
|
});
|
|
1253
1453
|
});
|
|
1254
1454
|
}
|
|
@@ -1274,9 +1474,29 @@ function ensureOk(result, command) {
|
|
|
1274
1474
|
}
|
|
1275
1475
|
return result;
|
|
1276
1476
|
}
|
|
1477
|
+
var REMOTE_RC_RE = /__ONIRO_RC=(-?\d+)__/g;
|
|
1478
|
+
function parseRemoteExit(stdout) {
|
|
1479
|
+
let code = null;
|
|
1480
|
+
for (const m of stdout.matchAll(REMOTE_RC_RE)) code = Number(m[1]);
|
|
1481
|
+
if (code === null) return null;
|
|
1482
|
+
return { code, stdout: stdout.replace(REMOTE_RC_RE, "").replace(/[ \t\r\n]+$/, "") };
|
|
1483
|
+
}
|
|
1484
|
+
async function shellChecked(opts) {
|
|
1485
|
+
const { command, ...rest } = opts;
|
|
1486
|
+
const res = await hdcExec({ ...rest, args: ["shell", `${command}; echo "__ONIRO_RC=$?__"`] });
|
|
1487
|
+
const parsed = parseRemoteExit(res.stdout);
|
|
1488
|
+
if (!parsed) return res;
|
|
1489
|
+
return { code: parsed.code, stdout: parsed.stdout, stderr: res.stderr };
|
|
1490
|
+
}
|
|
1491
|
+
function ensureRemoteOk(result, command) {
|
|
1492
|
+
if (result.code !== 0) {
|
|
1493
|
+
throw new CommandFailedError(command, result.code, result.stderr.trim() || result.stdout.trim());
|
|
1494
|
+
}
|
|
1495
|
+
return result;
|
|
1496
|
+
}
|
|
1277
1497
|
|
|
1278
1498
|
// src/emulator/lifecycle.ts
|
|
1279
|
-
var PID_FILE =
|
|
1499
|
+
var PID_FILE = path14.join(os7.tmpdir(), "oniro_emulator.pid");
|
|
1280
1500
|
async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger = noopLogger) {
|
|
1281
1501
|
try {
|
|
1282
1502
|
await hdcExec({ config, args: ["start", "-r"], logger });
|
|
@@ -1289,24 +1509,24 @@ async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger
|
|
|
1289
1509
|
}
|
|
1290
1510
|
}
|
|
1291
1511
|
function resolveLauncher(imagesPath, headless, connect) {
|
|
1292
|
-
const runSh =
|
|
1293
|
-
const runBat =
|
|
1512
|
+
const runSh = path14.join(imagesPath, "run.sh");
|
|
1513
|
+
const runBat = path14.join(imagesPath, "run.bat");
|
|
1294
1514
|
const extra = [];
|
|
1295
1515
|
if (headless) extra.push("--headless");
|
|
1296
1516
|
if (connect) extra.push("--connect", connect);
|
|
1297
|
-
if (
|
|
1298
|
-
if (
|
|
1517
|
+
if (os7.platform() === "win32") {
|
|
1518
|
+
if (fs14.existsSync(runBat)) {
|
|
1299
1519
|
return { command: "cmd.exe", args: ["/c", runBat, ...extra], cwd: imagesPath };
|
|
1300
1520
|
}
|
|
1301
|
-
if (
|
|
1521
|
+
if (fs14.existsSync(runSh)) {
|
|
1302
1522
|
return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
|
|
1303
1523
|
}
|
|
1304
1524
|
throw new OniroError(`No emulator launcher (run.bat or run.sh) found in ${imagesPath}.`);
|
|
1305
1525
|
}
|
|
1306
|
-
if (
|
|
1526
|
+
if (fs14.existsSync(runSh)) {
|
|
1307
1527
|
return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
|
|
1308
1528
|
}
|
|
1309
|
-
if (
|
|
1529
|
+
if (fs14.existsSync(runBat)) {
|
|
1310
1530
|
return { command: "bash", args: [runBat, ...extra], cwd: imagesPath };
|
|
1311
1531
|
}
|
|
1312
1532
|
throw new OniroError(`No emulator launcher (run.sh or run.bat) found in ${imagesPath}.`);
|
|
@@ -1314,9 +1534,9 @@ function resolveLauncher(imagesPath, headless, connect) {
|
|
|
1314
1534
|
async function startEmulator(opts) {
|
|
1315
1535
|
const { config } = opts;
|
|
1316
1536
|
const logger = opts.logger ?? noopLogger;
|
|
1317
|
-
if (
|
|
1537
|
+
if (os7.platform() !== "win32" && fs14.existsSync(PID_FILE)) {
|
|
1318
1538
|
try {
|
|
1319
|
-
const pid = Number(
|
|
1539
|
+
const pid = Number(fs14.readFileSync(PID_FILE, "utf8").trim());
|
|
1320
1540
|
if (Number.isFinite(pid) && pid > 0) {
|
|
1321
1541
|
try {
|
|
1322
1542
|
process.kill(pid, 0);
|
|
@@ -1332,13 +1552,13 @@ async function startEmulator(opts) {
|
|
|
1332
1552
|
logger.warn(`Could not read PID file: ${err.message}`);
|
|
1333
1553
|
}
|
|
1334
1554
|
}
|
|
1335
|
-
const imagesPath =
|
|
1336
|
-
if (!
|
|
1555
|
+
const imagesPath = path14.join(getEmulatorDir(config), "images");
|
|
1556
|
+
if (!fs14.existsSync(imagesPath)) {
|
|
1337
1557
|
throw new OniroError(`Emulator images directory not found at ${imagesPath}. Run \`oniro-app emulator install\` first.`);
|
|
1338
1558
|
}
|
|
1339
1559
|
const launcher = resolveLauncher(imagesPath, opts.headless === true, opts.connect);
|
|
1340
|
-
const logPath = opts.logFile ?? (
|
|
1341
|
-
const logFd =
|
|
1560
|
+
const logPath = opts.logFile ?? (os7.platform() === "win32" ? "NUL" : "/dev/null");
|
|
1561
|
+
const logFd = fs14.openSync(logPath, "a");
|
|
1342
1562
|
let child;
|
|
1343
1563
|
try {
|
|
1344
1564
|
child = spawn2(launcher.command, [...launcher.args], {
|
|
@@ -1348,19 +1568,19 @@ async function startEmulator(opts) {
|
|
|
1348
1568
|
windowsHide: true
|
|
1349
1569
|
});
|
|
1350
1570
|
} finally {
|
|
1351
|
-
|
|
1571
|
+
fs14.closeSync(logFd);
|
|
1352
1572
|
}
|
|
1353
1573
|
if (!child.pid) {
|
|
1354
1574
|
throw new OniroError(`Failed to spawn launcher (${launcher.command} ${launcher.args.join(" ")}).`);
|
|
1355
1575
|
}
|
|
1356
|
-
|
|
1576
|
+
fs14.writeFileSync(PID_FILE, String(child.pid));
|
|
1357
1577
|
child.unref();
|
|
1358
1578
|
logger.info(`Emulator launched (PID ${child.pid}). Logs: ${logPath}`);
|
|
1359
|
-
const earlyExit = new Promise((
|
|
1360
|
-
child.once("exit", (code) =>
|
|
1361
|
-
child.once("error", () =>
|
|
1579
|
+
const earlyExit = new Promise((resolve4) => {
|
|
1580
|
+
child.once("exit", (code) => resolve4(code));
|
|
1581
|
+
child.once("error", () => resolve4(-1));
|
|
1362
1582
|
});
|
|
1363
|
-
const liveness = new Promise((
|
|
1583
|
+
const liveness = new Promise((resolve4) => setTimeout(() => resolve4("alive"), 2e3));
|
|
1364
1584
|
const outcome = await Promise.race([earlyExit, liveness]);
|
|
1365
1585
|
if (outcome !== "alive") {
|
|
1366
1586
|
const tail = readTail(logPath, 4e3);
|
|
@@ -1400,24 +1620,24 @@ ${tail}` : ""}`);
|
|
|
1400
1620
|
}
|
|
1401
1621
|
function readTail(filePath, bytes) {
|
|
1402
1622
|
try {
|
|
1403
|
-
const stat =
|
|
1623
|
+
const stat = fs14.statSync(filePath);
|
|
1404
1624
|
const start = Math.max(0, stat.size - bytes);
|
|
1405
|
-
const fd =
|
|
1625
|
+
const fd = fs14.openSync(filePath, "r");
|
|
1406
1626
|
try {
|
|
1407
1627
|
const buf = Buffer.alloc(stat.size - start);
|
|
1408
|
-
|
|
1628
|
+
fs14.readSync(fd, buf, 0, buf.length, start);
|
|
1409
1629
|
return buf.toString("utf8");
|
|
1410
1630
|
} finally {
|
|
1411
|
-
|
|
1631
|
+
fs14.closeSync(fd);
|
|
1412
1632
|
}
|
|
1413
1633
|
} catch {
|
|
1414
1634
|
return "";
|
|
1415
1635
|
}
|
|
1416
1636
|
}
|
|
1417
1637
|
async function stopEmulator(logger = noopLogger) {
|
|
1418
|
-
if (
|
|
1638
|
+
if (fs14.existsSync(PID_FILE)) {
|
|
1419
1639
|
try {
|
|
1420
|
-
const pid = Number(
|
|
1640
|
+
const pid = Number(fs14.readFileSync(PID_FILE, "utf8").trim());
|
|
1421
1641
|
if (Number.isFinite(pid) && pid > 0) {
|
|
1422
1642
|
try {
|
|
1423
1643
|
process.kill(pid, "SIGTERM");
|
|
@@ -1430,13 +1650,13 @@ async function stopEmulator(logger = noopLogger) {
|
|
|
1430
1650
|
logger.warn(`Could not read PID file: ${err.message}`);
|
|
1431
1651
|
}
|
|
1432
1652
|
}
|
|
1433
|
-
const [killCmd, ...killArgs] =
|
|
1653
|
+
const [killCmd, ...killArgs] = os7.platform() === "win32" ? ["taskkill", "/IM", "qemu-system-x86_64.exe", "/F"] : ["pkill", "-f", "qemu-system-x86_64"];
|
|
1434
1654
|
try {
|
|
1435
1655
|
await runProcess({ command: killCmd, args: killArgs, logger });
|
|
1436
1656
|
} catch (err) {
|
|
1437
1657
|
logger.warn(`[emulator] could not run ${killCmd}: ${err.message}`);
|
|
1438
1658
|
}
|
|
1439
|
-
|
|
1659
|
+
fs14.rm(PID_FILE, { force: true }, (err) => {
|
|
1440
1660
|
if (err) logger.warn(`Could not remove PID file: ${err.message}`);
|
|
1441
1661
|
});
|
|
1442
1662
|
}
|
|
@@ -1559,7 +1779,7 @@ async function getDisplaySize(config, opts = {}) {
|
|
|
1559
1779
|
|
|
1560
1780
|
// src/hdc/wait.ts
|
|
1561
1781
|
function delay(ms, signal) {
|
|
1562
|
-
return new Promise((
|
|
1782
|
+
return new Promise((resolve4, reject) => {
|
|
1563
1783
|
if (signal?.aborted) {
|
|
1564
1784
|
reject(new CancelledError("Wait cancelled."));
|
|
1565
1785
|
return;
|
|
@@ -1574,7 +1794,7 @@ function delay(ms, signal) {
|
|
|
1574
1794
|
};
|
|
1575
1795
|
const timer = setTimeout(() => {
|
|
1576
1796
|
cleanup();
|
|
1577
|
-
|
|
1797
|
+
resolve4();
|
|
1578
1798
|
}, ms);
|
|
1579
1799
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1580
1800
|
});
|
|
@@ -1603,21 +1823,21 @@ async function waitForCondition(opts) {
|
|
|
1603
1823
|
}
|
|
1604
1824
|
|
|
1605
1825
|
// src/hdc/app.ts
|
|
1606
|
-
import * as
|
|
1607
|
-
import * as
|
|
1826
|
+
import * as fs16 from "fs";
|
|
1827
|
+
import * as path16 from "path";
|
|
1608
1828
|
import { spawn as spawn3 } from "child_process";
|
|
1609
1829
|
|
|
1610
1830
|
// src/hdc/project.ts
|
|
1611
|
-
import * as
|
|
1612
|
-
import * as
|
|
1831
|
+
import * as fs15 from "fs";
|
|
1832
|
+
import * as path15 from "path";
|
|
1613
1833
|
import JSON53 from "json5";
|
|
1614
1834
|
function readJson5(filePath) {
|
|
1615
|
-
const content =
|
|
1835
|
+
const content = fs15.readFileSync(filePath, "utf-8");
|
|
1616
1836
|
return JSON53.parse(content);
|
|
1617
1837
|
}
|
|
1618
1838
|
function getBundleName(projectDir) {
|
|
1619
|
-
const appJsonPath =
|
|
1620
|
-
if (!
|
|
1839
|
+
const appJsonPath = path15.join(projectDir, "AppScope", "app.json5");
|
|
1840
|
+
if (!fs15.existsSync(appJsonPath)) {
|
|
1621
1841
|
throw new OniroError(`Could not find app.json5 at ${appJsonPath}`);
|
|
1622
1842
|
}
|
|
1623
1843
|
const appJson = readJson5(appJsonPath);
|
|
@@ -1627,8 +1847,8 @@ function getBundleName(projectDir) {
|
|
|
1627
1847
|
return appJson.app.bundleName;
|
|
1628
1848
|
}
|
|
1629
1849
|
function getMainAbility(projectDir, moduleName = "entry", abilityName) {
|
|
1630
|
-
const moduleJsonPath =
|
|
1631
|
-
if (!
|
|
1850
|
+
const moduleJsonPath = path15.join(projectDir, moduleName, "src", "main", "module.json5");
|
|
1851
|
+
if (!fs15.existsSync(moduleJsonPath)) {
|
|
1632
1852
|
throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
|
|
1633
1853
|
}
|
|
1634
1854
|
const moduleJson = readJson5(moduleJsonPath);
|
|
@@ -1651,8 +1871,8 @@ function getMainAbility(projectDir, moduleName = "entry", abilityName) {
|
|
|
1651
1871
|
async function installApp(opts) {
|
|
1652
1872
|
const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
|
|
1653
1873
|
const relativeHapPath = opts.hapPath ?? opts.config.get("hapPath", defaultPaths.hapPath);
|
|
1654
|
-
const hapPath =
|
|
1655
|
-
if (!
|
|
1874
|
+
const hapPath = path16.isAbsolute(relativeHapPath) ? relativeHapPath : path16.join(opts.projectDir, relativeHapPath);
|
|
1875
|
+
if (!fs16.existsSync(hapPath)) {
|
|
1656
1876
|
throw new OniroError(`HAP file not found at: ${hapPath}. Build and sign the app first.`);
|
|
1657
1877
|
}
|
|
1658
1878
|
const result = await hdcExec({ config: opts.config, args: ["install", hapPath], timeoutMs: 3e5 });
|
|
@@ -1669,13 +1889,59 @@ async function installApp(opts) {
|
|
|
1669
1889
|
function detectAaStartFailure(output) {
|
|
1670
1890
|
return /failed to start ability|Error Code:\s*\d|Error Message:/i.test(output) ? output.trim() : null;
|
|
1671
1891
|
}
|
|
1892
|
+
function parseMainAbilityFromBmDump(stdout) {
|
|
1893
|
+
const start = stdout.indexOf("{");
|
|
1894
|
+
if (start < 0) return null;
|
|
1895
|
+
let info;
|
|
1896
|
+
try {
|
|
1897
|
+
info = JSON.parse(stdout.slice(start));
|
|
1898
|
+
} catch {
|
|
1899
|
+
return null;
|
|
1900
|
+
}
|
|
1901
|
+
const mods = Array.isArray(info?.hapModuleInfos) ? info.hapModuleInfos : [];
|
|
1902
|
+
const abilityOf = (m) => m?.mainElementName || m?.mainAbility || void 0;
|
|
1903
|
+
const isEntry = (m) => m?.moduleType === 1 || m?.moduleType === "1" || m?.moduleType === "entry";
|
|
1904
|
+
const entry = mods.find((m) => isEntry(m) && abilityOf(m));
|
|
1905
|
+
const any = mods.find((m) => abilityOf(m));
|
|
1906
|
+
return entry && abilityOf(entry) || any && abilityOf(any) || null;
|
|
1907
|
+
}
|
|
1908
|
+
async function resolveInstalledMainAbility(opts) {
|
|
1909
|
+
const res = await shell({
|
|
1910
|
+
config: opts.config,
|
|
1911
|
+
command: `bm dump -n ${opts.bundle}`,
|
|
1912
|
+
deviceSerial: opts.deviceSerial,
|
|
1913
|
+
timeoutMs: 15e3,
|
|
1914
|
+
logger: opts.logger
|
|
1915
|
+
});
|
|
1916
|
+
const ability = parseMainAbilityFromBmDump(res.stdout);
|
|
1917
|
+
if (!ability) {
|
|
1918
|
+
throw new OniroError(
|
|
1919
|
+
`Could not determine the main ability for installed bundle ${opts.bundle} (is it installed?). Pass an explicit ability instead.`
|
|
1920
|
+
);
|
|
1921
|
+
}
|
|
1922
|
+
return ability;
|
|
1923
|
+
}
|
|
1672
1924
|
async function launchApp(opts) {
|
|
1673
1925
|
const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
|
|
1674
|
-
|
|
1675
|
-
|
|
1926
|
+
let bundleName;
|
|
1927
|
+
let mainAbility;
|
|
1928
|
+
if (opts.bundleName) {
|
|
1929
|
+
bundleName = opts.bundleName;
|
|
1930
|
+
mainAbility = opts.abilityName ?? await resolveInstalledMainAbility({
|
|
1931
|
+
config: opts.config,
|
|
1932
|
+
bundle: bundleName,
|
|
1933
|
+
deviceSerial: opts.deviceSerial,
|
|
1934
|
+
logger: opts.logger
|
|
1935
|
+
});
|
|
1936
|
+
} else {
|
|
1937
|
+
if (!opts.projectDir) throw new OniroError("launchApp requires either bundleName or projectDir.");
|
|
1938
|
+
bundleName = getBundleName(opts.projectDir);
|
|
1939
|
+
mainAbility = getMainAbility(opts.projectDir, opts.moduleName, opts.abilityName);
|
|
1940
|
+
}
|
|
1676
1941
|
const result = await hdcExec({
|
|
1677
1942
|
config: opts.config,
|
|
1678
|
-
args: ["shell", "aa", "start", "-a", mainAbility, "-b", bundleName]
|
|
1943
|
+
args: ["shell", "aa", "start", "-a", mainAbility, "-b", bundleName],
|
|
1944
|
+
deviceSerial: opts.deviceSerial
|
|
1679
1945
|
});
|
|
1680
1946
|
if (result.stdout.trim()) logger.info(result.stdout.trim());
|
|
1681
1947
|
if (result.stderr.trim()) logger.warn(result.stderr.trim());
|
|
@@ -1690,7 +1956,7 @@ function listRunningProcesses(config, options = {}) {
|
|
|
1690
1956
|
const logger = options.logger ?? noopLogger;
|
|
1691
1957
|
const timeoutMs = options.timeoutMs ?? 1e3;
|
|
1692
1958
|
const hdc = getHdcPath(config);
|
|
1693
|
-
return new Promise((
|
|
1959
|
+
return new Promise((resolve4, reject) => {
|
|
1694
1960
|
const proc = spawn3(hdc, ["track-jpid"]);
|
|
1695
1961
|
const processes = [];
|
|
1696
1962
|
let resolved = false;
|
|
@@ -1704,7 +1970,7 @@ function listRunningProcesses(config, options = {}) {
|
|
|
1704
1970
|
if (options.targetProcessName && name === options.targetProcessName && !resolved) {
|
|
1705
1971
|
resolved = true;
|
|
1706
1972
|
proc.kill();
|
|
1707
|
-
|
|
1973
|
+
resolve4(pid);
|
|
1708
1974
|
return;
|
|
1709
1975
|
}
|
|
1710
1976
|
}
|
|
@@ -1725,7 +1991,7 @@ function listRunningProcesses(config, options = {}) {
|
|
|
1725
1991
|
if (options.targetProcessName) {
|
|
1726
1992
|
reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
|
|
1727
1993
|
} else {
|
|
1728
|
-
|
|
1994
|
+
resolve4(processes);
|
|
1729
1995
|
}
|
|
1730
1996
|
}, timeoutMs);
|
|
1731
1997
|
proc.on("close", () => {
|
|
@@ -1735,7 +2001,7 @@ function listRunningProcesses(config, options = {}) {
|
|
|
1735
2001
|
if (options.targetProcessName) {
|
|
1736
2002
|
reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
|
|
1737
2003
|
} else {
|
|
1738
|
-
|
|
2004
|
+
resolve4(processes);
|
|
1739
2005
|
}
|
|
1740
2006
|
});
|
|
1741
2007
|
});
|
|
@@ -1755,8 +2021,17 @@ async function findRunningProcess(opts) {
|
|
|
1755
2021
|
logger: opts.logger
|
|
1756
2022
|
});
|
|
1757
2023
|
const pid = res.stdout.trim().split(/\s+/).filter(Boolean)[0];
|
|
1758
|
-
if (res.code
|
|
1759
|
-
|
|
2024
|
+
if (res.code === 0 && pid && /^\d+$/.test(pid)) return { pid, name: opts.bundle };
|
|
2025
|
+
try {
|
|
2026
|
+
const list = await listRunningProcesses(opts.config, { timeoutMs: 1200, logger: opts.logger });
|
|
2027
|
+
if (Array.isArray(list)) {
|
|
2028
|
+
const mine = (name) => name === opts.bundle || name.startsWith(`${opts.bundle}/`) || name.startsWith(`${opts.bundle}:`);
|
|
2029
|
+
const match = list.find((p) => p.name === opts.bundle) ?? list.find((p) => mine(p.name));
|
|
2030
|
+
if (match) return match;
|
|
2031
|
+
}
|
|
2032
|
+
} catch {
|
|
2033
|
+
}
|
|
2034
|
+
return null;
|
|
1760
2035
|
}
|
|
1761
2036
|
async function uninstallApp(opts) {
|
|
1762
2037
|
const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
|
|
@@ -1870,12 +2145,12 @@ async function reboot(opts) {
|
|
|
1870
2145
|
}
|
|
1871
2146
|
|
|
1872
2147
|
// src/hdc/screen.ts
|
|
1873
|
-
import * as
|
|
1874
|
-
import * as
|
|
1875
|
-
import * as
|
|
2148
|
+
import * as fs17 from "fs";
|
|
2149
|
+
import * as os8 from "os";
|
|
2150
|
+
import * as path17 from "path";
|
|
1876
2151
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
1877
2152
|
function tmpName(ext) {
|
|
1878
|
-
return
|
|
2153
|
+
return path17.join(os8.tmpdir(), `oniro_screen_${randomBytes2(6).toString("hex")}.${ext}`);
|
|
1879
2154
|
}
|
|
1880
2155
|
async function takeScreenshot(opts) {
|
|
1881
2156
|
const remote = "/data/local/tmp/oniro_screenshot.jpeg";
|
|
@@ -1885,11 +2160,11 @@ async function takeScreenshot(opts) {
|
|
|
1885
2160
|
const local = tmpName("jpeg");
|
|
1886
2161
|
try {
|
|
1887
2162
|
await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
|
|
1888
|
-
const pixels = await
|
|
2163
|
+
const pixels = await fs17.promises.readFile(local);
|
|
1889
2164
|
const size = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial }) ?? { width: 0, height: 0 };
|
|
1890
2165
|
return { pixels, width: size.width, height: size.height };
|
|
1891
2166
|
} finally {
|
|
1892
|
-
|
|
2167
|
+
fs17.promises.unlink(local).catch(() => {
|
|
1893
2168
|
});
|
|
1894
2169
|
void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
|
|
1895
2170
|
});
|
|
@@ -1908,9 +2183,9 @@ async function captureBurst(opts) {
|
|
|
1908
2183
|
const local = tmpName(`burst_${i}.jpeg`);
|
|
1909
2184
|
try {
|
|
1910
2185
|
await recvFile({ config: opts.config, remote: `${remoteDir}/f${i}.jpeg`, local, deviceSerial: opts.deviceSerial });
|
|
1911
|
-
frames.push(await
|
|
2186
|
+
frames.push(await fs17.promises.readFile(local));
|
|
1912
2187
|
} finally {
|
|
1913
|
-
|
|
2188
|
+
fs17.promises.unlink(local).catch(() => {
|
|
1914
2189
|
});
|
|
1915
2190
|
}
|
|
1916
2191
|
}
|
|
@@ -1954,8 +2229,8 @@ function buildInputCommand(opts) {
|
|
|
1954
2229
|
}
|
|
1955
2230
|
async function sendInput(opts) {
|
|
1956
2231
|
const cmd = buildInputCommand(opts);
|
|
1957
|
-
const res = await
|
|
1958
|
-
|
|
2232
|
+
const res = await shellChecked({ config: opts.config, command: cmd, deviceSerial: opts.deviceSerial, timeoutMs: 3e4, logger: opts.logger });
|
|
2233
|
+
ensureRemoteOk(res, `hdc shell ${cmd}`);
|
|
1959
2234
|
}
|
|
1960
2235
|
function buildGestureCommands(waypoints) {
|
|
1961
2236
|
if (waypoints.length < 2) {
|
|
@@ -1998,32 +2273,47 @@ function waypointsToRawEvents(waypoints, holdStartMs = 0, holdEndMs = 0) {
|
|
|
1998
2273
|
events.push({ type: "up", x: last.x, y: last.y, t: last.t + holdStartMs + holdEndMs });
|
|
1999
2274
|
return events;
|
|
2000
2275
|
}
|
|
2276
|
+
var UITEST_VELOCITY_MIN = 200;
|
|
2277
|
+
var UITEST_VELOCITY_MAX = 4e4;
|
|
2278
|
+
function buildGestureUitestCommand(waypoints, opts = {}) {
|
|
2279
|
+
if (waypoints.length < 2) throw new OniroError("sendGesture requires at least two waypoints.");
|
|
2280
|
+
const a = waypoints[0];
|
|
2281
|
+
const b = waypoints[waypoints.length - 1];
|
|
2282
|
+
const dist = Math.hypot(b.x - a.x, b.y - a.y);
|
|
2283
|
+
const dtMs = b.t - a.t;
|
|
2284
|
+
const op = (opts.holdStartMs ?? 0) > 0 ? "drag" : "swipe";
|
|
2285
|
+
let cmd = `uitest uiInput ${op} ${a.x} ${a.y} ${b.x} ${b.y}`;
|
|
2286
|
+
if (dtMs > 0 && dist > 0) {
|
|
2287
|
+
const v = Math.round(dist / (dtMs / 1e3));
|
|
2288
|
+
cmd += ` ${Math.max(UITEST_VELOCITY_MIN, Math.min(UITEST_VELOCITY_MAX, v))}`;
|
|
2289
|
+
}
|
|
2290
|
+
return cmd;
|
|
2291
|
+
}
|
|
2001
2292
|
async function sendGesture(opts) {
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
events: waypointsToRawEvents(opts.waypoints, opts.holdStartMs, opts.holdEndMs),
|
|
2007
|
-
deviceSerial: opts.deviceSerial,
|
|
2008
|
-
logger: opts.logger
|
|
2009
|
-
});
|
|
2010
|
-
return;
|
|
2293
|
+
if ((opts.holdEndMs ?? 0) > 0) {
|
|
2294
|
+
opts.logger?.warn(
|
|
2295
|
+
"sendGesture: hold-end has no uitest equivalent and is ignored; use sendRawTouch for a trailing hold."
|
|
2296
|
+
);
|
|
2011
2297
|
}
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2298
|
+
if (opts.waypoints.length > 2) {
|
|
2299
|
+
opts.logger?.debug(
|
|
2300
|
+
`sendGesture: ${opts.waypoints.length} waypoints collapsed to first\u2192last (uitest has no polyline path).`
|
|
2301
|
+
);
|
|
2015
2302
|
}
|
|
2303
|
+
const cmd = buildGestureUitestCommand(opts.waypoints, { holdStartMs: opts.holdStartMs });
|
|
2304
|
+
const res = await shellChecked({ config: opts.config, command: cmd, deviceSerial: opts.deviceSerial, timeoutMs: 3e4, logger: opts.logger });
|
|
2305
|
+
ensureRemoteOk(res, `hdc shell ${cmd}`);
|
|
2016
2306
|
}
|
|
2017
2307
|
async function sendRawTouch(opts) {
|
|
2018
2308
|
const cmd = buildRawTouchCommand(opts.events);
|
|
2019
|
-
const res = await
|
|
2020
|
-
|
|
2309
|
+
const res = await shellChecked({ config: opts.config, command: cmd, deviceSerial: opts.deviceSerial, timeoutMs: 3e4, logger: opts.logger });
|
|
2310
|
+
ensureRemoteOk(res, `hdc shell ${cmd}`);
|
|
2021
2311
|
}
|
|
2022
2312
|
|
|
2023
2313
|
// src/hdc/dump.ts
|
|
2024
|
-
import * as
|
|
2025
|
-
import * as
|
|
2026
|
-
import * as
|
|
2314
|
+
import * as fs18 from "fs";
|
|
2315
|
+
import * as os9 from "os";
|
|
2316
|
+
import * as path18 from "path";
|
|
2027
2317
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
2028
2318
|
function parseBounds(s) {
|
|
2029
2319
|
const m = /^\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]$/.exec(s ?? "");
|
|
@@ -2108,10 +2398,10 @@ async function dumpLayout(opts) {
|
|
|
2108
2398
|
if (!m) throw new OniroError(`Could not parse dumpLayout output:
|
|
2109
2399
|
${dump.stdout}`);
|
|
2110
2400
|
const remote = m[1];
|
|
2111
|
-
const local =
|
|
2401
|
+
const local = path18.join(os9.tmpdir(), `oniro_layout_${randomBytes3(6).toString("hex")}.json`);
|
|
2112
2402
|
try {
|
|
2113
2403
|
await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
|
|
2114
|
-
const parsed = JSON.parse(await
|
|
2404
|
+
const parsed = JSON.parse(await fs18.promises.readFile(local, "utf8"));
|
|
2115
2405
|
const display = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial });
|
|
2116
2406
|
if (opts.prune === false) {
|
|
2117
2407
|
return { display, tree: parsed };
|
|
@@ -2120,7 +2410,7 @@ ${dump.stdout}`);
|
|
|
2120
2410
|
const tree = Array.isArray(prunedRaw) ? { children: prunedRaw } : prunedRaw ?? { children: [] };
|
|
2121
2411
|
return { display, tree };
|
|
2122
2412
|
} finally {
|
|
2123
|
-
|
|
2413
|
+
fs18.promises.unlink(local).catch(() => {
|
|
2124
2414
|
});
|
|
2125
2415
|
void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
|
|
2126
2416
|
});
|
|
@@ -2132,7 +2422,7 @@ import { spawn as spawn4 } from "child_process";
|
|
|
2132
2422
|
function setHilogLevel(opts) {
|
|
2133
2423
|
const logger = opts.logger ?? noopLogger;
|
|
2134
2424
|
const hdc = getHdcPath(opts.config);
|
|
2135
|
-
return new Promise((
|
|
2425
|
+
return new Promise((resolve4, reject) => {
|
|
2136
2426
|
const child = spawn4(hdc, ["shell", "hilog", "-b", opts.level]);
|
|
2137
2427
|
let stderr = "";
|
|
2138
2428
|
child.stderr.on("data", (chunk) => {
|
|
@@ -2144,7 +2434,7 @@ function setHilogLevel(opts) {
|
|
|
2144
2434
|
});
|
|
2145
2435
|
child.once("close", (code) => {
|
|
2146
2436
|
if (code === 0) {
|
|
2147
|
-
|
|
2437
|
+
resolve4();
|
|
2148
2438
|
} else {
|
|
2149
2439
|
const msg = stderr.trim() || `hdc shell hilog -b ${opts.level} exited with code ${code}`;
|
|
2150
2440
|
logger.error(`[hilog] ${msg}`);
|
|
@@ -2199,7 +2489,7 @@ async function resolveProcessId(config, bundle, deviceSerial) {
|
|
|
2199
2489
|
return proc?.pid;
|
|
2200
2490
|
}
|
|
2201
2491
|
function waitForLog(opts) {
|
|
2202
|
-
return new Promise((
|
|
2492
|
+
return new Promise((resolve4, reject) => {
|
|
2203
2493
|
const deadline = Date.now() + opts.timeoutMs;
|
|
2204
2494
|
let settled = false;
|
|
2205
2495
|
let child = null;
|
|
@@ -2221,7 +2511,7 @@ function waitForLog(opts) {
|
|
|
2221
2511
|
if (settled) return;
|
|
2222
2512
|
settled = true;
|
|
2223
2513
|
cleanup();
|
|
2224
|
-
|
|
2514
|
+
resolve4(entry);
|
|
2225
2515
|
};
|
|
2226
2516
|
const fail = (err) => {
|
|
2227
2517
|
if (settled) return;
|
|
@@ -2268,7 +2558,7 @@ function waitForLog(opts) {
|
|
|
2268
2558
|
});
|
|
2269
2559
|
}
|
|
2270
2560
|
function watchLog(opts) {
|
|
2271
|
-
return new Promise((
|
|
2561
|
+
return new Promise((resolve4, reject) => {
|
|
2272
2562
|
const matches = [];
|
|
2273
2563
|
let settled = false;
|
|
2274
2564
|
let child = null;
|
|
@@ -2288,7 +2578,7 @@ function watchLog(opts) {
|
|
|
2288
2578
|
if (settled) return;
|
|
2289
2579
|
settled = true;
|
|
2290
2580
|
cleanup();
|
|
2291
|
-
|
|
2581
|
+
resolve4(opts.dedup === false ? matches : dedupEntries(matches));
|
|
2292
2582
|
};
|
|
2293
2583
|
const onAbort = () => {
|
|
2294
2584
|
if (settled) return;
|
|
@@ -2340,8 +2630,8 @@ async function dumpLog(opts) {
|
|
|
2340
2630
|
}
|
|
2341
2631
|
|
|
2342
2632
|
// src/build/runHvigorw.ts
|
|
2343
|
-
import * as
|
|
2344
|
-
import * as
|
|
2633
|
+
import * as fs19 from "fs";
|
|
2634
|
+
import * as path19 from "path";
|
|
2345
2635
|
import { spawn as spawn5 } from "child_process";
|
|
2346
2636
|
function buildHvigorwArgs(opts) {
|
|
2347
2637
|
const args = [opts.task ?? "assembleHap", "--mode", "module"];
|
|
@@ -2356,17 +2646,17 @@ function buildHvigorwArgs(opts) {
|
|
|
2356
2646
|
function runHvigorw(opts) {
|
|
2357
2647
|
const { config, projectDir } = opts;
|
|
2358
2648
|
const logger = opts.logger ?? noopLogger;
|
|
2359
|
-
if (!
|
|
2649
|
+
if (!fs19.existsSync(path19.join(projectDir, "build-profile.json5"))) {
|
|
2360
2650
|
throw new OniroError(`Not an OpenHarmony project: ${projectDir} (build-profile.json5 not found).`);
|
|
2361
2651
|
}
|
|
2362
|
-
if (!
|
|
2652
|
+
if (!fs19.existsSync(getCmdToolsPath(config))) {
|
|
2363
2653
|
throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
|
|
2364
2654
|
}
|
|
2365
2655
|
const hvigorw = getHvigorwPath(config, projectDir);
|
|
2366
2656
|
const args = buildHvigorwArgs(opts);
|
|
2367
2657
|
if (process.platform !== "win32") {
|
|
2368
2658
|
try {
|
|
2369
|
-
|
|
2659
|
+
fs19.chmodSync(hvigorw, 493);
|
|
2370
2660
|
} catch {
|
|
2371
2661
|
}
|
|
2372
2662
|
}
|
|
@@ -2374,7 +2664,7 @@ function runHvigorw(opts) {
|
|
|
2374
2664
|
...process.env,
|
|
2375
2665
|
OHOS_BASE_SDK_HOME: getOhosBaseSdkHome(config)
|
|
2376
2666
|
};
|
|
2377
|
-
return new Promise((
|
|
2667
|
+
return new Promise((resolve4, reject) => {
|
|
2378
2668
|
if (opts.abortSignal?.aborted) {
|
|
2379
2669
|
reject(new CancelledError("hvigorw build cancelled."));
|
|
2380
2670
|
return;
|
|
@@ -2415,19 +2705,18 @@ function runHvigorw(opts) {
|
|
|
2415
2705
|
settled = true;
|
|
2416
2706
|
cleanup();
|
|
2417
2707
|
const exitCode = code ?? 1;
|
|
2418
|
-
if (exitCode === 0)
|
|
2708
|
+
if (exitCode === 0) resolve4({ exitCode });
|
|
2419
2709
|
else reject(new OniroError(`hvigorw exited with code ${exitCode}`));
|
|
2420
2710
|
});
|
|
2421
2711
|
});
|
|
2422
2712
|
}
|
|
2423
2713
|
|
|
2424
2714
|
// src/build/discoverHaps.ts
|
|
2425
|
-
import * as
|
|
2426
|
-
import * as
|
|
2427
|
-
function
|
|
2428
|
-
const
|
|
2429
|
-
|
|
2430
|
-
return first && first !== ".." ? first : "(root)";
|
|
2715
|
+
import * as fs20 from "fs";
|
|
2716
|
+
import * as path20 from "path";
|
|
2717
|
+
function moduleOfHap(hapFileName) {
|
|
2718
|
+
const dash = hapFileName.indexOf("-");
|
|
2719
|
+
return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
|
|
2431
2720
|
}
|
|
2432
2721
|
async function discoverHaps(opts) {
|
|
2433
2722
|
const { projectDir } = opts;
|
|
@@ -2435,14 +2724,14 @@ async function discoverHaps(opts) {
|
|
|
2435
2724
|
const result = {};
|
|
2436
2725
|
const walk = async (dir, depth) => {
|
|
2437
2726
|
if (depth > maxDepth) return;
|
|
2438
|
-
const entries = await
|
|
2727
|
+
const entries = await fs20.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
2439
2728
|
for (const e of entries) {
|
|
2440
|
-
const full =
|
|
2729
|
+
const full = path20.join(dir, e.name);
|
|
2441
2730
|
if (e.isDirectory()) {
|
|
2442
2731
|
if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
|
|
2443
2732
|
await walk(full, depth + 1);
|
|
2444
2733
|
} else if (e.isFile() && e.name.endsWith(".hap")) {
|
|
2445
|
-
const moduleName =
|
|
2734
|
+
const moduleName = moduleOfHap(e.name);
|
|
2446
2735
|
const bucket = result[moduleName] ??= { signed: [], unsigned: [] };
|
|
2447
2736
|
if (e.name.includes("unsigned")) bucket.unsigned.push(full);
|
|
2448
2737
|
else if (e.name.includes("signed")) bucket.signed.push(full);
|
|
@@ -2454,8 +2743,8 @@ async function discoverHaps(opts) {
|
|
|
2454
2743
|
}
|
|
2455
2744
|
|
|
2456
2745
|
// src/build/ohpm.ts
|
|
2457
|
-
import * as
|
|
2458
|
-
import * as
|
|
2746
|
+
import * as fs21 from "fs";
|
|
2747
|
+
import * as path21 from "path";
|
|
2459
2748
|
function runOhpm(opts) {
|
|
2460
2749
|
return runProcess({
|
|
2461
2750
|
command: getOhpmPath(opts.config),
|
|
@@ -2468,7 +2757,7 @@ function runOhpm(opts) {
|
|
|
2468
2757
|
});
|
|
2469
2758
|
}
|
|
2470
2759
|
async function ensureOhModules(opts) {
|
|
2471
|
-
if (
|
|
2760
|
+
if (fs21.existsSync(path21.join(opts.projectDir, "oh_modules"))) {
|
|
2472
2761
|
return { installed: false };
|
|
2473
2762
|
}
|
|
2474
2763
|
const res = await runOhpm({
|
|
@@ -2563,21 +2852,21 @@ function isValidBundleName(bundleName) {
|
|
|
2563
2852
|
}
|
|
2564
2853
|
|
|
2565
2854
|
// src/project/templates.ts
|
|
2566
|
-
import * as
|
|
2567
|
-
import * as
|
|
2855
|
+
import * as fs22 from "fs";
|
|
2856
|
+
import * as path22 from "path";
|
|
2568
2857
|
function toHumanTemplateName(folderName) {
|
|
2569
2858
|
return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
|
|
2570
2859
|
}
|
|
2571
2860
|
function listTemplates(templateRoot) {
|
|
2572
|
-
if (!
|
|
2573
|
-
const entries =
|
|
2861
|
+
if (!fs22.existsSync(templateRoot)) return [];
|
|
2862
|
+
const entries = fs22.readdirSync(templateRoot, { withFileTypes: true });
|
|
2574
2863
|
return entries.filter((e) => e.isDirectory()).map((e) => {
|
|
2575
|
-
const dir =
|
|
2576
|
-
const metaPath =
|
|
2864
|
+
const dir = path22.join(templateRoot, e.name);
|
|
2865
|
+
const metaPath = path22.join(dir, "template.json");
|
|
2577
2866
|
let meta = {};
|
|
2578
|
-
if (
|
|
2867
|
+
if (fs22.existsSync(metaPath)) {
|
|
2579
2868
|
try {
|
|
2580
|
-
meta = JSON.parse(
|
|
2869
|
+
meta = JSON.parse(fs22.readFileSync(metaPath, "utf8"));
|
|
2581
2870
|
} catch {
|
|
2582
2871
|
}
|
|
2583
2872
|
}
|
|
@@ -2592,57 +2881,57 @@ function listTemplates(templateRoot) {
|
|
|
2592
2881
|
function validateTemplateLayout(templateDir, defaultModuleName) {
|
|
2593
2882
|
const required = [
|
|
2594
2883
|
"build-profile.json5",
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2884
|
+
path22.join("AppScope", "app.json5"),
|
|
2885
|
+
path22.join(defaultModuleName, "src", "main", "module.json5"),
|
|
2886
|
+
path22.join(defaultModuleName, "oh-package.json5"),
|
|
2598
2887
|
"hvigorfile.ts"
|
|
2599
2888
|
];
|
|
2600
|
-
return required.filter((rel) => !
|
|
2889
|
+
return required.filter((rel) => !fs22.existsSync(path22.join(templateDir, rel)));
|
|
2601
2890
|
}
|
|
2602
2891
|
|
|
2603
2892
|
// src/project/createScaffold.ts
|
|
2604
|
-
import * as
|
|
2605
|
-
import * as
|
|
2893
|
+
import * as fs24 from "fs";
|
|
2894
|
+
import * as path23 from "path";
|
|
2606
2895
|
|
|
2607
2896
|
// src/project/jsonHelpers.ts
|
|
2608
|
-
import * as
|
|
2897
|
+
import * as fs23 from "fs";
|
|
2609
2898
|
import JSON54 from "json5";
|
|
2610
2899
|
function readJson5File(filePath) {
|
|
2611
|
-
return JSON54.parse(
|
|
2900
|
+
return JSON54.parse(fs23.readFileSync(filePath, "utf8"));
|
|
2612
2901
|
}
|
|
2613
2902
|
function writeJson5File(filePath, value) {
|
|
2614
|
-
|
|
2903
|
+
fs23.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
2615
2904
|
}
|
|
2616
2905
|
function readJsonFile(filePath) {
|
|
2617
|
-
return JSON.parse(
|
|
2906
|
+
return JSON.parse(fs23.readFileSync(filePath, "utf8"));
|
|
2618
2907
|
}
|
|
2619
2908
|
function writeJsonFile(filePath, value) {
|
|
2620
|
-
|
|
2909
|
+
fs23.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
2621
2910
|
}
|
|
2622
2911
|
|
|
2623
2912
|
// src/project/createScaffold.ts
|
|
2624
2913
|
var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
|
|
2625
2914
|
async function pathExists(p) {
|
|
2626
2915
|
try {
|
|
2627
|
-
await
|
|
2916
|
+
await fs24.promises.access(p);
|
|
2628
2917
|
return true;
|
|
2629
2918
|
} catch {
|
|
2630
2919
|
return false;
|
|
2631
2920
|
}
|
|
2632
2921
|
}
|
|
2633
2922
|
async function copyDirRecursive(srcDir, destDir) {
|
|
2634
|
-
await
|
|
2635
|
-
const entries = await
|
|
2923
|
+
await fs24.promises.mkdir(destDir, { recursive: true });
|
|
2924
|
+
const entries = await fs24.promises.readdir(srcDir, { withFileTypes: true });
|
|
2636
2925
|
for (const entry of entries) {
|
|
2637
|
-
const src =
|
|
2638
|
-
const dest =
|
|
2926
|
+
const src = path23.join(srcDir, entry.name);
|
|
2927
|
+
const dest = path23.join(destDir, entry.name);
|
|
2639
2928
|
if (entry.isDirectory()) {
|
|
2640
2929
|
await copyDirRecursive(src, dest);
|
|
2641
2930
|
} else if (entry.isSymbolicLink()) {
|
|
2642
2931
|
continue;
|
|
2643
2932
|
} else if (entry.isFile()) {
|
|
2644
2933
|
if (entry.name === "template.json") continue;
|
|
2645
|
-
await
|
|
2934
|
+
await fs24.promises.copyFile(src, dest);
|
|
2646
2935
|
}
|
|
2647
2936
|
}
|
|
2648
2937
|
}
|
|
@@ -2650,9 +2939,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
|
|
|
2650
2939
|
const stack = [projectDir];
|
|
2651
2940
|
while (stack.length > 0) {
|
|
2652
2941
|
const currentDir = stack.pop();
|
|
2653
|
-
const entries = await
|
|
2942
|
+
const entries = await fs24.promises.readdir(currentDir, { withFileTypes: true });
|
|
2654
2943
|
for (const entry of entries) {
|
|
2655
|
-
const fullPath =
|
|
2944
|
+
const fullPath = path23.join(currentDir, entry.name);
|
|
2656
2945
|
if (entry.isDirectory()) {
|
|
2657
2946
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
2658
2947
|
stack.push(fullPath);
|
|
@@ -2674,23 +2963,23 @@ function toJavaPropertiesPath(p) {
|
|
|
2674
2963
|
function createOrUpdateLocalProperties(projectDir, sdkDir) {
|
|
2675
2964
|
const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
|
|
2676
2965
|
`;
|
|
2677
|
-
|
|
2966
|
+
fs24.writeFileSync(path23.join(projectDir, "local.properties"), content, "utf8");
|
|
2678
2967
|
}
|
|
2679
2968
|
function renameIfExists(fromPath, toPath) {
|
|
2680
|
-
if (
|
|
2681
|
-
|
|
2969
|
+
if (fs24.existsSync(fromPath) && fromPath !== toPath) {
|
|
2970
|
+
fs24.renameSync(fromPath, toPath);
|
|
2682
2971
|
}
|
|
2683
2972
|
}
|
|
2684
2973
|
function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
|
|
2685
|
-
const appJsonPath =
|
|
2686
|
-
if (
|
|
2974
|
+
const appJsonPath = path23.join(projectDir, "AppScope", "app.json5");
|
|
2975
|
+
if (fs24.existsSync(appJsonPath)) {
|
|
2687
2976
|
const appJson = readJson5File(appJsonPath);
|
|
2688
2977
|
appJson.app = appJson.app ?? {};
|
|
2689
2978
|
appJson.app.bundleName = args.bundleName;
|
|
2690
2979
|
writeJson5File(appJsonPath, appJson);
|
|
2691
2980
|
}
|
|
2692
|
-
const stringsPath =
|
|
2693
|
-
if (
|
|
2981
|
+
const stringsPath = path23.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
|
|
2982
|
+
if (fs24.existsSync(stringsPath)) {
|
|
2694
2983
|
const strings = readJsonFile(stringsPath);
|
|
2695
2984
|
if (Array.isArray(strings.string)) {
|
|
2696
2985
|
const appName = strings.string.find((s) => s.name === "app_name");
|
|
@@ -2698,8 +2987,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
|
|
|
2698
2987
|
}
|
|
2699
2988
|
writeJsonFile(stringsPath, strings);
|
|
2700
2989
|
}
|
|
2701
|
-
const buildProfilePath =
|
|
2702
|
-
if (
|
|
2990
|
+
const buildProfilePath = path23.join(projectDir, "build-profile.json5");
|
|
2991
|
+
if (fs24.existsSync(buildProfilePath)) {
|
|
2703
2992
|
const buildProfile = readJson5File(buildProfilePath);
|
|
2704
2993
|
buildProfile.app = buildProfile.app ?? {};
|
|
2705
2994
|
if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
|
|
@@ -2712,23 +3001,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
|
|
|
2712
3001
|
}
|
|
2713
3002
|
writeJson5File(buildProfilePath, buildProfile);
|
|
2714
3003
|
}
|
|
2715
|
-
const moduleJsonPath =
|
|
2716
|
-
if (
|
|
3004
|
+
const moduleJsonPath = path23.join(projectDir, args.moduleName, "src", "main", "module.json5");
|
|
3005
|
+
if (fs24.existsSync(moduleJsonPath)) {
|
|
2717
3006
|
const moduleJson = readJson5File(moduleJsonPath);
|
|
2718
3007
|
moduleJson.module = moduleJson.module ?? {};
|
|
2719
3008
|
moduleJson.module.name = args.moduleName;
|
|
2720
3009
|
writeJson5File(moduleJsonPath, moduleJson);
|
|
2721
3010
|
}
|
|
2722
|
-
const moduleOhPackagePath =
|
|
2723
|
-
if (
|
|
3011
|
+
const moduleOhPackagePath = path23.join(projectDir, args.moduleName, "oh-package.json5");
|
|
3012
|
+
if (fs24.existsSync(moduleOhPackagePath)) {
|
|
2724
3013
|
const pkg = readJson5File(moduleOhPackagePath);
|
|
2725
3014
|
pkg.name = args.moduleName;
|
|
2726
3015
|
writeJson5File(moduleOhPackagePath, pkg);
|
|
2727
3016
|
}
|
|
2728
|
-
const vscodeDir =
|
|
2729
|
-
|
|
3017
|
+
const vscodeDir = path23.join(projectDir, ".vscode");
|
|
3018
|
+
fs24.mkdirSync(vscodeDir, { recursive: true });
|
|
2730
3019
|
const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
|
|
2731
|
-
writeJsonFile(
|
|
3020
|
+
writeJsonFile(path23.join(vscodeDir, "settings.json"), {
|
|
2732
3021
|
"oniro.hapPath": hapPath,
|
|
2733
3022
|
"files.associations": { "*.json5": "jsonc" }
|
|
2734
3023
|
});
|
|
@@ -2745,7 +3034,7 @@ async function createScaffold(opts) {
|
|
|
2745
3034
|
if (!opts.location || !await pathExists(opts.location)) {
|
|
2746
3035
|
throw new OniroError(`Location does not exist: ${opts.location}`);
|
|
2747
3036
|
}
|
|
2748
|
-
const templateDir =
|
|
3037
|
+
const templateDir = path23.join(opts.templateRoot, opts.templateId);
|
|
2749
3038
|
if (!await pathExists(templateDir)) {
|
|
2750
3039
|
throw new OniroError(`Template not found: ${templateDir}`);
|
|
2751
3040
|
}
|
|
@@ -2758,17 +3047,17 @@ async function createScaffold(opts) {
|
|
|
2758
3047
|
throw new OniroError(`Template '${opts.templateId}' is missing required files:
|
|
2759
3048
|
- ${missing.join("\n- ")}`);
|
|
2760
3049
|
}
|
|
2761
|
-
const projectDir =
|
|
3050
|
+
const projectDir = path23.join(opts.location, opts.projectName);
|
|
2762
3051
|
if (await pathExists(projectDir)) {
|
|
2763
3052
|
if (!opts.overwrite) {
|
|
2764
3053
|
throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
|
|
2765
3054
|
}
|
|
2766
|
-
await
|
|
3055
|
+
await fs24.promises.rm(projectDir, { recursive: true, force: true });
|
|
2767
3056
|
}
|
|
2768
3057
|
logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
|
|
2769
3058
|
await copyDirRecursive(templateDir, projectDir);
|
|
2770
3059
|
if (moduleName !== defaultModuleName) {
|
|
2771
|
-
renameIfExists(
|
|
3060
|
+
renameIfExists(path23.join(projectDir, defaultModuleName), path23.join(projectDir, moduleName));
|
|
2772
3061
|
}
|
|
2773
3062
|
updateTemplateConfigs(
|
|
2774
3063
|
projectDir,
|
|
@@ -2785,11 +3074,11 @@ async function createScaffold(opts) {
|
|
|
2785
3074
|
}
|
|
2786
3075
|
|
|
2787
3076
|
// src/project/listModules.ts
|
|
2788
|
-
import * as
|
|
2789
|
-
import * as
|
|
3077
|
+
import * as fs25 from "fs";
|
|
3078
|
+
import * as path24 from "path";
|
|
2790
3079
|
function listModules(opts) {
|
|
2791
|
-
const buildProfile =
|
|
2792
|
-
if (!
|
|
3080
|
+
const buildProfile = path24.join(opts.projectDir, "build-profile.json5");
|
|
3081
|
+
if (!fs25.existsSync(buildProfile)) {
|
|
2793
3082
|
throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
|
|
2794
3083
|
}
|
|
2795
3084
|
const parsed = readJson5File(buildProfile);
|
|
@@ -2801,12 +3090,12 @@ function listModules(opts) {
|
|
|
2801
3090
|
}
|
|
2802
3091
|
|
|
2803
3092
|
// src/project/abilities.ts
|
|
2804
|
-
import * as
|
|
2805
|
-
import * as
|
|
3093
|
+
import * as fs26 from "fs";
|
|
3094
|
+
import * as path25 from "path";
|
|
2806
3095
|
function listAbilities(opts) {
|
|
2807
3096
|
const moduleName = opts.moduleName ?? "entry";
|
|
2808
|
-
const moduleJsonPath =
|
|
2809
|
-
if (!
|
|
3097
|
+
const moduleJsonPath = path25.join(opts.projectDir, moduleName, "src", "main", "module.json5");
|
|
3098
|
+
if (!fs26.existsSync(moduleJsonPath)) {
|
|
2810
3099
|
throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
|
|
2811
3100
|
}
|
|
2812
3101
|
const parsed = readJson5File(moduleJsonPath);
|
|
@@ -2814,12 +3103,12 @@ function listAbilities(opts) {
|
|
|
2814
3103
|
}
|
|
2815
3104
|
|
|
2816
3105
|
// src/project/prepareSigning.ts
|
|
2817
|
-
import * as
|
|
2818
|
-
import * as
|
|
3106
|
+
import * as fs27 from "fs";
|
|
3107
|
+
import * as path26 from "path";
|
|
2819
3108
|
function hasSigningArtifacts(dir) {
|
|
2820
|
-
if (!
|
|
2821
|
-
const buildProfile =
|
|
2822
|
-
if (!
|
|
3109
|
+
if (!fs27.existsSync(path26.join(dir, "signatures"))) return false;
|
|
3110
|
+
const buildProfile = path26.join(dir, "build-profile.json5");
|
|
3111
|
+
if (!fs27.existsSync(buildProfile)) return false;
|
|
2823
3112
|
try {
|
|
2824
3113
|
const parsed = readJson5File(buildProfile);
|
|
2825
3114
|
return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
|
|
@@ -2909,8 +3198,8 @@ async function diffHapAssets(opts) {
|
|
|
2909
3198
|
}
|
|
2910
3199
|
|
|
2911
3200
|
// src/install/apply.ts
|
|
2912
|
-
import * as
|
|
2913
|
-
import * as
|
|
3201
|
+
import * as fs28 from "fs";
|
|
3202
|
+
import * as path27 from "path";
|
|
2914
3203
|
function decideInstallMethod(s) {
|
|
2915
3204
|
if (!s.signInfoInconsistent) return "replace";
|
|
2916
3205
|
if (s.isSystemBundle && !s.allowUninstall) return "refuse";
|
|
@@ -2931,7 +3220,7 @@ async function pidOf(config, bundle, deviceSerial) {
|
|
|
2931
3220
|
async function resolveHapPath(opts) {
|
|
2932
3221
|
let hapPath = opts.hapPath;
|
|
2933
3222
|
if (hapPath) {
|
|
2934
|
-
if (!
|
|
3223
|
+
if (!path27.isAbsolute(hapPath) && opts.projectDir) hapPath = path27.join(opts.projectDir, hapPath);
|
|
2935
3224
|
} else {
|
|
2936
3225
|
if (!opts.projectDir) throw new OniroError("applyChanges requires hapPath or projectDir.");
|
|
2937
3226
|
const haps = await discoverHaps({ projectDir: opts.projectDir });
|
|
@@ -2950,7 +3239,7 @@ async function resolveHapPath(opts) {
|
|
|
2950
3239
|
hapPath = withSigned[0][1].signed[0];
|
|
2951
3240
|
}
|
|
2952
3241
|
}
|
|
2953
|
-
if (!
|
|
3242
|
+
if (!fs28.existsSync(hapPath)) throw new OniroError(`HAP not found at ${hapPath}.`);
|
|
2954
3243
|
return hapPath;
|
|
2955
3244
|
}
|
|
2956
3245
|
async function applyChanges(opts) {
|
|
@@ -2960,7 +3249,7 @@ async function applyChanges(opts) {
|
|
|
2960
3249
|
const allowUninstall = opts.allowUninstall ?? false;
|
|
2961
3250
|
const hapPath = await resolveHapPath(opts);
|
|
2962
3251
|
let assetsChanged = false;
|
|
2963
|
-
if (opts.installedHapPath &&
|
|
3252
|
+
if (opts.installedHapPath && fs28.existsSync(opts.installedHapPath)) {
|
|
2964
3253
|
try {
|
|
2965
3254
|
const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
|
|
2966
3255
|
assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
|
|
@@ -3002,7 +3291,7 @@ async function applyChanges(opts) {
|
|
|
3002
3291
|
}
|
|
3003
3292
|
|
|
3004
3293
|
// src/index.ts
|
|
3005
|
-
var VERSION = "0.
|
|
3294
|
+
var VERSION = "0.8.0";
|
|
3006
3295
|
export {
|
|
3007
3296
|
ALL_SDKS,
|
|
3008
3297
|
APL_VALUES,
|
|
@@ -3011,22 +3300,28 @@ export {
|
|
|
3011
3300
|
ChecksumMismatchError,
|
|
3012
3301
|
CmdToolsNotInstalledError,
|
|
3013
3302
|
CommandFailedError,
|
|
3303
|
+
INSTALL_TMP_DIRNAME,
|
|
3304
|
+
InsufficientSpaceError,
|
|
3014
3305
|
OHOS_URL_BASE,
|
|
3015
3306
|
OniroError,
|
|
3016
3307
|
SdkNotInstalledError,
|
|
3308
|
+
TMP_DIR_HINT,
|
|
3017
3309
|
UnsupportedPlatformError,
|
|
3018
3310
|
VERSION,
|
|
3019
3311
|
applyChanges,
|
|
3020
3312
|
attemptHdcConnection,
|
|
3021
3313
|
buildGestureCommands,
|
|
3314
|
+
buildGestureUitestCommand,
|
|
3022
3315
|
buildHap,
|
|
3023
3316
|
buildHvigorwArgs,
|
|
3024
3317
|
buildInputCommand,
|
|
3025
3318
|
buildRawTouchCommand,
|
|
3026
3319
|
captureBurst,
|
|
3027
3320
|
consoleLogger,
|
|
3321
|
+
createInstallTempDir,
|
|
3028
3322
|
createMaterial,
|
|
3029
3323
|
createScaffold,
|
|
3324
|
+
createTempWorkDir,
|
|
3030
3325
|
decideInstallMethod,
|
|
3031
3326
|
decideReboot,
|
|
3032
3327
|
decryptPwd,
|
|
@@ -3047,14 +3342,17 @@ export {
|
|
|
3047
3342
|
dumpScreen,
|
|
3048
3343
|
dumpWindow,
|
|
3049
3344
|
encryptPwd,
|
|
3345
|
+
ensureFreeSpace,
|
|
3050
3346
|
ensureOhModules,
|
|
3051
3347
|
ensureOk,
|
|
3348
|
+
ensureRemoteOk,
|
|
3052
3349
|
extractTarball,
|
|
3053
3350
|
extractZipWithProgress,
|
|
3054
3351
|
findAppProcessId,
|
|
3055
3352
|
findCmdToolsSourceDir,
|
|
3056
3353
|
findRunningProcess,
|
|
3057
3354
|
forceStop,
|
|
3355
|
+
formatBytes,
|
|
3058
3356
|
generateSigningConfigs,
|
|
3059
3357
|
getBundleName,
|
|
3060
3358
|
getCmdToolsBin,
|
|
@@ -3064,6 +3362,7 @@ export {
|
|
|
3064
3362
|
getDeviceInfo,
|
|
3065
3363
|
getDisplaySize,
|
|
3066
3364
|
getEmulatorDir,
|
|
3365
|
+
getFreeSpaceBytes,
|
|
3067
3366
|
getHdcPath,
|
|
3068
3367
|
getHvigorwPath,
|
|
3069
3368
|
getInstalledSdks,
|
|
@@ -3081,6 +3380,8 @@ export {
|
|
|
3081
3380
|
installEmulator,
|
|
3082
3381
|
isCmdToolsInstalled,
|
|
3083
3382
|
isEmulatorInstalled,
|
|
3383
|
+
isOutOfSpaceError,
|
|
3384
|
+
isRamBackedPath,
|
|
3084
3385
|
isValidBundleName,
|
|
3085
3386
|
isValidProjectName,
|
|
3086
3387
|
launchApp,
|
|
@@ -3098,6 +3399,8 @@ export {
|
|
|
3098
3399
|
parseCodelinterFindings,
|
|
3099
3400
|
parseDeviceList,
|
|
3100
3401
|
parseHilogLine,
|
|
3402
|
+
parseMainAbilityFromBmDump,
|
|
3403
|
+
parseRemoteExit,
|
|
3101
3404
|
pickSigningKind,
|
|
3102
3405
|
prepareSigning,
|
|
3103
3406
|
pruneLayout,
|
|
@@ -3108,6 +3411,8 @@ export {
|
|
|
3108
3411
|
removeCmdTools,
|
|
3109
3412
|
removeEmulator,
|
|
3110
3413
|
removeSdk,
|
|
3414
|
+
removeTempWorkDir,
|
|
3415
|
+
resolveTmpRoot,
|
|
3111
3416
|
runCodelinter,
|
|
3112
3417
|
runHvigorw,
|
|
3113
3418
|
runOhpm,
|
|
@@ -3120,11 +3425,13 @@ export {
|
|
|
3120
3425
|
sendRawTouch,
|
|
3121
3426
|
setHilogLevel,
|
|
3122
3427
|
shell,
|
|
3428
|
+
shellChecked,
|
|
3123
3429
|
startEmulator,
|
|
3124
3430
|
staticConfig,
|
|
3125
3431
|
stopEmulator,
|
|
3126
3432
|
streamHilog,
|
|
3127
3433
|
takeScreenshot,
|
|
3434
|
+
toSpaceError,
|
|
3128
3435
|
uninstallApp,
|
|
3129
3436
|
validateTemplateLayout,
|
|
3130
3437
|
verifySha256,
|
|
@@ -3133,6 +3440,7 @@ export {
|
|
|
3133
3440
|
waitForCondition,
|
|
3134
3441
|
waitForLog,
|
|
3135
3442
|
watchLog,
|
|
3443
|
+
waypointsToRawEvents,
|
|
3136
3444
|
writeJson5File,
|
|
3137
3445
|
writeJsonFile
|
|
3138
3446
|
};
|