@aiot-toolkit/emulator 2.0.2-dev.8 → 2.0.3-beta.1
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 +14 -1
- package/lib/avd/index.d.ts +16 -0
- package/lib/avd/index.js +52 -28
- package/lib/emulatorutil/EmulatorCmd.d.ts +9 -0
- package/lib/emulatorutil/EmulatorCmd.js +226 -0
- package/lib/emulatorutil/EmulatorLog.d.ts +11 -0
- package/lib/emulatorutil/EmulatorLog.js +21 -0
- package/lib/emulatorutil/constants.d.ts +31 -0
- package/lib/emulatorutil/constants.js +63 -0
- package/lib/emulatorutil/index.d.ts +3 -0
- package/lib/emulatorutil/index.js +10 -0
- package/lib/index.d.ts +3 -1
- package/lib/index.js +4 -1
- package/lib/instance/common.d.ts +15 -12
- package/lib/instance/common.js +89 -153
- package/lib/instance/dev.d.ts +42 -6
- package/lib/instance/dev.js +137 -83
- package/lib/instance/index.d.ts +10 -2
- package/lib/instance/index.js +14 -2
- package/lib/instance/miwear.d.ts +66 -12
- package/lib/instance/miwear.js +220 -182
- package/lib/instance/pre.d.ts +11 -0
- package/lib/instance/pre.js +100 -0
- package/lib/instance/preDev.d.ts +37 -7
- package/lib/instance/preDev.js +80 -106
- package/lib/static/advancedFeatures.ini +1 -0
- package/lib/static/avdConfigIni.json +4 -4
- package/lib/typing/Avd.d.ts +1 -0
- package/lib/typing/Instance.d.ts +16 -3
- package/lib/typing/Instance.js +6 -0
- package/lib/utils/index.d.ts +21 -0
- package/lib/utils/index.js +67 -10
- package/package.json +15 -5
- package/lib/static/constants.d.ts +0 -19
- package/lib/static/constants.js +0 -27
package/README.md
CHANGED
|
@@ -1,2 +1,15 @@
|
|
|
1
|
+
## emulator
|
|
1
2
|
|
|
2
|
-
|
|
3
|
+
QEMU模拟器
|
|
4
|
+
|
|
5
|
+
模拟器的介绍可参考[开发帮助文档](https://xiaomi.f.mioffice.cn/docx/doxk4Rk6x67GanHlzQ8bEOrxtEe)里的「模拟器」章节
|
|
6
|
+
|
|
7
|
+
## 目录结构
|
|
8
|
+
|
|
9
|
+
| 目录 | 描述 |
|
|
10
|
+
| -------- | ------------------------------------------------------------------------------- |
|
|
11
|
+
| avd | 模拟器的AVD,配置统一放置$HOME/.android/avd目录下 |
|
|
12
|
+
| instance | 模拟器实例,不同的Vela镜像版本会使用不同的instance,通过findInstance确定 |
|
|
13
|
+
| static | 创建AVD时需要用到的静态资源,常量配置文件 |
|
|
14
|
+
| typing | 接口定义 |
|
|
15
|
+
| utils | 工具函数 |
|
package/lib/avd/index.d.ts
CHANGED
|
@@ -3,10 +3,26 @@ declare class VelaAvdCls {
|
|
|
3
3
|
private avdHome;
|
|
4
4
|
private sdkHome;
|
|
5
5
|
constructor(avdResourcePaths: IAvdResourcePaths);
|
|
6
|
+
/**
|
|
7
|
+
* 创建Vela端的AVD,统一保存在.android目录下
|
|
8
|
+
* 1. 创建.android/advancedFeatures.ini文件
|
|
9
|
+
* 2. 创建.android/${avdName}.ini文件
|
|
10
|
+
* 3. 创建.android/${avdName}.avd/config.ini文件
|
|
11
|
+
* @param avdParams AVD参数,宽高、绑定的镜像路径等
|
|
12
|
+
* @returns
|
|
13
|
+
*/
|
|
6
14
|
createVelaAvd(avdParams: IAvdParams): boolean;
|
|
15
|
+
/** 根据AVD名字获取模拟器的详细信息 */
|
|
7
16
|
getVelaAvdInfo(avdName: string): IAvdParams;
|
|
17
|
+
/** 根据名字删除AVD */
|
|
8
18
|
deleteVelaAvd(avdName: string): boolean;
|
|
19
|
+
/** 获取已经创建的模拟器列表 */
|
|
9
20
|
getVelaAvdList(): IAvdParams[];
|
|
21
|
+
/** 获取模拟器皮肤列表 */
|
|
10
22
|
getVelaSkinList(): string[];
|
|
23
|
+
/** 自定义模拟器的镜像目录 */
|
|
24
|
+
customImageDir(avdName: string, target: string): void;
|
|
25
|
+
/** 重置自定义的镜像目录 */
|
|
26
|
+
resetImageDir(avdName: string): void;
|
|
11
27
|
}
|
|
12
28
|
export default VelaAvdCls;
|
package/lib/avd/index.js
CHANGED
|
@@ -5,15 +5,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const ColorConsole_1 = __importDefault(require("@aiot-toolkit/shared-utils/lib/ColorConsole"));
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const os_1 = __importDefault(require("os"));
|
|
8
9
|
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const
|
|
10
|
+
const ini_1 = require("ini");
|
|
11
|
+
const constants_1 = require("../emulatorutil/constants");
|
|
10
12
|
const Avd_1 = require("../typing/Avd");
|
|
11
13
|
const EAvdParamsToIni = {
|
|
12
|
-
|
|
13
|
-
abiType: 'armeabi-v7a'
|
|
14
|
+
arm: {
|
|
15
|
+
abiType: 'armeabi-v7a'
|
|
14
16
|
},
|
|
15
|
-
|
|
16
|
-
abiType: 'arm64-v8a'
|
|
17
|
+
arm64: {
|
|
18
|
+
abiType: 'arm64-v8a'
|
|
17
19
|
}
|
|
18
20
|
};
|
|
19
21
|
class VelaAvdCls {
|
|
@@ -25,7 +27,21 @@ class VelaAvdCls {
|
|
|
25
27
|
fs_1.default.mkdirSync(this.avdHome, { recursive: true });
|
|
26
28
|
}
|
|
27
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* 创建Vela端的AVD,统一保存在.android目录下
|
|
32
|
+
* 1. 创建.android/advancedFeatures.ini文件
|
|
33
|
+
* 2. 创建.android/${avdName}.ini文件
|
|
34
|
+
* 3. 创建.android/${avdName}.avd/config.ini文件
|
|
35
|
+
* @param avdParams AVD参数,宽高、绑定的镜像路径等
|
|
36
|
+
* @returns
|
|
37
|
+
*/
|
|
28
38
|
createVelaAvd(avdParams) {
|
|
39
|
+
// 在.android下创建advancedFeatures.ini文件
|
|
40
|
+
const advancedFeaturesIni = path_1.default.resolve(os_1.default.homedir(), '.android/advancedFeatures.ini');
|
|
41
|
+
if (!fs_1.default.existsSync(advancedFeaturesIni)) {
|
|
42
|
+
const iniSourcePath = path_1.default.join(__dirname, '../static/advancedFeatures.ini');
|
|
43
|
+
fs_1.default.copyFileSync(iniSourcePath, advancedFeaturesIni);
|
|
44
|
+
}
|
|
29
45
|
const { avdName, avdArch, avdWidth, avdHeight, avdSkin, avdImagePath = constants_1.defaultImageHome } = avdParams;
|
|
30
46
|
const avdDir = path_1.default.resolve(this.avdHome, `${avdName}.avd`);
|
|
31
47
|
const avdIni = path_1.default.resolve(this.avdHome, `${avdName}.ini`);
|
|
@@ -57,18 +73,14 @@ class VelaAvdCls {
|
|
|
57
73
|
// 写入Vela_Virtual_Device.ini文件
|
|
58
74
|
fs_1.default.writeFileSync(avdIni, nuttxAvdIniContent);
|
|
59
75
|
// 写入Vela_Virtual_Device.avd/config.ini文件
|
|
60
|
-
|
|
61
|
-
for (const item in configIniJson) {
|
|
62
|
-
const line = `${item} = ${configIniJson[item]}\n`;
|
|
63
|
-
fWrite.write(line);
|
|
64
|
-
}
|
|
65
|
-
fWrite.close();
|
|
76
|
+
fs_1.default.writeFileSync(avdConfigIni, (0, ini_1.stringify)(configIniJson));
|
|
66
77
|
return true;
|
|
67
78
|
}
|
|
68
79
|
catch (e) {
|
|
69
|
-
throw
|
|
80
|
+
throw `createVelaAvd: ${e.message}`;
|
|
70
81
|
}
|
|
71
82
|
}
|
|
83
|
+
/** 根据AVD名字获取模拟器的详细信息 */
|
|
72
84
|
getVelaAvdInfo(avdName) {
|
|
73
85
|
const avdInfo = {
|
|
74
86
|
avdName,
|
|
@@ -82,22 +94,13 @@ class VelaAvdCls {
|
|
|
82
94
|
const configIni = path_1.default.resolve(currAvdDir, 'config.ini');
|
|
83
95
|
try {
|
|
84
96
|
const contents = fs_1.default.readFileSync(configIni, 'utf-8');
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const skinRegex = /skin.name = ([\d\D]+?)\n/;
|
|
93
|
-
const skinMatcher = contents.match(skinRegex);
|
|
94
|
-
const imagePathRegex = /image.sysdir.1 = ([\d\D]+?)\n/;
|
|
95
|
-
const imagePathMather = contents.match(imagePathRegex);
|
|
96
|
-
archMatcher && (avdInfo.avdArch = archMatcher[1]);
|
|
97
|
-
heightMatcher && (avdInfo.avdHeight = heightMatcher[1]);
|
|
98
|
-
widthMatcher && (avdInfo.avdWidth = widthMatcher[1]);
|
|
99
|
-
skinMatcher && (avdInfo.avdSkin = skinMatcher[1]);
|
|
100
|
-
imagePathMather && (avdInfo.avdImagePath = imagePathMather[1]);
|
|
97
|
+
const config = (0, ini_1.parse)(contents);
|
|
98
|
+
avdInfo.avdArch = config['hw.cpu.arch'];
|
|
99
|
+
avdInfo.avdHeight = config['hw.lcd.height'];
|
|
100
|
+
avdInfo.avdWidth = config['hw.lcd.width'];
|
|
101
|
+
avdInfo.avdSkin = config['skin.name'];
|
|
102
|
+
avdInfo.avdImagePath = config['image.sysdir.1'];
|
|
103
|
+
avdInfo.customImagePath = config['image.sysdir.2'];
|
|
101
104
|
return avdInfo;
|
|
102
105
|
}
|
|
103
106
|
catch (err) {
|
|
@@ -105,6 +108,7 @@ class VelaAvdCls {
|
|
|
105
108
|
return avdInfo;
|
|
106
109
|
}
|
|
107
110
|
}
|
|
111
|
+
/** 根据名字删除AVD */
|
|
108
112
|
deleteVelaAvd(avdName) {
|
|
109
113
|
const avdDir = path_1.default.resolve(this.avdHome, `${avdName}.avd`);
|
|
110
114
|
const avdIni = path_1.default.resolve(this.avdHome, `${avdName}.ini`);
|
|
@@ -117,6 +121,7 @@ class VelaAvdCls {
|
|
|
117
121
|
return false;
|
|
118
122
|
}
|
|
119
123
|
}
|
|
124
|
+
/** 获取已经创建的模拟器列表 */
|
|
120
125
|
getVelaAvdList() {
|
|
121
126
|
const avdList = [];
|
|
122
127
|
const files = fs_1.default.readdirSync(this.avdHome);
|
|
@@ -131,6 +136,7 @@ class VelaAvdCls {
|
|
|
131
136
|
}
|
|
132
137
|
return avdList;
|
|
133
138
|
}
|
|
139
|
+
/** 获取模拟器皮肤列表 */
|
|
134
140
|
getVelaSkinList() {
|
|
135
141
|
try {
|
|
136
142
|
const skinList = [];
|
|
@@ -145,5 +151,23 @@ class VelaAvdCls {
|
|
|
145
151
|
return [];
|
|
146
152
|
}
|
|
147
153
|
}
|
|
154
|
+
/** 自定义模拟器的镜像目录 */
|
|
155
|
+
customImageDir(avdName, target) {
|
|
156
|
+
const currAvdDir = path_1.default.resolve(this.avdHome, `${avdName}.avd`);
|
|
157
|
+
const configIni = path_1.default.resolve(currAvdDir, 'config.ini');
|
|
158
|
+
const contents = fs_1.default.readFileSync(configIni, 'utf-8');
|
|
159
|
+
const config = (0, ini_1.parse)(contents);
|
|
160
|
+
config['image.sysdir.2'] = target;
|
|
161
|
+
fs_1.default.writeFileSync(configIni, (0, ini_1.stringify)(config));
|
|
162
|
+
}
|
|
163
|
+
/** 重置自定义的镜像目录 */
|
|
164
|
+
resetImageDir(avdName) {
|
|
165
|
+
const currAvdDir = path_1.default.resolve(this.avdHome, `${avdName}.avd`);
|
|
166
|
+
const configIni = path_1.default.resolve(currAvdDir, 'config.ini');
|
|
167
|
+
const contents = fs_1.default.readFileSync(configIni, 'utf-8');
|
|
168
|
+
const config = (0, ini_1.parse)(contents);
|
|
169
|
+
delete config['image.sysdir.2'];
|
|
170
|
+
fs_1.default.writeFileSync(configIni, (0, ini_1.stringify)(config));
|
|
171
|
+
}
|
|
148
172
|
}
|
|
149
173
|
exports.default = VelaAvdCls;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { IStartOptions } from '../typing/Instance';
|
|
2
|
+
declare class EmulatorCmd {
|
|
3
|
+
static startOptions: IStartOptions | undefined;
|
|
4
|
+
static createMiwerCmd(options: IStartOptions, sdkHome: string, avdHome: string): string | undefined;
|
|
5
|
+
static createDevCmd(options: IStartOptions, sdkHome: string, avdHome: string): string | undefined;
|
|
6
|
+
static createPreCmd(options: IStartOptions, sdkHome: string, avdHome: string): string[] | undefined;
|
|
7
|
+
static getEmulatorBinPath(sdkHome: string): string;
|
|
8
|
+
}
|
|
9
|
+
export default EmulatorCmd;
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const ColorConsole_1 = __importDefault(require("@aiot-toolkit/shared-utils/lib/ColorConsole"));
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const avd_1 = __importDefault(require("../avd"));
|
|
10
|
+
const utils_1 = require("../utils");
|
|
11
|
+
const os_1 = __importDefault(require("os"));
|
|
12
|
+
const dayjs_1 = __importDefault(require("dayjs"));
|
|
13
|
+
const constants_1 = require("./constants");
|
|
14
|
+
class EmulatorCmd {
|
|
15
|
+
static createMiwerCmd(options, sdkHome, avdHome) {
|
|
16
|
+
var _a;
|
|
17
|
+
this.startOptions = options;
|
|
18
|
+
const { avdName, devtool } = options;
|
|
19
|
+
// 获取emulator bin的绝对路径
|
|
20
|
+
const emulatorBin = this.getEmulatorBinPath(sdkHome);
|
|
21
|
+
ColorConsole_1.default.log(`### Emulator ### emulator path: ${emulatorBin}`);
|
|
22
|
+
// 获取vela镜像的绝对路径
|
|
23
|
+
const velaAvdCls = new avd_1.default({
|
|
24
|
+
sdkHome: sdkHome,
|
|
25
|
+
avdHome: avdHome
|
|
26
|
+
});
|
|
27
|
+
const avdInfo = velaAvdCls.getVelaAvdInfo(avdName);
|
|
28
|
+
const { avdArch, avdImagePath, customImagePath } = avdInfo;
|
|
29
|
+
ColorConsole_1.default.log(`### Emulator ### adb port: ${options.adbPort}`);
|
|
30
|
+
if (!avdImagePath) {
|
|
31
|
+
ColorConsole_1.default.throw(`### Emulator ### Unable to find vela image via avd`);
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
const nuttxBinPath = path_1.default.resolve(customImagePath || avdImagePath, 'nuttx');
|
|
35
|
+
ColorConsole_1.default.log(`### Emulator ### nuttx path: ${nuttxBinPath}`);
|
|
36
|
+
// 端口映射
|
|
37
|
+
let portMappingStr = ``;
|
|
38
|
+
if (devtool) {
|
|
39
|
+
portMappingStr += `-network-user-mode-options hostfwd=tcp:127.0.0.1:${options.debugPort}-10.0.2.15:101`;
|
|
40
|
+
}
|
|
41
|
+
// 设备挂载节点
|
|
42
|
+
const systemImageBin = path_1.default.resolve(avdImagePath, 'vela_resource.bin'); // 只读
|
|
43
|
+
const dataImageBin = path_1.default.resolve(avdImagePath, 'vela_data.bin'); // 可读可写
|
|
44
|
+
const coreBin = path_1.default.resolve(avdImagePath, 'coredump.core'); // 只读
|
|
45
|
+
// 复制可写文件到AVD目录下(多模拟器实例时需要)
|
|
46
|
+
const dataImageBinInAvd = path_1.default.join(avdHome, `${avdName}.avd`, 'vela_data.bin');
|
|
47
|
+
if (!fs_extra_1.default.existsSync(dataImageBinInAvd)) {
|
|
48
|
+
// vela_data.bin不存在时直接copy
|
|
49
|
+
fs_extra_1.default.copyFileSync(dataImageBin, dataImageBinInAvd);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// vela_data.bin存在时,如果.export里的时间晚于avd里的时候,说明更新了镜像,需要重新copy
|
|
53
|
+
const statsInAvd = fs_extra_1.default.statSync(dataImageBinInAvd);
|
|
54
|
+
const stats = fs_extra_1.default.statSync(dataImageBin);
|
|
55
|
+
if ((0, dayjs_1.default)(stats.mtime).isAfter(statsInAvd.mtime)) {
|
|
56
|
+
fs_extra_1.default.copyFileSync(dataImageBin, dataImageBinInAvd);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// 挂载节点配置,只读文件加入read-only标识
|
|
60
|
+
const imageMountStr = `-drive index=0,id=system,if=none,format=raw,file=${systemImageBin},read-only \
|
|
61
|
+
-device virtio-blk-device,bus=virtio-mmio-bus.0,drive=system \
|
|
62
|
+
-drive index=1,id=userdata,if=none,format=raw,file=${dataImageBinInAvd} \
|
|
63
|
+
-device virtio-blk-device,bus=virtio-mmio-bus.1,drive=userdata \
|
|
64
|
+
-drive index=2,id=vendor,if=none,format=raw,file=${coreBin},read-only \
|
|
65
|
+
-device virtio-blk-device,bus=virtio-mmio-bus.4,drive=vendor \
|
|
66
|
+
-device virtio-snd,bus=virtio-mmio-bus.2 -allow-host-audio -semihosting`;
|
|
67
|
+
// grpc配置
|
|
68
|
+
let windowStr = '';
|
|
69
|
+
let grpcStr = '';
|
|
70
|
+
if ((_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.grpcPort) {
|
|
71
|
+
windowStr = '-qt-hide-window';
|
|
72
|
+
grpcStr = ` -idle-grpc-timeout 300 -grpc ${this.startOptions.grpcPort}`;
|
|
73
|
+
}
|
|
74
|
+
// 启动模拟器的命令和参数
|
|
75
|
+
const cmd = `${emulatorBin} -nuttx -avd ${avdName} -port ${options.adbPort} -avd-arch ${avdArch} -show-kernel -kernel ${nuttxBinPath} ${portMappingStr} ${windowStr} ${grpcStr} -qemu ${imageMountStr}`;
|
|
76
|
+
return cmd;
|
|
77
|
+
}
|
|
78
|
+
static createDevCmd(options, sdkHome, avdHome) {
|
|
79
|
+
var _a;
|
|
80
|
+
this.startOptions = options;
|
|
81
|
+
const { avdName, devtool } = options;
|
|
82
|
+
const emulatorBin = this.getEmulatorBinPath(sdkHome);
|
|
83
|
+
ColorConsole_1.default.log(`### Emulator ### emulator path: ${emulatorBin}`);
|
|
84
|
+
const velaAvdCls = new avd_1.default({
|
|
85
|
+
sdkHome: sdkHome,
|
|
86
|
+
avdHome: avdHome
|
|
87
|
+
});
|
|
88
|
+
const avdInfo = velaAvdCls.getVelaAvdInfo(avdName);
|
|
89
|
+
const { avdArch, avdImagePath, customImagePath } = avdInfo;
|
|
90
|
+
ColorConsole_1.default.log(`### Emulator ### adb port: ${options.adbPort}`);
|
|
91
|
+
if (!avdImagePath) {
|
|
92
|
+
ColorConsole_1.default.throw(`### Emulator ### Unable to find vela image via avd`);
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
const nuttxBinPath = path_1.default.resolve(customImagePath || avdImagePath, 'nuttx');
|
|
96
|
+
ColorConsole_1.default.log(`### Emulator ### nuttx path: ${nuttxBinPath}`);
|
|
97
|
+
// 端口映射
|
|
98
|
+
let portMappingStr = ``;
|
|
99
|
+
if (devtool) {
|
|
100
|
+
portMappingStr += `-network-user-mode-options hostfwd=tcp:127.0.0.1:${options.debugPort}-10.0.2.15:101`;
|
|
101
|
+
}
|
|
102
|
+
// 文件系统配置,第一次使用fatfs镜像挂载,后续使用adb push更新应用
|
|
103
|
+
const systemImageBin = path_1.default.join(avdImagePath, 'system.img');
|
|
104
|
+
const dataImageBin = path_1.default.join(avdImagePath, 'data.img');
|
|
105
|
+
// 复制可写文件到AVD目录下(多模拟器实例时需要)
|
|
106
|
+
const dataImageBinInAvd = path_1.default.join(avdHome, `${avdName}.avd`, 'data.img');
|
|
107
|
+
if (!fs_extra_1.default.existsSync(dataImageBinInAvd)) {
|
|
108
|
+
// data.img不存在时直接copy
|
|
109
|
+
fs_extra_1.default.copyFileSync(dataImageBin, dataImageBinInAvd);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
// data.img存在时,如果.export里的时间晚于avd里的时候,说明更新了镜像,需要重新copy
|
|
113
|
+
const statsInAvd = fs_extra_1.default.statSync(dataImageBinInAvd);
|
|
114
|
+
const stats = fs_extra_1.default.statSync(dataImageBin);
|
|
115
|
+
if ((0, dayjs_1.default)(stats.mtime).isAfter(statsInAvd.mtime)) {
|
|
116
|
+
fs_extra_1.default.copyFileSync(dataImageBin, dataImageBinInAvd);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// 复制可写文件到AVD目录下(多模拟器实例时需要)
|
|
120
|
+
const systemImageBinInAvd = path_1.default.join(avdHome, `${avdName}.avd`, 'system.img');
|
|
121
|
+
if (!fs_extra_1.default.existsSync(systemImageBinInAvd)) {
|
|
122
|
+
// data.img不存在时直接copy
|
|
123
|
+
fs_extra_1.default.copyFileSync(systemImageBin, systemImageBinInAvd);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
// data.img存在时,如果.export里的时间晚于avd里的时候,说明更新了镜像,需要重新copy
|
|
127
|
+
const statsInAvd = fs_extra_1.default.statSync(systemImageBinInAvd);
|
|
128
|
+
const stats = fs_extra_1.default.statSync(systemImageBin);
|
|
129
|
+
if ((0, dayjs_1.default)(stats.mtime).isAfter(statsInAvd.mtime)) {
|
|
130
|
+
fs_extra_1.default.copyFileSync(systemImageBin, systemImageBinInAvd);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// 复制可写文件到AVD目录下(多模拟器实例时需要)
|
|
134
|
+
// const nuttxBinInAvd = path.join(this.avdHome, `${avdName}.avd`, 'nuttx')
|
|
135
|
+
// if (!fs.existsSync(nuttxBinInAvd)) {
|
|
136
|
+
// // data.img不存在时直接copy
|
|
137
|
+
// fs.copyFileSync(nuttxBinPath, nuttxBinInAvd)
|
|
138
|
+
// } else {
|
|
139
|
+
// // data.img存在时,如果.export里的时间晚于avd里的时候,说明更新了镜像,需要重新copy
|
|
140
|
+
// const statsInAvd = fs.statSync(nuttxBinInAvd)
|
|
141
|
+
// const stats = fs.statSync(nuttxBinPath)
|
|
142
|
+
// if (dayjs(stats.mtime).isAfter(statsInAvd.mtime)) {
|
|
143
|
+
// fs.copyFileSync(nuttxBinPath, nuttxBinInAvd)
|
|
144
|
+
// }
|
|
145
|
+
// }
|
|
146
|
+
const imageMountStr = `-drive index=0,id=system,if=none,format=raw,file=${systemImageBinInAvd} \
|
|
147
|
+
-device virtio-blk-device,bus=virtio-mmio-bus.0,drive=system \
|
|
148
|
+
-drive index=1,id=userdata,if=none,format=raw,file=${dataImageBinInAvd} \
|
|
149
|
+
-device virtio-blk-device,bus=virtio-mmio-bus.1,drive=userdata \
|
|
150
|
+
-device virtio-snd,bus=virtio-mmio-bus.2 -allow-host-audio -semihosting`;
|
|
151
|
+
// grpc配置
|
|
152
|
+
let windowStr = '';
|
|
153
|
+
let grpcStr = '';
|
|
154
|
+
if ((_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.grpcPort) {
|
|
155
|
+
windowStr = '-qt-hide-window';
|
|
156
|
+
grpcStr = ` -idle-grpc-timeout 300 -grpc ${this.startOptions.grpcPort}`;
|
|
157
|
+
}
|
|
158
|
+
// 启动模拟器的命令和参数
|
|
159
|
+
const cmd = `${emulatorBin} -nuttx -avd ${avdName} -port ${options.adbPort} -avd-arch ${avdArch} -show-kernel -kernel ${nuttxBinPath} ${portMappingStr} ${windowStr} ${grpcStr} -qemu ${imageMountStr}`;
|
|
160
|
+
return cmd;
|
|
161
|
+
}
|
|
162
|
+
static createPreCmd(options, sdkHome, avdHome) {
|
|
163
|
+
this.startOptions = options;
|
|
164
|
+
const { avdName, devtool } = options;
|
|
165
|
+
// 获取emulator bin的绝对路径
|
|
166
|
+
const emulatorBin = this.getEmulatorBinPath(sdkHome);
|
|
167
|
+
ColorConsole_1.default.log(`### Emulator ### emulator path: ${emulatorBin}`);
|
|
168
|
+
// 获取vela镜像的绝对路径
|
|
169
|
+
const velaAvdCls = new avd_1.default({
|
|
170
|
+
sdkHome: sdkHome,
|
|
171
|
+
avdHome: avdHome
|
|
172
|
+
});
|
|
173
|
+
const avdInfo = velaAvdCls.getVelaAvdInfo(avdName);
|
|
174
|
+
const { avdArch, avdImagePath, customImagePath } = avdInfo;
|
|
175
|
+
ColorConsole_1.default.log(`### Emulator ### adb port: ${options.adbPort}`);
|
|
176
|
+
if (!avdImagePath) {
|
|
177
|
+
ColorConsole_1.default.throw(`### Emulator ### Unable to find vela image via avd`);
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
const nuttxBinPath = path_1.default.resolve(customImagePath || avdImagePath, 'nuttx');
|
|
181
|
+
ColorConsole_1.default.log(`### Emulator ### nuttx path: ${nuttxBinPath}`);
|
|
182
|
+
// 端口映射,adb端口和debug端口
|
|
183
|
+
let portMappingStr = `user,id=u1,hostfwd=tcp:127.0.0.1:${options.adbPort}-10.0.2.15:5555`;
|
|
184
|
+
if (devtool) {
|
|
185
|
+
ColorConsole_1.default.log(`### Emulator ### debug port: ${options.debugPort}`);
|
|
186
|
+
portMappingStr += `,hostfwd=tcp:127.0.0.1:${options.debugPort}-10.0.2.15:101`;
|
|
187
|
+
}
|
|
188
|
+
ColorConsole_1.default.log(`### Emulator ### Start qemu with TCP: ${portMappingStr}`);
|
|
189
|
+
// vnc配置
|
|
190
|
+
let noWindow = false;
|
|
191
|
+
let vncStr = '';
|
|
192
|
+
if (options.grpcPort) {
|
|
193
|
+
noWindow = true;
|
|
194
|
+
const portSuffix = options.grpcPort - constants_1.defaultVncPort;
|
|
195
|
+
vncStr = `-vnc :${portSuffix}`;
|
|
196
|
+
}
|
|
197
|
+
// 启动goldfish的命令和参数
|
|
198
|
+
const spawnArgs = [
|
|
199
|
+
emulatorBin,
|
|
200
|
+
'-nuttx',
|
|
201
|
+
'-avd',
|
|
202
|
+
avdName,
|
|
203
|
+
'-avd-arch',
|
|
204
|
+
avdArch,
|
|
205
|
+
'-show-kernel',
|
|
206
|
+
'-kernel',
|
|
207
|
+
nuttxBinPath,
|
|
208
|
+
noWindow ? '-no-window' : '',
|
|
209
|
+
'-qemu',
|
|
210
|
+
vncStr,
|
|
211
|
+
'-netdev',
|
|
212
|
+
portMappingStr,
|
|
213
|
+
'-device',
|
|
214
|
+
'virtio-net-device,netdev=u1,bus=virtio-mmio-bus.3'
|
|
215
|
+
];
|
|
216
|
+
return spawnArgs;
|
|
217
|
+
}
|
|
218
|
+
static getEmulatorBinPath(sdkHome) {
|
|
219
|
+
const osPlatform = os_1.default.platform();
|
|
220
|
+
const arch = (0, utils_1.getSystemArch)();
|
|
221
|
+
const platform = osPlatform === 'win32' ? 'windows' : osPlatform;
|
|
222
|
+
const emulatorHome = path_1.default.resolve(sdkHome, 'emulator');
|
|
223
|
+
return path_1.default.resolve(emulatorHome, `${platform}-${arch}`, 'emulator');
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
exports.default = EmulatorCmd;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
declare class EmulatorLog {
|
|
2
|
+
static emulatorStartedFlag: RegExp;
|
|
3
|
+
static installFlag: RegExp;
|
|
4
|
+
static devStartFlag: string;
|
|
5
|
+
static preDevStartFlag: RegExp;
|
|
6
|
+
static inStallIsFinshe(msg: string): RegExpMatchArray | null;
|
|
7
|
+
static rpkIsStart(msg: string): RegExpMatchArray | null;
|
|
8
|
+
static devIsStart(msg: string): boolean;
|
|
9
|
+
static preDevIsStart(msg: string): RegExpMatchArray | null;
|
|
10
|
+
}
|
|
11
|
+
export default EmulatorLog;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class EmulatorLog {
|
|
4
|
+
static inStallIsFinshe(msg) {
|
|
5
|
+
return msg.match(EmulatorLog.installFlag);
|
|
6
|
+
}
|
|
7
|
+
static rpkIsStart(msg) {
|
|
8
|
+
return msg.match(EmulatorLog.emulatorStartedFlag);
|
|
9
|
+
}
|
|
10
|
+
static devIsStart(msg) {
|
|
11
|
+
return msg.includes(EmulatorLog.devStartFlag);
|
|
12
|
+
}
|
|
13
|
+
static preDevIsStart(msg) {
|
|
14
|
+
return msg.match(EmulatorLog.preDevStartFlag);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
EmulatorLog.emulatorStartedFlag = /quickapp_rpk_installer_init|rpk installer init done/;
|
|
18
|
+
EmulatorLog.installFlag = /InstallState_Finished|install finished/;
|
|
19
|
+
EmulatorLog.devStartFlag = '(NSH)';
|
|
20
|
+
EmulatorLog.preDevStartFlag = /Server started, listening on: 127.0.0.1:(\d+)/;
|
|
21
|
+
exports.default = EmulatorLog;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export declare const defaultSDKHome: string;
|
|
2
|
+
export declare const defaultAvdHome: string;
|
|
3
|
+
export declare const defaultImageHome: string;
|
|
4
|
+
export declare const defaultEmulatorHome: string;
|
|
5
|
+
export declare const defaultSkinHome: string;
|
|
6
|
+
export declare const defaultQuickappHome: string;
|
|
7
|
+
export declare const defaultToolsHome: string;
|
|
8
|
+
export declare const defaultVncPort = 5900;
|
|
9
|
+
export declare const defaultAdbPort = 5555;
|
|
10
|
+
export declare const defaultDebugPort = 10055;
|
|
11
|
+
/**
|
|
12
|
+
* vela-release-4.0 : vela 4.0 带 miwear
|
|
13
|
+
* vela-pre-4.0 : vela 4.0 不带 miwear
|
|
14
|
+
* vela-dev-0.0.4 : vela 4.0 dev 分支
|
|
15
|
+
*/
|
|
16
|
+
export declare const VelaImageVersionList: {
|
|
17
|
+
label: string;
|
|
18
|
+
value: string;
|
|
19
|
+
time: string;
|
|
20
|
+
hide: boolean;
|
|
21
|
+
icon: null;
|
|
22
|
+
}[];
|
|
23
|
+
export declare const EmulatorEnvVersion: {
|
|
24
|
+
name: string;
|
|
25
|
+
emulator: string;
|
|
26
|
+
qa: string;
|
|
27
|
+
skins: string;
|
|
28
|
+
'system-images': string;
|
|
29
|
+
tools: string;
|
|
30
|
+
modem_simulator: string;
|
|
31
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.EmulatorEnvVersion = exports.VelaImageVersionList = exports.defaultDebugPort = exports.defaultAdbPort = exports.defaultVncPort = exports.defaultToolsHome = exports.defaultQuickappHome = exports.defaultSkinHome = exports.defaultEmulatorHome = exports.defaultImageHome = exports.defaultAvdHome = exports.defaultSDKHome = void 0;
|
|
7
|
+
const os_1 = __importDefault(require("os"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
exports.defaultSDKHome = path_1.default.resolve(os_1.default.homedir(), '.export');
|
|
10
|
+
exports.defaultAvdHome = path_1.default.resolve(os_1.default.homedir(), '.android', 'avd');
|
|
11
|
+
exports.defaultImageHome = path_1.default.resolve(exports.defaultSDKHome, 'system-images/arm');
|
|
12
|
+
exports.defaultEmulatorHome = path_1.default.resolve(exports.defaultSDKHome, 'emulator');
|
|
13
|
+
exports.defaultSkinHome = path_1.default.resolve(exports.defaultSDKHome, 'skins');
|
|
14
|
+
exports.defaultQuickappHome = path_1.default.resolve(exports.defaultSDKHome, 'qa');
|
|
15
|
+
exports.defaultToolsHome = path_1.default.resolve(exports.defaultSDKHome, 'tools');
|
|
16
|
+
exports.defaultVncPort = 5900;
|
|
17
|
+
exports.defaultAdbPort = 5555;
|
|
18
|
+
exports.defaultDebugPort = 10055;
|
|
19
|
+
// 不确定vela镜像的发布策略,暂时需要手动更新此列表
|
|
20
|
+
// 0.0.2和0.0.3版本比较特殊,线上是一个nuttx文件。其他版本都是一个zip包,包含nuttx data.img和vela_source.img
|
|
21
|
+
/**
|
|
22
|
+
* vela-release-4.0 : vela 4.0 带 miwear
|
|
23
|
+
* vela-pre-4.0 : vela 4.0 不带 miwear
|
|
24
|
+
* vela-dev-0.0.4 : vela 4.0 dev 分支
|
|
25
|
+
*/
|
|
26
|
+
exports.VelaImageVersionList = [
|
|
27
|
+
{
|
|
28
|
+
label: 'vela-4.0-正式版',
|
|
29
|
+
value: 'vela-release-4.0',
|
|
30
|
+
time: '2024-08-12T06:48:00',
|
|
31
|
+
hide: false,
|
|
32
|
+
icon: null
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
label: 'vela-4.0-测试版',
|
|
36
|
+
value: 'vela-pre-4.0',
|
|
37
|
+
time: '2024-08-12T06:48:00',
|
|
38
|
+
hide: false,
|
|
39
|
+
icon: null
|
|
40
|
+
},
|
|
41
|
+
// 废弃 0.0.2 版本
|
|
42
|
+
// {
|
|
43
|
+
// label: 'vela开发版(dev, 0.0.2)',
|
|
44
|
+
// value: 'vela-dev-0.0.2',
|
|
45
|
+
// time: '2023-12-04T16:31:00',
|
|
46
|
+
// },
|
|
47
|
+
{
|
|
48
|
+
label: 'vela-dev-开发版',
|
|
49
|
+
value: 'vela-dev-0.0.4',
|
|
50
|
+
time: '2024-07-12T11:12:00',
|
|
51
|
+
hide: true,
|
|
52
|
+
icon: null
|
|
53
|
+
}
|
|
54
|
+
];
|
|
55
|
+
exports.EmulatorEnvVersion = {
|
|
56
|
+
name: '模拟器资源版本管理',
|
|
57
|
+
emulator: '0.0.4',
|
|
58
|
+
qa: '0.0.1',
|
|
59
|
+
skins: '0.0.1',
|
|
60
|
+
'system-images': exports.VelaImageVersionList[0].value,
|
|
61
|
+
tools: '0.0.2',
|
|
62
|
+
modem_simulator: '0.0.1'
|
|
63
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.EmulatorCmd = exports.EmulatorLog = void 0;
|
|
7
|
+
const EmulatorLog_1 = __importDefault(require("./EmulatorLog"));
|
|
8
|
+
exports.EmulatorLog = EmulatorLog_1.default;
|
|
9
|
+
const EmulatorCmd_1 = __importDefault(require("./EmulatorCmd"));
|
|
10
|
+
exports.EmulatorCmd = EmulatorCmd_1.default;
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { default as VelaAvdCls } from './avd';
|
|
2
|
+
import { getSystemArch } from './utils';
|
|
2
3
|
export * from './instance';
|
|
3
4
|
export * from './typing/Avd';
|
|
4
5
|
export * from './typing/Instance';
|
|
5
|
-
export
|
|
6
|
+
export * from './emulatorutil/constants';
|
|
7
|
+
export { VelaAvdCls, getSystemArch };
|
package/lib/index.js
CHANGED
|
@@ -17,9 +17,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.VelaAvdCls = void 0;
|
|
20
|
+
exports.getSystemArch = exports.VelaAvdCls = void 0;
|
|
21
21
|
const avd_1 = __importDefault(require("./avd"));
|
|
22
22
|
Object.defineProperty(exports, "VelaAvdCls", { enumerable: true, get: function () { return avd_1.default; } });
|
|
23
|
+
const utils_1 = require("./utils");
|
|
24
|
+
Object.defineProperty(exports, "getSystemArch", { enumerable: true, get: function () { return utils_1.getSystemArch; } });
|
|
23
25
|
__exportStar(require("./instance"), exports);
|
|
24
26
|
__exportStar(require("./typing/Avd"), exports);
|
|
25
27
|
__exportStar(require("./typing/Instance"), exports);
|
|
28
|
+
__exportStar(require("./emulatorutil/constants"), exports);
|
package/lib/instance/common.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
1
|
import IManifest from '@aiot-toolkit/aiotpack/lib/compiler/javascript/vela/interface/IManifest';
|
|
3
2
|
import { ChildProcess } from 'child_process';
|
|
4
3
|
import VelaAvdCls from '../avd';
|
|
@@ -7,37 +6,41 @@ import { INewGoldfishInstanceParams, IStartOptions } from '../typing/Instance';
|
|
|
7
6
|
* CommonInstance
|
|
8
7
|
*/
|
|
9
8
|
declare class CommonInstance {
|
|
9
|
+
quickappStartedFlag: RegExp;
|
|
10
10
|
projectPath: string;
|
|
11
11
|
sdkHome: string;
|
|
12
12
|
avdHome: string;
|
|
13
|
-
adbPort: number;
|
|
14
|
-
debugPort: number;
|
|
15
13
|
velaAvdCls: VelaAvdCls;
|
|
16
14
|
goldfishProcess: ChildProcess | undefined;
|
|
17
15
|
isFirstStart: boolean;
|
|
18
16
|
startOptions: IStartOptions | undefined;
|
|
19
17
|
projectInfo: IManifest;
|
|
20
18
|
isRpk: boolean;
|
|
19
|
+
isDistributedApp: boolean;
|
|
20
|
+
sn?: string;
|
|
21
21
|
constructor(params: INewGoldfishInstanceParams);
|
|
22
22
|
/** 获取模拟器二进制文件所在位置 */
|
|
23
23
|
getEmulatorBinPath(): string;
|
|
24
24
|
/** 在goldfish模拟器中运行快应用 */
|
|
25
25
|
start(options: IStartOptions): Promise<void>;
|
|
26
|
-
/**
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
/**
|
|
27
|
+
* 判断模拟器是否 ready
|
|
28
|
+
*/
|
|
29
|
+
isConnected(): Promise<boolean>;
|
|
30
|
+
/**
|
|
31
|
+
* 通过adb连接模拟器。
|
|
32
|
+
* 时间限制为 @param timeout 秒,超时则表示连接失败
|
|
33
|
+
* @returns
|
|
34
|
+
*/
|
|
35
|
+
connectGoldfish(timeout?: number): Promise<unknown>;
|
|
34
36
|
/** 杀死进程 */
|
|
35
37
|
killProcess(currProcess?: ChildProcess): void;
|
|
36
38
|
/** 停止模拟器并释放相关资源 */
|
|
37
39
|
stop(): Promise<void>;
|
|
38
40
|
/** 重启模拟器 */
|
|
39
|
-
restart(): void
|
|
41
|
+
restart(): Promise<void>;
|
|
40
42
|
/** 创建server */
|
|
41
43
|
createWebsockeServer(): Promise<void>;
|
|
44
|
+
connectDevice(): Promise<string | void>;
|
|
42
45
|
}
|
|
43
46
|
export default CommonInstance;
|