@aiot-toolkit/emulator 2.0.2-beta.4 → 2.0.2-beta.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/README.md +14 -1
- package/lib/avd/index.d.ts +12 -0
- package/lib/avd/index.js +12 -0
- package/lib/instance/index.d.ts +6 -0
- package/lib/instance/index.js +6 -0
- package/lib/instance/miwear.d.ts +49 -8
- package/lib/instance/miwear.js +67 -25
- package/lib/instance/preDev.d.ts +39 -5
- package/lib/instance/preDev.js +52 -6
- package/lib/static/constants.js +1 -1
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +1 -0
- package/package.json +8 -4
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,22 @@ 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[];
|
|
11
23
|
}
|
|
12
24
|
export default VelaAvdCls;
|
package/lib/avd/index.js
CHANGED
|
@@ -26,6 +26,14 @@ class VelaAvdCls {
|
|
|
26
26
|
fs_1.default.mkdirSync(this.avdHome, { recursive: true });
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* 创建Vela端的AVD,统一保存在.android目录下
|
|
31
|
+
* 1. 创建.android/advancedFeatures.ini文件
|
|
32
|
+
* 2. 创建.android/${avdName}.ini文件
|
|
33
|
+
* 3. 创建.android/${avdName}.avd/config.ini文件
|
|
34
|
+
* @param avdParams AVD参数,宽高、绑定的镜像路径等
|
|
35
|
+
* @returns
|
|
36
|
+
*/
|
|
29
37
|
createVelaAvd(avdParams) {
|
|
30
38
|
// 在.android下创建advancedFeatures.ini文件
|
|
31
39
|
const advancedFeaturesIni = path_1.default.resolve(os_1.default.homedir(), '.android/advancedFeatures.ini');
|
|
@@ -76,6 +84,7 @@ class VelaAvdCls {
|
|
|
76
84
|
throw (`createVelaAvd: ${e.message}`);
|
|
77
85
|
}
|
|
78
86
|
}
|
|
87
|
+
/** 根据AVD名字获取模拟器的详细信息 */
|
|
79
88
|
getVelaAvdInfo(avdName) {
|
|
80
89
|
const avdInfo = {
|
|
81
90
|
avdName,
|
|
@@ -112,6 +121,7 @@ class VelaAvdCls {
|
|
|
112
121
|
return avdInfo;
|
|
113
122
|
}
|
|
114
123
|
}
|
|
124
|
+
/** 根据名字删除AVD */
|
|
115
125
|
deleteVelaAvd(avdName) {
|
|
116
126
|
const avdDir = path_1.default.resolve(this.avdHome, `${avdName}.avd`);
|
|
117
127
|
const avdIni = path_1.default.resolve(this.avdHome, `${avdName}.ini`);
|
|
@@ -124,6 +134,7 @@ class VelaAvdCls {
|
|
|
124
134
|
return false;
|
|
125
135
|
}
|
|
126
136
|
}
|
|
137
|
+
/** 获取已经创建的模拟器列表 */
|
|
127
138
|
getVelaAvdList() {
|
|
128
139
|
const avdList = [];
|
|
129
140
|
const files = fs_1.default.readdirSync(this.avdHome);
|
|
@@ -138,6 +149,7 @@ class VelaAvdCls {
|
|
|
138
149
|
}
|
|
139
150
|
return avdList;
|
|
140
151
|
}
|
|
152
|
+
/** 获取模拟器皮肤列表 */
|
|
141
153
|
getVelaSkinList() {
|
|
142
154
|
try {
|
|
143
155
|
const skinList = [];
|
package/lib/instance/index.d.ts
CHANGED
|
@@ -3,5 +3,11 @@ import CommonInstance from './common';
|
|
|
3
3
|
import GoldfishInstance from './dev';
|
|
4
4
|
import MiwearInstance from "./miwear";
|
|
5
5
|
import OldGoldfishInstance from './preDev';
|
|
6
|
+
/**
|
|
7
|
+
* 根据镜像决定使用哪个instance
|
|
8
|
+
* Vela正式版(4.0) -> MiwearInstance
|
|
9
|
+
* Vela开发版(dev, 0.0.2) -> OldGoldfishInstance
|
|
10
|
+
* Vela开发版(dev),除0.0.2的其他版本 -> GoldfishInstance
|
|
11
|
+
*/
|
|
6
12
|
declare function findInstance(avdName: string, params: INewGoldfishInstanceParams): GoldfishInstance | MiwearInstance | OldGoldfishInstance | undefined;
|
|
7
13
|
export { CommonInstance, GoldfishInstance, MiwearInstance, OldGoldfishInstance, findInstance };
|
package/lib/instance/index.js
CHANGED
|
@@ -14,6 +14,12 @@ const miwear_1 = __importDefault(require("./miwear"));
|
|
|
14
14
|
exports.MiwearInstance = miwear_1.default;
|
|
15
15
|
const preDev_1 = __importDefault(require("./preDev"));
|
|
16
16
|
exports.OldGoldfishInstance = preDev_1.default;
|
|
17
|
+
/**
|
|
18
|
+
* 根据镜像决定使用哪个instance
|
|
19
|
+
* Vela正式版(4.0) -> MiwearInstance
|
|
20
|
+
* Vela开发版(dev, 0.0.2) -> OldGoldfishInstance
|
|
21
|
+
* Vela开发版(dev),除0.0.2的其他版本 -> GoldfishInstance
|
|
22
|
+
*/
|
|
17
23
|
function findInstance(avdName, params) {
|
|
18
24
|
const { sdkHome, avdHome } = params;
|
|
19
25
|
const { avdImagePath } = new avd_1.default({ sdkHome, avdHome }).getVelaAvdInfo(avdName);
|
package/lib/instance/miwear.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { INewGoldfishInstanceParams, IStartOptions } from '../typing/Instance';
|
|
|
2
2
|
import CommonInstance from './common';
|
|
3
3
|
/**
|
|
4
4
|
* MiwearInstance
|
|
5
|
-
* 针对
|
|
5
|
+
* 针对 Vela正式版(4.0)的镜像
|
|
6
6
|
*/
|
|
7
7
|
declare class MiwearInstance extends CommonInstance {
|
|
8
8
|
private params;
|
|
@@ -10,20 +10,61 @@ declare class MiwearInstance extends CommonInstance {
|
|
|
10
10
|
private debugSocket?;
|
|
11
11
|
private reconnectCount;
|
|
12
12
|
constructor(params: INewGoldfishInstanceParams);
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* 1. 启动模拟器
|
|
15
|
+
* 2. 启动成功后,adb连接模拟器
|
|
16
|
+
* 3. 连接成功后,在模拟器中启动快应用
|
|
17
|
+
*/
|
|
14
18
|
start(options: IStartOptions): Promise<void>;
|
|
15
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* 启动模拟器
|
|
21
|
+
* 1. 通过options生成模拟器的启动命令
|
|
22
|
+
* 2. 执行启动命令
|
|
23
|
+
* 3. 判断模拟器是否启动成功
|
|
24
|
+
* 3.1 若disableNSH=true,输出流中匹配到/quickapp_rpk_installer_init|rpk installer init done/,认为模拟器启动成功了
|
|
25
|
+
* 3.2 若disableNSH=false,认为8s过后模拟器启动成功了
|
|
26
|
+
* @param options
|
|
27
|
+
* @returns
|
|
28
|
+
*/
|
|
16
29
|
startGoldfish(options: IStartOptions): Promise<void>;
|
|
17
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* 通过adb连接模拟器。
|
|
32
|
+
* 时间限制为10分钟,超时则表示连接失败
|
|
33
|
+
* @returns
|
|
34
|
+
*/
|
|
18
35
|
connectGoldfish(): Promise<boolean>;
|
|
19
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* 在模拟器中启动快应用
|
|
38
|
+
* 1. 检查release目录是否有打包好的rpk
|
|
39
|
+
* 2. 检查当前是否为调试模式.
|
|
40
|
+
* 若是,将debugger_ip.cfg推到模拟器的/data/目录下
|
|
41
|
+
* 若否,则要删除模拟器中已有的data/debugger_ip.cfg
|
|
42
|
+
* 3. 调用installRpkToAppList将当前快应用安装到模拟器的应用列表中
|
|
43
|
+
* @param options
|
|
44
|
+
*/
|
|
20
45
|
startupQuickApp(options: IStartOptions): Promise<void>;
|
|
21
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* 将快应用安装到模拟器的应用列表
|
|
48
|
+
* 1. 使用adb push将打包好的rpk推到模拟器的/data/quickapp/app/
|
|
49
|
+
* 2. nsh中调用pm install命令安装应用
|
|
50
|
+
* @param rpkPath rpk的绝对目录
|
|
51
|
+
* @param targetDir 要将rpk放到模拟器的哪个目录下
|
|
52
|
+
*/
|
|
22
53
|
installRpkToAppList(rpkPath: string, targetDir: string): Promise<void>;
|
|
54
|
+
/** 连接模拟器中的调试服务,创建debugSocket
|
|
55
|
+
* 主要用于热更新时,通过debugSocket通知调试服务更新页面
|
|
56
|
+
* 设置了重连机制,会重复连接8次
|
|
57
|
+
*/
|
|
23
58
|
initDebugSocket(): Promise<void>;
|
|
24
|
-
/** 通知模拟器更新
|
|
59
|
+
/** 通知模拟器更新
|
|
60
|
+
* 1. 确保已经成功连接模拟器中的调试服务
|
|
61
|
+
* 2. 使用adb push将build文件下的内容推到/data/quickapp/app/${packageName}
|
|
62
|
+
* 3. 发送Page.reload命令给调试服务,通知更新
|
|
63
|
+
*/
|
|
25
64
|
handleUpdate(): Promise<void>;
|
|
26
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* 创建server端,监听打包过程中client端发来的消息
|
|
67
|
+
*/
|
|
27
68
|
createWebsockeServer(): Promise<void>;
|
|
28
69
|
}
|
|
29
70
|
export default MiwearInstance;
|
package/lib/instance/miwear.js
CHANGED
|
@@ -49,7 +49,7 @@ const common_1 = __importDefault(require("./common"));
|
|
|
49
49
|
const MAX_RECONNECT_COUNT = 8;
|
|
50
50
|
/**
|
|
51
51
|
* MiwearInstance
|
|
52
|
-
* 针对
|
|
52
|
+
* 针对 Vela正式版(4.0)的镜像
|
|
53
53
|
*/
|
|
54
54
|
class MiwearInstance extends common_1.default {
|
|
55
55
|
constructor(params) {
|
|
@@ -58,7 +58,11 @@ class MiwearInstance extends common_1.default {
|
|
|
58
58
|
this.appPathInEmulator = '/data/quickapp/app';
|
|
59
59
|
this.reconnectCount = 0;
|
|
60
60
|
}
|
|
61
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* 1. 启动模拟器
|
|
63
|
+
* 2. 启动成功后,adb连接模拟器
|
|
64
|
+
* 3. 连接成功后,在模拟器中启动快应用
|
|
65
|
+
*/
|
|
62
66
|
start(options) {
|
|
63
67
|
return __awaiter(this, void 0, void 0, function* () {
|
|
64
68
|
this.startOptions = options;
|
|
@@ -68,9 +72,11 @@ class MiwearInstance extends common_1.default {
|
|
|
68
72
|
const connected = yield this.connectGoldfish();
|
|
69
73
|
if (connected) {
|
|
70
74
|
ColorConsole_1.default.info('### Emulator ### Goldfish emulator connected successfully');
|
|
75
|
+
// 如果是首次启动,创建server端监听事件
|
|
71
76
|
if (this.isFirstStart && this.startOptions.serverPort) {
|
|
72
77
|
yield this.createWebsockeServer();
|
|
73
78
|
}
|
|
79
|
+
// 在模拟器中启动快应用
|
|
74
80
|
this.startupQuickApp(options);
|
|
75
81
|
this.isFirstStart = false;
|
|
76
82
|
}
|
|
@@ -79,13 +85,24 @@ class MiwearInstance extends common_1.default {
|
|
|
79
85
|
}
|
|
80
86
|
});
|
|
81
87
|
}
|
|
82
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* 启动模拟器
|
|
90
|
+
* 1. 通过options生成模拟器的启动命令
|
|
91
|
+
* 2. 执行启动命令
|
|
92
|
+
* 3. 判断模拟器是否启动成功
|
|
93
|
+
* 3.1 若disableNSH=true,输出流中匹配到/quickapp_rpk_installer_init|rpk installer init done/,认为模拟器启动成功了
|
|
94
|
+
* 3.2 若disableNSH=false,认为8s过后模拟器启动成功了
|
|
95
|
+
* @param options
|
|
96
|
+
* @returns
|
|
97
|
+
*/
|
|
83
98
|
startGoldfish(options) {
|
|
84
99
|
var _a;
|
|
85
100
|
return __awaiter(this, void 0, void 0, function* () {
|
|
86
101
|
const { avdName, devtool } = options;
|
|
102
|
+
// 获取emulator bin的绝对路径
|
|
87
103
|
const emulatorBin = this.getEmulatorBinPath();
|
|
88
104
|
ColorConsole_1.default.log(`### Emulator ### emulator path: ${emulatorBin}`);
|
|
105
|
+
// 获取vela镜像的绝对路径
|
|
89
106
|
const avdInfo = this.velaAvdCls.getVelaAvdInfo(avdName);
|
|
90
107
|
const { avdArch, avdImagePath } = avdInfo;
|
|
91
108
|
this.adbPort = yield portfinder_1.default.getPortPromise({ port: this.adbPort });
|
|
@@ -95,15 +112,15 @@ class MiwearInstance extends common_1.default {
|
|
|
95
112
|
}
|
|
96
113
|
const nuttxBinPath = path_1.default.resolve(avdImagePath, 'nuttx');
|
|
97
114
|
ColorConsole_1.default.log(`### Emulator ### nuttx path: ${nuttxBinPath}`);
|
|
98
|
-
//
|
|
115
|
+
// 端口映射,adb端口和debug端口
|
|
99
116
|
let portMappingStr = `-network-user-mode-options hostfwd=tcp:127.0.0.1:${this.adbPort}-10.0.2.15:5555`;
|
|
100
117
|
if (devtool) {
|
|
101
118
|
this.debugPort = yield portfinder_1.default.getPortPromise({ port: this.debugPort });
|
|
102
119
|
portMappingStr += `,hostfwd=tcp:127.0.0.1:${this.debugPort}-10.0.2.15:101`;
|
|
103
120
|
}
|
|
104
121
|
// 文件系统配置,第一次使用fatfs镜像挂载,后续使用adb push更新应用
|
|
105
|
-
const systemImageBin = path_1.default.resolve(avdImagePath, 'vela_resource.
|
|
106
|
-
const dataImageBin = path_1.default.resolve(avdImagePath, '
|
|
122
|
+
const systemImageBin = path_1.default.resolve(avdImagePath, 'vela_resource.bin');
|
|
123
|
+
const dataImageBin = path_1.default.resolve(avdImagePath, 'vela_data.bin');
|
|
107
124
|
const coreBin = path_1.default.resolve(avdImagePath, 'coredump.core');
|
|
108
125
|
const imageMountStr = `-drive index=0,id=system,if=none,format=raw,file=${systemImageBin} \
|
|
109
126
|
-device virtio-blk-device,bus=virtio-mmio-bus.0,drive=system \
|
|
@@ -120,8 +137,9 @@ class MiwearInstance extends common_1.default {
|
|
|
120
137
|
const portSuffix = this.startOptions.vncPort - constants_1.defaultVncPort;
|
|
121
138
|
vncStr = `-vnc :${portSuffix}`;
|
|
122
139
|
}
|
|
140
|
+
// 根据disableNSH参数配置stdio
|
|
123
141
|
const stdioType = options.disableNSH ? 'pipe' : 'inherit';
|
|
124
|
-
//
|
|
142
|
+
// 启动模拟器的命令和参数
|
|
125
143
|
const cmd = `${emulatorBin} -nuttx -avd ${avdName} -avd-arch ${avdArch} -show-kernel -kernel ${nuttxBinPath} ${portMappingStr} ${windowStr} -qemu ${vncStr} ${imageMountStr}`;
|
|
126
144
|
const spawnArgs = cmd.split(' ');
|
|
127
145
|
const spawnBin = spawnArgs.shift();
|
|
@@ -129,16 +147,19 @@ class MiwearInstance extends common_1.default {
|
|
|
129
147
|
return new Promise((resolve) => {
|
|
130
148
|
var _a, _b, _c;
|
|
131
149
|
this.goldfishProcess = (0, child_process_1.spawn)(spawnBin, spawnArgs, { stdio: stdioType, shell: true, cwd: this.sdkHome });
|
|
150
|
+
// 监听错误流
|
|
132
151
|
(_a = this.goldfishProcess.stderr) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
|
|
133
152
|
const stderrCb = options.stderrCallback || console.log;
|
|
134
153
|
stderrCb(data.toString());
|
|
135
154
|
});
|
|
155
|
+
// 监听模拟器的退出事件
|
|
136
156
|
this.goldfishProcess.on('exit', (code) => {
|
|
137
157
|
ColorConsole_1.default.error(`### Emulator ### Goldfish emulator exited with code ${code}`);
|
|
138
158
|
if (options.exitCallback) {
|
|
139
159
|
options.exitCallback(code);
|
|
140
160
|
}
|
|
141
161
|
});
|
|
162
|
+
// disableNSH=false时,输出流会直接定向到终端,可在终端输入「回车」进入模拟器的终端,这种情况下无法拿到stdout中的数据
|
|
142
163
|
if (options.disableNSH) {
|
|
143
164
|
(_c = (_b = this.goldfishProcess) === null || _b === void 0 ? void 0 : _b.stdout) === null || _c === void 0 ? void 0 : _c.on('data', (data) => {
|
|
144
165
|
const msg = data.toString();
|
|
@@ -151,6 +172,7 @@ class MiwearInstance extends common_1.default {
|
|
|
151
172
|
});
|
|
152
173
|
}
|
|
153
174
|
else {
|
|
175
|
+
// disableNSH=true时,默认8s后模拟器启动成功
|
|
154
176
|
setTimeout(() => {
|
|
155
177
|
ColorConsole_1.default.info(`### Emulator ### Goldfish emulator starts successfully`);
|
|
156
178
|
resolve();
|
|
@@ -159,7 +181,11 @@ class MiwearInstance extends common_1.default {
|
|
|
159
181
|
});
|
|
160
182
|
});
|
|
161
183
|
}
|
|
162
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* 通过adb连接模拟器。
|
|
186
|
+
* 时间限制为10分钟,超时则表示连接失败
|
|
187
|
+
* @returns
|
|
188
|
+
*/
|
|
163
189
|
connectGoldfish() {
|
|
164
190
|
return __awaiter(this, void 0, void 0, function* () {
|
|
165
191
|
let adbConnected = false;
|
|
@@ -173,6 +199,7 @@ class MiwearInstance extends common_1.default {
|
|
|
173
199
|
ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${adbConnectCmd}`);
|
|
174
200
|
const str = yield adbMiwt.execAdbCmdAsync(adbConnectCmd);
|
|
175
201
|
ColorConsole_1.default.log(`### Emulator ### ${str}`);
|
|
202
|
+
// 查询模拟器的状态是否为“device”
|
|
176
203
|
const devices = yield adbMiwt.getAdbDevices();
|
|
177
204
|
ColorConsole_1.default.log(`### Emulator ### adb devices: ${JSON.stringify(devices)}`);
|
|
178
205
|
adbConnected =
|
|
@@ -189,7 +216,15 @@ class MiwearInstance extends common_1.default {
|
|
|
189
216
|
return adbConnected;
|
|
190
217
|
});
|
|
191
218
|
}
|
|
192
|
-
/**
|
|
219
|
+
/**
|
|
220
|
+
* 在模拟器中启动快应用
|
|
221
|
+
* 1. 检查release目录是否有打包好的rpk
|
|
222
|
+
* 2. 检查当前是否为调试模式.
|
|
223
|
+
* 若是,将debugger_ip.cfg推到模拟器的/data/目录下
|
|
224
|
+
* 若否,则要删除模拟器中已有的data/debugger_ip.cfg
|
|
225
|
+
* 3. 调用installRpkToAppList将当前快应用安装到模拟器的应用列表中
|
|
226
|
+
* @param options
|
|
227
|
+
*/
|
|
193
228
|
startupQuickApp(options) {
|
|
194
229
|
var _a;
|
|
195
230
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -216,29 +251,25 @@ class MiwearInstance extends common_1.default {
|
|
|
216
251
|
this.installRpkToAppList(rpkPath, this.appPathInEmulator);
|
|
217
252
|
});
|
|
218
253
|
}
|
|
219
|
-
/**
|
|
254
|
+
/**
|
|
255
|
+
* 将快应用安装到模拟器的应用列表
|
|
256
|
+
* 1. 使用adb push将打包好的rpk推到模拟器的/data/quickapp/app/
|
|
257
|
+
* 2. nsh中调用pm install命令安装应用
|
|
258
|
+
* @param rpkPath rpk的绝对目录
|
|
259
|
+
* @param targetDir 要将rpk放到模拟器的哪个目录下
|
|
260
|
+
*/
|
|
220
261
|
installRpkToAppList(rpkPath, targetDir) {
|
|
221
262
|
return __awaiter(this, void 0, void 0, function* () {
|
|
222
263
|
try {
|
|
223
264
|
const sn = `127.0.0.1:${this.adbPort}`;
|
|
224
265
|
const { package: packageName } = this.projectInfo;
|
|
225
|
-
// 1.
|
|
226
|
-
const lsCmd = `adb -s ${sn} shell ls ${this.appPathInEmulator}`;
|
|
227
|
-
const res = yield adbMiwt.execAdbCmdAsync(lsCmd);
|
|
228
|
-
if (res.includes(packageName)) {
|
|
229
|
-
const uninstallCmd = `adb -s ${sn} shell pm uninstall ${packageName}`;
|
|
230
|
-
ColorConsole_1.default.info(`### Emulator ### Excuting cmd: ${uninstallCmd}`);
|
|
231
|
-
adbMiwt.execAdbCmdAsync(uninstallCmd);
|
|
232
|
-
// 这里等待2s的作用是等qemu执行完uninstall操作的一系列工作
|
|
233
|
-
yield (0, utils_1.sleep)(2000);
|
|
234
|
-
}
|
|
235
|
-
// 2. adb push应用的rpk
|
|
266
|
+
// 1. adb push应用的rpk
|
|
236
267
|
const targetPath = `${targetDir}/${packageName}.rpk`;
|
|
237
268
|
const pushCmd = `adb -s ${sn} push "${rpkPath}" ${targetPath}`;
|
|
238
269
|
ColorConsole_1.default.info(`### Emulator ### Excuting cmd: ${pushCmd}`);
|
|
239
270
|
yield adbMiwt.execAdbCmdAsync(pushCmd);
|
|
240
|
-
yield (0, utils_1.sleep)(
|
|
241
|
-
//
|
|
271
|
+
yield (0, utils_1.sleep)(1000);
|
|
272
|
+
// 2. 安装应用(pm install时如何应用重名会覆盖)
|
|
242
273
|
const installCmd = `adb -s ${sn} shell pm install ${targetPath}`;
|
|
243
274
|
ColorConsole_1.default.info(`### Emulator ### Excuting cmd: ${installCmd}`);
|
|
244
275
|
adbMiwt.execAdbCmdAsync(installCmd);
|
|
@@ -248,6 +279,10 @@ class MiwearInstance extends common_1.default {
|
|
|
248
279
|
}
|
|
249
280
|
});
|
|
250
281
|
}
|
|
282
|
+
/** 连接模拟器中的调试服务,创建debugSocket
|
|
283
|
+
* 主要用于热更新时,通过debugSocket通知调试服务更新页面
|
|
284
|
+
* 设置了重连机制,会重复连接8次
|
|
285
|
+
*/
|
|
251
286
|
initDebugSocket() {
|
|
252
287
|
return new Promise((resolve, reject) => {
|
|
253
288
|
if (this.debugSocket && this.debugSocket.OPEN) {
|
|
@@ -280,7 +315,11 @@ class MiwearInstance extends common_1.default {
|
|
|
280
315
|
};
|
|
281
316
|
});
|
|
282
317
|
}
|
|
283
|
-
/** 通知模拟器更新
|
|
318
|
+
/** 通知模拟器更新
|
|
319
|
+
* 1. 确保已经成功连接模拟器中的调试服务
|
|
320
|
+
* 2. 使用adb push将build文件下的内容推到/data/quickapp/app/${packageName}
|
|
321
|
+
* 3. 发送Page.reload命令给调试服务,通知更新
|
|
322
|
+
*/
|
|
284
323
|
handleUpdate() {
|
|
285
324
|
var _a, _b;
|
|
286
325
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -314,7 +353,9 @@ class MiwearInstance extends common_1.default {
|
|
|
314
353
|
}
|
|
315
354
|
});
|
|
316
355
|
}
|
|
317
|
-
/**
|
|
356
|
+
/**
|
|
357
|
+
* 创建server端,监听打包过程中client端发来的消息
|
|
358
|
+
*/
|
|
318
359
|
createWebsockeServer() {
|
|
319
360
|
var _a;
|
|
320
361
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -326,6 +367,7 @@ class MiwearInstance extends common_1.default {
|
|
|
326
367
|
socket.on('error', err => {
|
|
327
368
|
ColorConsole_1.default.error(`### App Socket server ### Websocket server error: ${err.message}`);
|
|
328
369
|
});
|
|
370
|
+
// data的格式:{ type: string, data: any }
|
|
329
371
|
socket.on('message', (data) => __awaiter(this, void 0, void 0, function* () {
|
|
330
372
|
var _a;
|
|
331
373
|
const message = JSON.parse(data.toString());
|
package/lib/instance/preDev.d.ts
CHANGED
|
@@ -2,20 +2,54 @@
|
|
|
2
2
|
import { ChildProcess } from 'child_process';
|
|
3
3
|
import { INewGoldfishInstanceParams, IStartOptions } from '../typing/Instance';
|
|
4
4
|
import CommonInstance from './common';
|
|
5
|
+
/**
|
|
6
|
+
* OldGoldfishInstance
|
|
7
|
+
* 针对 Vela开发版(dev, 0.0.2)的镜像
|
|
8
|
+
*/
|
|
5
9
|
declare class OldGoldfishInstance extends CommonInstance {
|
|
6
10
|
private host9pPort;
|
|
7
11
|
v9fsProcess: ChildProcess | undefined;
|
|
8
12
|
constructor(params: INewGoldfishInstanceParams);
|
|
9
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* 1. 启动9p server
|
|
15
|
+
* 2. 将打包好的rpk推到host的挂载目录
|
|
16
|
+
* 3. 启动模拟器
|
|
17
|
+
* 4. 模拟器启动成功后,adb连接模拟器
|
|
18
|
+
* 5. 连接成功后,在模拟器中启动快应用
|
|
19
|
+
*/
|
|
10
20
|
start(options: IStartOptions): Promise<void>;
|
|
11
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* 在模拟器中启动快应用(快应用都在模拟器的/data/app目录下)
|
|
23
|
+
* 1. 是否为调试模式
|
|
24
|
+
* 若是,在模拟器终端执行vapp app/${packageName} &
|
|
25
|
+
* 若否,在模拟器终端执行vapp --jsdebugger=10.0.2.15:101 app/${packageName} &
|
|
26
|
+
*/
|
|
12
27
|
startupQuickApp(options: IStartOptions): void;
|
|
13
|
-
/** host启动9pServer
|
|
28
|
+
/** host启动9pServer
|
|
29
|
+
* 作用是将本地的quickappMountDir目录挂载到模拟器的/data目录
|
|
30
|
+
*/
|
|
14
31
|
ensure9pServerRunnning(): Promise<void>;
|
|
15
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* 启动模拟器
|
|
34
|
+
* 1. 通过options生成模拟器的启动命令
|
|
35
|
+
* 2. 执行启动命令
|
|
36
|
+
* 3. 判断模拟器是否启动成功
|
|
37
|
+
* 3.1 若disableNSH=true,输出流中匹配到/quickapp_rpk_installer_init|rpk installer init done/,认为模拟器启动成功了
|
|
38
|
+
* 3.2 若disableNSH=false,认为8s过后模拟器启动成功了
|
|
39
|
+
* @param options
|
|
40
|
+
* @returns
|
|
41
|
+
*/
|
|
16
42
|
startGoldfish(options: IStartOptions): Promise<void>;
|
|
17
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* 通过adb连接模拟器。
|
|
45
|
+
* 时间限制为10分钟,超时则表示连接失败
|
|
46
|
+
* @returns
|
|
47
|
+
*/
|
|
18
48
|
connectGoldfish(): Promise<boolean>;
|
|
49
|
+
/**
|
|
50
|
+
* 推送文件到本地的${sdkHome}/qa/app/${packageName}目录
|
|
51
|
+
* @param sourceRoot 源目录
|
|
52
|
+
*/
|
|
19
53
|
pushRpk(sourceRoot: string): Promise<void>;
|
|
20
54
|
/** 停止模拟器并释放相关资源 */
|
|
21
55
|
stop(): Promise<void>;
|
package/lib/instance/preDev.js
CHANGED
|
@@ -46,12 +46,22 @@ const path_1 = __importDefault(require("path"));
|
|
|
46
46
|
const portfinder_1 = __importDefault(require("portfinder"));
|
|
47
47
|
const constants_1 = require("../static/constants");
|
|
48
48
|
const common_1 = __importDefault(require("./common"));
|
|
49
|
+
/**
|
|
50
|
+
* OldGoldfishInstance
|
|
51
|
+
* 针对 Vela开发版(dev, 0.0.2)的镜像
|
|
52
|
+
*/
|
|
49
53
|
class OldGoldfishInstance extends common_1.default {
|
|
50
54
|
constructor(params) {
|
|
51
55
|
super(params);
|
|
52
56
|
this.host9pPort = 7878;
|
|
53
57
|
}
|
|
54
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* 1. 启动9p server
|
|
60
|
+
* 2. 将打包好的rpk推到host的挂载目录
|
|
61
|
+
* 3. 启动模拟器
|
|
62
|
+
* 4. 模拟器启动成功后,adb连接模拟器
|
|
63
|
+
* 5. 连接成功后,在模拟器中启动快应用
|
|
64
|
+
*/
|
|
55
65
|
start(options) {
|
|
56
66
|
return __awaiter(this, void 0, void 0, function* () {
|
|
57
67
|
// host启动9p server
|
|
@@ -62,9 +72,11 @@ class OldGoldfishInstance extends common_1.default {
|
|
|
62
72
|
this.pushRpk(buildedFilesPath);
|
|
63
73
|
// 启动模拟器
|
|
64
74
|
yield this.startGoldfish(options);
|
|
75
|
+
// adb连接快应用
|
|
65
76
|
const connected = yield this.connectGoldfish();
|
|
66
77
|
if (connected) {
|
|
67
78
|
ColorConsole_1.default.log('### Emulator ### Goldfish emulator connected successfully');
|
|
79
|
+
// 如果是首次启动,创建server端监听事件
|
|
68
80
|
if (this.isFirstStart && this.startOptions.serverPort) {
|
|
69
81
|
yield this.createWebsockeServer();
|
|
70
82
|
}
|
|
@@ -77,7 +89,12 @@ class OldGoldfishInstance extends common_1.default {
|
|
|
77
89
|
}
|
|
78
90
|
});
|
|
79
91
|
}
|
|
80
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* 在模拟器中启动快应用(快应用都在模拟器的/data/app目录下)
|
|
94
|
+
* 1. 是否为调试模式
|
|
95
|
+
* 若是,在模拟器终端执行vapp app/${packageName} &
|
|
96
|
+
* 若否,在模拟器终端执行vapp --jsdebugger=10.0.2.15:101 app/${packageName} &
|
|
97
|
+
*/
|
|
81
98
|
startupQuickApp(options) {
|
|
82
99
|
try {
|
|
83
100
|
const { package: packageName } = this.projectInfo;
|
|
@@ -86,6 +103,7 @@ class OldGoldfishInstance extends common_1.default {
|
|
|
86
103
|
ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${mountCmd}`);
|
|
87
104
|
adbMiwt.execAdbCmdSync(mountCmd);
|
|
88
105
|
let vappCmd = `adb -s 127.0.0.1:${this.adbPort} shell vapp app/${packageName} &`;
|
|
106
|
+
// 调试情况下,需要加--jsdebugger=10.0.2.15:101
|
|
89
107
|
if (options.devtool) {
|
|
90
108
|
vappCmd = `adb -s 127.0.0.1:${this.adbPort} shell vapp --jsdebugger=10.0.2.15:101 app/${packageName} &`;
|
|
91
109
|
}
|
|
@@ -97,7 +115,9 @@ class OldGoldfishInstance extends common_1.default {
|
|
|
97
115
|
ColorConsole_1.default.error(`### Emulator ### Failed to startup quickapp: ${e.message}`);
|
|
98
116
|
}
|
|
99
117
|
}
|
|
100
|
-
/** host启动9pServer
|
|
118
|
+
/** host启动9pServer
|
|
119
|
+
* 作用是将本地的quickappMountDir目录挂载到模拟器的/data目录
|
|
120
|
+
*/
|
|
101
121
|
ensure9pServerRunnning() {
|
|
102
122
|
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
103
123
|
var _a;
|
|
@@ -121,6 +141,7 @@ class OldGoldfishInstance extends common_1.default {
|
|
|
121
141
|
address,
|
|
122
142
|
'--debug'
|
|
123
143
|
]);
|
|
144
|
+
// 监听stderr,判断9p server是否启动成功了
|
|
124
145
|
(_a = this.v9fsProcess.stderr) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
|
|
125
146
|
const output = data.toString();
|
|
126
147
|
if (output.match(/Server started, listening on: 127.0.0.1:(\d+)/)) {
|
|
@@ -129,18 +150,30 @@ class OldGoldfishInstance extends common_1.default {
|
|
|
129
150
|
return resolve();
|
|
130
151
|
}
|
|
131
152
|
});
|
|
153
|
+
// 监听exit事件,判断9p server是否退出了
|
|
132
154
|
this.v9fsProcess.on('exit', (code) => {
|
|
133
155
|
ColorConsole_1.default.error(`### Emulator ### ya-vm-file-server exited with code ${code}`);
|
|
134
156
|
return reject();
|
|
135
157
|
});
|
|
136
158
|
}));
|
|
137
159
|
}
|
|
138
|
-
/**
|
|
160
|
+
/**
|
|
161
|
+
* 启动模拟器
|
|
162
|
+
* 1. 通过options生成模拟器的启动命令
|
|
163
|
+
* 2. 执行启动命令
|
|
164
|
+
* 3. 判断模拟器是否启动成功
|
|
165
|
+
* 3.1 若disableNSH=true,输出流中匹配到/quickapp_rpk_installer_init|rpk installer init done/,认为模拟器启动成功了
|
|
166
|
+
* 3.2 若disableNSH=false,认为8s过后模拟器启动成功了
|
|
167
|
+
* @param options
|
|
168
|
+
* @returns
|
|
169
|
+
*/
|
|
139
170
|
startGoldfish(options) {
|
|
140
171
|
return __awaiter(this, void 0, void 0, function* () {
|
|
141
172
|
const { avdName, devtool } = options;
|
|
173
|
+
// 获取emulator bin的绝对路径
|
|
142
174
|
const emulatorBin = this.getEmulatorBinPath();
|
|
143
175
|
ColorConsole_1.default.log(`### Emulator ### emulator path: ${emulatorBin}`);
|
|
176
|
+
// 获取vela镜像的绝对路径
|
|
144
177
|
const avdInfo = this.velaAvdCls.getVelaAvdInfo(avdName);
|
|
145
178
|
const { avdArch, avdImagePath } = avdInfo;
|
|
146
179
|
this.adbPort = yield portfinder_1.default.getPortPromise({ port: this.adbPort });
|
|
@@ -150,6 +183,7 @@ class OldGoldfishInstance extends common_1.default {
|
|
|
150
183
|
}
|
|
151
184
|
const nuttxBinPath = path_1.default.resolve(avdImagePath, 'nuttx');
|
|
152
185
|
ColorConsole_1.default.log(`### Emulator ### nuttx path: ${nuttxBinPath}`);
|
|
186
|
+
// 端口映射,adb端口和debug端口
|
|
153
187
|
let portMappingStr = `user,id=u1,hostfwd=tcp:127.0.0.1:${this.adbPort}-10.0.2.15:5555`;
|
|
154
188
|
if (devtool) {
|
|
155
189
|
this.debugPort = yield portfinder_1.default.getPortPromise({ port: this.debugPort });
|
|
@@ -157,7 +191,6 @@ class OldGoldfishInstance extends common_1.default {
|
|
|
157
191
|
portMappingStr += `,hostfwd=tcp:127.0.0.1:${this.debugPort}-10.0.2.15:101`;
|
|
158
192
|
}
|
|
159
193
|
ColorConsole_1.default.log(`### Emulator ### Start qemu with TCP: ${portMappingStr}`);
|
|
160
|
-
const stdioType = options.disableNSH ? 'pipe' : 'inherit';
|
|
161
194
|
// vnc配置
|
|
162
195
|
let noWindow = false;
|
|
163
196
|
let vncStr = '';
|
|
@@ -166,6 +199,8 @@ class OldGoldfishInstance extends common_1.default {
|
|
|
166
199
|
const portSuffix = options.vncPort - constants_1.defaultVncPort;
|
|
167
200
|
vncStr = `-vnc :${portSuffix}`;
|
|
168
201
|
}
|
|
202
|
+
// 根据disableNSH参数配置stdio
|
|
203
|
+
const stdioType = options.disableNSH ? 'pipe' : 'inherit';
|
|
169
204
|
// 启动goldfish的命令和参数
|
|
170
205
|
const spawnBin = emulatorBin;
|
|
171
206
|
const spawnArgs = [
|
|
@@ -189,16 +224,19 @@ class OldGoldfishInstance extends common_1.default {
|
|
|
189
224
|
return new Promise((resolve) => {
|
|
190
225
|
var _a;
|
|
191
226
|
this.goldfishProcess = (0, child_process_1.spawn)(spawnBin, spawnArgs, { stdio: stdioType, shell: true });
|
|
227
|
+
// 监听错误流
|
|
192
228
|
(_a = this.goldfishProcess.stderr) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
|
|
193
229
|
const stderrCb = options.stderrCallback || console.log;
|
|
194
230
|
stderrCb(data.toString());
|
|
195
231
|
});
|
|
232
|
+
// 监听模拟器的退出事件
|
|
196
233
|
this.goldfishProcess.on('exit', (code) => {
|
|
197
234
|
ColorConsole_1.default.error(`### Emulator ### Goldfish emulator exited with code ${code}`);
|
|
198
235
|
if (options.exitCallback) {
|
|
199
236
|
options.exitCallback(code);
|
|
200
237
|
}
|
|
201
238
|
});
|
|
239
|
+
// 监听输出流。输出了'(NSH)'标识或者2s后则认为模拟器启动成功
|
|
202
240
|
const p1 = new Promise((resolve) => {
|
|
203
241
|
var _a, _b;
|
|
204
242
|
(_b = (_a = this.goldfishProcess) === null || _a === void 0 ? void 0 : _a.stdout) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
|
|
@@ -218,7 +256,11 @@ class OldGoldfishInstance extends common_1.default {
|
|
|
218
256
|
});
|
|
219
257
|
});
|
|
220
258
|
}
|
|
221
|
-
/**
|
|
259
|
+
/**
|
|
260
|
+
* 通过adb连接模拟器。
|
|
261
|
+
* 时间限制为10分钟,超时则表示连接失败
|
|
262
|
+
* @returns
|
|
263
|
+
*/
|
|
222
264
|
connectGoldfish() {
|
|
223
265
|
return __awaiter(this, void 0, void 0, function* () {
|
|
224
266
|
let adbConnected = false;
|
|
@@ -248,6 +290,10 @@ class OldGoldfishInstance extends common_1.default {
|
|
|
248
290
|
return adbConnected;
|
|
249
291
|
});
|
|
250
292
|
}
|
|
293
|
+
/**
|
|
294
|
+
* 推送文件到本地的${sdkHome}/qa/app/${packageName}目录
|
|
295
|
+
* @param sourceRoot 源目录
|
|
296
|
+
*/
|
|
251
297
|
pushRpk(sourceRoot) {
|
|
252
298
|
return __awaiter(this, void 0, void 0, function* () {
|
|
253
299
|
const { package: appPackageName } = this.projectInfo;
|
package/lib/static/constants.js
CHANGED
package/lib/utils/index.d.ts
CHANGED
package/lib/utils/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiot-toolkit/emulator",
|
|
3
|
-
"version": "2.0.2-beta.
|
|
3
|
+
"version": "2.0.2-beta.5",
|
|
4
4
|
"description": "vela emulator tool.",
|
|
5
5
|
"homepage": "",
|
|
6
6
|
"license": "ISC",
|
|
@@ -35,10 +35,14 @@
|
|
|
35
35
|
"emulator"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@aiot-toolkit/aiotpack": "2.0.2-beta.
|
|
39
|
-
"@aiot-toolkit/shared-utils": "2.0.2-beta.
|
|
38
|
+
"@aiot-toolkit/aiotpack": "2.0.2-beta.5",
|
|
39
|
+
"@aiot-toolkit/shared-utils": "2.0.2-beta.5",
|
|
40
40
|
"find-process": "^1.4.7",
|
|
41
41
|
"portfinder": "^1.0.32"
|
|
42
42
|
},
|
|
43
|
-
"
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/fs-extra": "^11.0.4",
|
|
45
|
+
"fs-extra": "^11.2.0"
|
|
46
|
+
},
|
|
47
|
+
"gitHead": "8c18753992c453c6569a3125378e75fbf4760a1f"
|
|
44
48
|
}
|