@aiot-toolkit/emulator 2.0.5-widget-provider-beta.1 → 2.0.5

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/lib/vvd/index.js CHANGED
@@ -3,8 +3,9 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.VvdManager = void 0;
6
+ exports.isHeadlessEnvironment = exports.VvdManager = void 0;
7
7
  var _ColorConsole = _interopRequireDefault(require("@aiot-toolkit/shared-utils/lib/ColorConsole"));
8
+ var _promises = _interopRequireDefault(require("fs/promises"));
8
9
  var _fs = _interopRequireDefault(require("fs"));
9
10
  var _os = _interopRequireDefault(require("os"));
10
11
  var _path = _interopRequireDefault(require("path"));
@@ -26,6 +27,7 @@ var _logcat = require("./logcat");
26
27
  var _sharedUtils = require("@aiot-toolkit/shared-utils");
27
28
  var _ILog = require("@aiot-toolkit/shared-utils/lib/interface/ILog");
28
29
  var _grpc = require("./grpc");
30
+ var _file = require("../utils/file");
29
31
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
30
32
  // TODO: 升级构建工具支持 esm @xujunjie
31
33
  const getPort = (async () => {
@@ -42,6 +44,21 @@ const EAvdParamsToIni = {
42
44
  abiType: 'arm64-v8a'
43
45
  }
44
46
  };
47
+ const isHeadlessEnvironment = () => {
48
+ // 检查是否在 Docker 容器中
49
+ if (_fs.default.existsSync('/.dockerenv')) return true;
50
+
51
+ // 检查是否在 WSL 环境中
52
+ if (process.env.WSL_DISTRO_NAME) return true;
53
+
54
+ // 检查是否设置了 DISPLAY 环境变量(Linux)
55
+ if (process.platform === 'linux' && !process.env.DISPLAY) return true;
56
+
57
+ // 检查是否在 CI 环境中
58
+ if (process.env.CI) return true;
59
+ return false;
60
+ };
61
+ exports.isHeadlessEnvironment = isHeadlessEnvironment;
45
62
  class VvdManager {
46
63
  // 需要复制的文件
47
64
  binFiles = ['system.img', 'data.img', 'coredump.core', 'vela_data.bin', 'vela_resource.bin', 'vela_system.bin'];
@@ -62,13 +79,13 @@ class VvdManager {
62
79
  return _path.default.resolve(__dirname, '../static/debugger_ip.cfg');
63
80
  }
64
81
 
65
- /**
66
- * 创建Vela端的 VVD ,统一保存在 .vela/vvd 目录下
67
- * 1. 创建.vela/advancedFeatures.ini文件
68
- * 2. 创建.vela/vvd/${avdName}.ini文件
69
- * 3. 创建.vela/vvd/${avdName}.vvd/config.ini文件
70
- * @param vvdParams VVD参数,宽高、绑定的镜像路径等
71
- * @returns
82
+ /**
83
+ * 创建Vela端的 VVD ,统一保存在 .vela/vvd 目录下
84
+ * 1. 创建.vela/advancedFeatures.ini文件
85
+ * 2. 创建.vela/vvd/${avdName}.ini文件
86
+ * 3. 创建.vela/vvd/${avdName}.vvd/config.ini文件
87
+ * @param vvdParams VVD参数,宽高、绑定的镜像路径等
88
+ * @returns
72
89
  */
73
90
  createVvd(vvdParams) {
74
91
  const {
@@ -144,10 +161,13 @@ class VvdManager {
144
161
  }
145
162
  }
146
163
  getVvdDir(vvdName) {
147
- // 模拟器有可能在 .vvd .avd 目录下
148
- let res = _path.default.resolve(this.vvdHome, `${vvdName}.vvd`);
149
- if (!_fs.default.existsSync(res)) res = _path.default.resolve(this.vvdHome, `${vvdName}.avd`);
150
- return res;
164
+ const maybe = [_path.default.join(this.vvdHome, `${vvdName}.ini`), _path.default.join(_os.default.homedir(), '.android', 'avd', `${vvdName}.ini`), _path.default.join(_os.default.homedir(), '.android', 'vvd', `${vvdName}.ini`)];
165
+ for (const file of maybe) {
166
+ if (_fs.default.existsSync(file)) {
167
+ return (0, _ini.parse)(_fs.default.readFileSync(file, 'utf-8')).path;
168
+ }
169
+ }
170
+ throw new Error(`VVD directory for ${vvdName} not found`);
151
171
  }
152
172
 
153
173
  /** 根据AVD名字获取模拟器的详细信息 */
@@ -333,6 +353,54 @@ class VvdManager {
333
353
  const emulatorHome = _path.default.resolve(sdkHome, 'emulator');
334
354
  return _path.default.resolve(emulatorHome, `${platform}-${arch}`, 'emulator');
335
355
  }
356
+
357
+ // 旧的模拟器迁移
358
+ async oldEmulatorMigrate(vvdName) {
359
+ const configIni = _path.default.join(this.getVvdDir(vvdName), 'config.ini');
360
+ const contents = await _promises.default.readFile(configIni, 'utf-8');
361
+ const config = (0, _ini.parse)(contents);
362
+ let needUpdate = false;
363
+
364
+ // 检查 ramsize 调整为 1024
365
+ if (config['hw.ramSize'] < 1024) {
366
+ needUpdate = true;
367
+ config['hw.ramSize'] = 1024;
368
+ }
369
+ // 检查皮肤路径
370
+ if (config['skin.path']?.includes('.export_dev/skins')) {
371
+ if (config['skin.path'].includes('.export_dev/skins/user')) {
372
+ // 自定义的皮肤,将自定义皮肤迁移到新的 sdk 目录中
373
+ const oldUserSkinDir = _path.default.join(_os.default.homedir(), '.export_dev/skins/user');
374
+ const newUserSkinDir = _path.default.join(this.sdkHome, 'skins', 'user');
375
+ const userSkins = await _promises.default.readdir(oldUserSkinDir, {
376
+ withFileTypes: true
377
+ });
378
+ for (const skin of userSkins) {
379
+ if (!skin.isDirectory()) continue;
380
+ const oldSkinPath = _path.default.join(oldUserSkinDir, skin.name);
381
+ const newSkinPath = _path.default.join(newUserSkinDir, skin.name);
382
+ if (!_fs.default.existsSync(newSkinPath)) {
383
+ await _promises.default.mkdir(newSkinPath, {
384
+ recursive: true
385
+ });
386
+ }
387
+ await (0, _file.copyDir)(oldSkinPath, newSkinPath);
388
+ }
389
+ }
390
+ needUpdate = true;
391
+ config['skin.path'] = config['skin.path'].replace('.export_dev/skins', '.vela/sdk/skins');
392
+ }
393
+
394
+ // 检查镜像路径
395
+ if (config['image.sysdir.2']?.includes('.export_dev/system-images')) {
396
+ needUpdate = true;
397
+ config['image.sysdir.2'] = config['image.sysdir.2'].replace('.export_dev/system-images', '.vela/sdk/system-images');
398
+ }
399
+ if (needUpdate) {
400
+ await _promises.default.writeFile(configIni, (0, _ini.stringify)(config));
401
+ _ColorConsole.default.log(`update ${vvdName} config.ini`);
402
+ }
403
+ }
336
404
  async getVvdStartCmd(options) {
337
405
  const vvdName = options.vvdName;
338
406
 
@@ -365,7 +433,11 @@ class VvdManager {
365
433
  const qemuOption = `-device virtio-snd,bus=virtio-mmio-bus.2 -allow-host-audio -semihosting`;
366
434
 
367
435
  // qt windows 配置
368
- const windowOption = options.qtHideWindow ? `-no-window` : '';
436
+ // docker,wls,等无界面平台上用 -no-window ,否则用 -qt-hide-window
437
+ // 使用 -no-window 时 extended control 不可用
438
+ // 在无界面平台上必须使用 -no-window 否则会报错 qt `Could not load the Qt platform plugin \"xcb\"
439
+ const noWindowOption = isHeadlessEnvironment() ? `-no-window` : '-qt-hide-window';
440
+ const windowOption = options.qtHideWindow ? noWindowOption : '';
369
441
  let grpcStr = options.grpcPort ? `-grpc ${options.grpcPort}` : '';
370
442
  let serialStr = ``;
371
443
  if (options.serialPort) {
@@ -379,6 +451,7 @@ class VvdManager {
379
451
  // 启动模拟器的命令和参数
380
452
  const cmd = `${emulatorBin} -vela -avd ${options.vvdName} ${serialStr} -show-kernel ${portMappingStr} ${windowOption} ${grpcStr} ${verboseOption} -qemu ${qemuOption}`;
381
453
  const vvdInfo = this.getVvdInfo(vvdName);
454
+ await this.oldEmulatorMigrate(vvdName);
382
455
  if (!vvdInfo.imageDir) {
383
456
  const errMsg = `${vvdName} is not supported`;
384
457
  _ColorConsole.default.throw(errMsg);
@@ -394,12 +467,14 @@ class VvdManager {
394
467
  } else continue;
395
468
  if (!_fs.default.existsSync(pOfVvd)) {
396
469
  // 文件不存在则直接复制
470
+ _ColorConsole.default.log(`${file} not found, copy from ${pOfImageDir}`);
397
471
  _fs.default.copyFileSync(pOfImageDir, pOfVvd);
398
472
  } else {
399
473
  // 文件存在但过时
400
474
  const statsInAvd = _fs.default.statSync(pOfVvd);
401
475
  const stats = _fs.default.statSync(pOfImageDir);
402
476
  if ((0, _dayjs.default)(stats.mtime).isAfter(statsInAvd.mtime)) {
477
+ _ColorConsole.default.log(`${file} file is outdate, update from ${pOfImageDir}`);
403
478
  _fs.default.copyFileSync(pOfImageDir, pOfVvd);
404
479
  }
405
480
  }
@@ -448,7 +523,8 @@ class VvdManager {
448
523
  return {
449
524
  coldBoot: false,
450
525
  emulatorInstance,
451
- getAgent: () => (0, _grpc.createGrpcClient)(e)
526
+ getAgent: () => (0, _grpc.createGrpcClient)(e),
527
+ grpcConfig: e
452
528
  };
453
529
  }
454
530
 
@@ -471,10 +547,9 @@ class VvdManager {
471
547
 
472
548
  // 利用 readline 接口可解决子进程日志换行的问题
473
549
  const readlines = (0, _logcat.attachReadline)(emulatorProcess, onStdout, onErrout);
474
- const emulatorStartedHandler = async msg => {
550
+ const emulatorStartedHandler = msg => {
475
551
  if (func(msg)) {
476
- const runningVvds = await (0, _emulatorutil.getRunningVvds)();
477
- const e = runningVvds.find(e => e['avd.id'] === vvdName);
552
+ const e = (0, _emulatorutil.getRunningAvdConfigByName)(vvdName);
478
553
  if (e) {
479
554
  const emulatorInstance = (0, _instance.findInstance)(vvdInfo.imageType, {
480
555
  serialPort: e['port.serial'],
@@ -491,7 +566,8 @@ class VvdManager {
491
566
  resolve({
492
567
  coldBoot: true,
493
568
  emulatorInstance,
494
- getAgent: () => (0, _grpc.createGrpcClient)(e)
569
+ getAgent: () => (0, _grpc.createGrpcClient)(e),
570
+ grpcConfig: e
495
571
  });
496
572
  } else {
497
573
  reject('get emulator running config failed');
@@ -663,31 +739,31 @@ class VvdManager {
663
739
  }
664
740
  }
665
741
 
666
- /**
667
- * 下载 SDK,默认只下载需要更新的部分
668
- * @param opt.force 强制下载所有部分
669
- *
670
- * @example
671
- * async function main() {
672
- * const sdkHome = path.resolve(os.homedir(), '.export_dev1')
673
- * const velaAvdCls = new VelaAvdCls({ sdkHome })
674
- *
675
- * const downloder = await velaAvdCls.downloadSDK({
676
- * force: true,
677
- * cliProgress: false,
678
- * parallelDownloads: 6
679
- * })
680
- *
681
- * downloder.on('progress', (progress) => {
682
- * console.log(
683
- * `progress: ${progress.formattedSpeed} ${progress.formattedPercentage} ${progress.formatTotal} ${progress.formatTimeLeft}`
684
- * )
685
- * })
686
- *
687
- * await downloder.downlodPromise
688
- *
689
- * console.log('download success')
690
- * }
742
+ /**
743
+ * 下载 SDK,默认只下载需要更新的部分
744
+ * @param opt.force 强制下载所有部分
745
+ *
746
+ * @example
747
+ * async function main() {
748
+ * const sdkHome = path.resolve(os.homedir(), '.export_dev1')
749
+ * const velaAvdCls = new VelaAvdCls({ sdkHome })
750
+ *
751
+ * const downloder = await velaAvdCls.downloadSDK({
752
+ * force: true,
753
+ * cliProgress: false,
754
+ * parallelDownloads: 6
755
+ * })
756
+ *
757
+ * downloder.on('progress', (progress) => {
758
+ * console.log(
759
+ * `progress: ${progress.formattedSpeed} ${progress.formattedPercentage} ${progress.formatTotal} ${progress.formatTimeLeft}`
760
+ * )
761
+ * })
762
+ *
763
+ * await downloder.downlodPromise
764
+ *
765
+ * console.log('download success')
766
+ * }
691
767
  */
692
768
  async downloadSDK(opt) {
693
769
  const updateList = opt.force ? Object.values(_Vvd.SDKParts) : await this.hasSDKPartUpdate();
@@ -744,6 +820,10 @@ class VvdManager {
744
820
  zip.extractAllTo(targetDir, true, true);
745
821
  await _fs.default.promises.rm(zipFile, {
746
822
  force: true
823
+ }).catch(e => {
824
+ _ColorConsole.default.info(`remove ${zipFile} failed: ${e}`);
825
+ // my be failed on windows as resource busy or locked, don't care
826
+ return Promise.resolve();
747
827
  });
748
828
  }
749
829
  const verFile = this.getSDKVersionPath();
@@ -773,6 +853,10 @@ class VvdManager {
773
853
  zip.extractAllTo(_path.default.resolve(this.sdkHome, _Vvd.SDKParts.SYSTEM_IMAGES, imageId), true, true);
774
854
  await _fs.default.promises.rm(zipFile, {
775
855
  force: true
856
+ }).catch(e => {
857
+ _ColorConsole.default.info(`remove ${zipFile} failed: ${e}`);
858
+ // my be failed on windows as resource busy or locked, don't care
859
+ return Promise.resolve();
776
860
  });
777
861
  });
778
862
  return Object.assign(downloader, {
package/lib/vvd/logcat.js CHANGED
@@ -27,7 +27,7 @@ function attachReadline(p, onStdout, onErrout) {
27
27
  let stderrReadline;
28
28
 
29
29
  // 利用 readline 接口可解决子进程日志换行的问题
30
- if (p.stdout) {
30
+ if (p?.stdout) {
31
31
  stdoutReadline = _readline.default.createInterface({
32
32
  input: p.stdout
33
33
  });
@@ -39,7 +39,7 @@ function attachReadline(p, onStdout, onErrout) {
39
39
  stdoutReadline = undefined;
40
40
  });
41
41
  }
42
- if (p.stderr) {
42
+ if (p?.stderr) {
43
43
  stderrReadline = _readline.default.createInterface({
44
44
  input: p.stderr
45
45
  });
@@ -59,7 +59,7 @@ function attachReadline(p, onStdout, onErrout) {
59
59
  stdoutReadline = undefined;
60
60
  stderrReadline = undefined;
61
61
  }
62
- p.on('exit', () => {
62
+ p?.on('exit', () => {
63
63
  dispose();
64
64
  });
65
65
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiot-toolkit/emulator",
3
- "version": "2.0.5-widget-provider-beta.1",
3
+ "version": "2.0.5",
4
4
  "description": "vela emulator tool.",
5
5
  "homepage": "",
6
6
  "license": "ISC",
@@ -36,7 +36,7 @@
36
36
  "emulator"
37
37
  ],
38
38
  "dependencies": {
39
- "@aiot-toolkit/shared-utils": "2.0.5-widget-provider-beta.1",
39
+ "@aiot-toolkit/shared-utils": "2.0.5",
40
40
  "@grpc/grpc-js": "^1.13.3",
41
41
  "@grpc/proto-loader": "^0.7.13",
42
42
  "@miwt/adb": "0.10.1",
@@ -53,5 +53,5 @@
53
53
  "@types/adm-zip": "^0.5.5",
54
54
  "@types/ini": "^4.1.1"
55
55
  },
56
- "gitHead": "64842a2c374b8b3b344cef377d9c032eaf1a99a7"
56
+ "gitHead": "deb83e8b8fa65191a286690efe3d957d7df772f4"
57
57
  }