@aiot-toolkit/emulator 2.0.2-dev.7 → 2.0.2
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 +93 -144
- 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 +16 -5
- package/lib/static/constants.d.ts +0 -19
- package/lib/static/constants.js +0 -27
package/lib/instance/common.js
CHANGED
|
@@ -39,25 +39,31 @@ const UxFileUtils_1 = __importDefault(require("@aiot-toolkit/aiotpack/lib/utils/
|
|
|
39
39
|
const ColorConsole_1 = __importDefault(require("@aiot-toolkit/shared-utils/lib/ColorConsole"));
|
|
40
40
|
const adbMiwt = __importStar(require("@miwt/adb"));
|
|
41
41
|
const child_process_1 = require("child_process");
|
|
42
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
42
43
|
const os_1 = __importDefault(require("os"));
|
|
43
44
|
const path_1 = __importDefault(require("path"));
|
|
44
|
-
const portfinder_1 = __importDefault(require("portfinder"));
|
|
45
45
|
const ws_1 = require("ws");
|
|
46
46
|
const avd_1 = __importDefault(require("../avd"));
|
|
47
|
-
const constants_1 = require("../
|
|
47
|
+
const constants_1 = require("../emulatorutil/constants");
|
|
48
48
|
const utils_1 = require("../utils");
|
|
49
49
|
/**
|
|
50
50
|
* CommonInstance
|
|
51
51
|
*/
|
|
52
52
|
class CommonInstance {
|
|
53
53
|
constructor(params) {
|
|
54
|
-
this.
|
|
55
|
-
this.debugPort = 10055;
|
|
54
|
+
this.quickappStartedFlag = /Start App loop/;
|
|
56
55
|
this.isFirstStart = true;
|
|
56
|
+
this.isDistributedApp = false;
|
|
57
57
|
this.projectPath = params.projectPath;
|
|
58
58
|
this.sdkHome = params.sdkHome || constants_1.defaultSDKHome;
|
|
59
59
|
this.avdHome = params.avdHome || constants_1.defaultAvdHome;
|
|
60
60
|
this.isRpk = params.sourceRoot === './';
|
|
61
|
+
const projectJsonFile = path_1.default.resolve(this.projectPath, '.project.json');
|
|
62
|
+
const projectJsonExist = fs_extra_1.default.existsSync(projectJsonFile);
|
|
63
|
+
if (projectJsonExist) {
|
|
64
|
+
const projectJsonInfo = fs_extra_1.default.readJSONSync(projectJsonFile);
|
|
65
|
+
this.isDistributedApp = projectJsonInfo.projectType === 'distributed';
|
|
66
|
+
}
|
|
61
67
|
this.velaAvdCls = new avd_1.default({
|
|
62
68
|
sdkHome: this.sdkHome,
|
|
63
69
|
avdHome: this.avdHome
|
|
@@ -75,153 +81,64 @@ class CommonInstance {
|
|
|
75
81
|
/** 在goldfish模拟器中运行快应用 */
|
|
76
82
|
start(options) {
|
|
77
83
|
return __awaiter(this, void 0, void 0, function* () {
|
|
78
|
-
|
|
79
|
-
// 启动模拟器
|
|
80
|
-
yield this.startGoldfish(options);
|
|
81
|
-
const connected = yield this.connectGoldfish();
|
|
82
|
-
if (connected) {
|
|
83
|
-
ColorConsole_1.default.log('### Emulator ### Goldfish emulator connected successfully');
|
|
84
|
-
if (this.isFirstStart && this.startOptions.serverPort) {
|
|
85
|
-
yield this.createWebsockeServer();
|
|
86
|
-
}
|
|
87
|
-
// adb push快应用到模拟器的/data/app目录
|
|
88
|
-
const buildedFilesPath = this.isRpk ? this.projectPath : path_1.default.resolve(this.projectPath, './build');
|
|
89
|
-
this.pushRpk(buildedFilesPath);
|
|
90
|
-
// 在模拟器中启动快应用
|
|
91
|
-
this.startupQuickApp(options);
|
|
92
|
-
this.isFirstStart = false;
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
ColorConsole_1.default.throw('### Emulator ### Failed to connect emulator, please check whether the adb is normal');
|
|
96
|
-
}
|
|
84
|
+
throw new Error(`start method not implemented, ${options}`);
|
|
97
85
|
});
|
|
98
86
|
}
|
|
99
|
-
/**
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (options.devtool) {
|
|
104
|
-
vappCmd = `adb -s 127.0.0.1:${this.adbPort} shell vapp --jsdebugger=10.0.2.15:101 app/${this.projectInfo.package} &`;
|
|
105
|
-
}
|
|
106
|
-
ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${vappCmd}`);
|
|
107
|
-
// vapp进程会一直pending,不会退出。这里必须加stdio: 'ignore',否则快应用无法运行成功
|
|
108
|
-
adbMiwt.execAdbCmdAsync(vappCmd, { stdio: 'ignore', encoding: 'utf-8' });
|
|
109
|
-
}
|
|
110
|
-
catch (e) {
|
|
111
|
-
ColorConsole_1.default.error(`### Emulator ### Failed to startup quickapp: ${e.message}`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
/** 启动goldfish模拟器 */
|
|
115
|
-
startGoldfish(options) {
|
|
116
|
-
var _a;
|
|
87
|
+
/**
|
|
88
|
+
* 判断模拟器是否 ready
|
|
89
|
+
*/
|
|
90
|
+
isConnected() {
|
|
117
91
|
return __awaiter(this, void 0, void 0, function* () {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
ColorConsole_1.default.log(`### Emulator ### adb port: ${this.adbPort}`);
|
|
125
|
-
if (!avdImagePath) {
|
|
126
|
-
return ColorConsole_1.default.throw(`### Emulator ### Unable to find vela image via avd`);
|
|
127
|
-
}
|
|
128
|
-
const nuttxBinPath = path_1.default.resolve(avdImagePath, 'nuttx');
|
|
129
|
-
ColorConsole_1.default.log(`### Emulator ### nuttx path: ${nuttxBinPath}`);
|
|
130
|
-
// 端口映射
|
|
131
|
-
const adbPortMappingStr = `-network-user-mode-options hostfwd=tcp:127.0.0.1:${this.adbPort}-10.0.2.15:5555`;
|
|
132
|
-
let debugPortMappingStr = '';
|
|
133
|
-
if (devtool) {
|
|
134
|
-
debugPortMappingStr = `-network-user-mode-options hostfwd=tcp:127.0.0.1:${this.debugPort}-10.0.2.15:101`;
|
|
135
|
-
}
|
|
136
|
-
// 文件系统配置,第一次使用fatfs镜像挂载,后续使用adb push更新应用
|
|
137
|
-
const systemImageBin = path_1.default.resolve(this.sdkHome, 'tools/image/system.img');
|
|
138
|
-
const dataImageBin = path_1.default.resolve(this.sdkHome, 'tools/image/data.img');
|
|
139
|
-
const imageMountStr = `-drive index=0,id=system,if=none,format=raw,file=${systemImageBin} \
|
|
140
|
-
-device virtio-blk-device,bus=virtio-mmio-bus.0,drive=system \
|
|
141
|
-
-drive index=1,id=userdata,if=none,format=raw,file=${dataImageBin} \
|
|
142
|
-
-device virtio-blk-device,bus=virtio-mmio-bus.1,drive=userdata \
|
|
143
|
-
-device virtio-snd,bus=virtio-mmio-bus.2 -allow-host-audio -semihosting`;
|
|
144
|
-
// vnc配置
|
|
145
|
-
let windowStr = '';
|
|
146
|
-
let vncStr = '';
|
|
147
|
-
if ((_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.vncPort) {
|
|
148
|
-
windowStr = '-no-window';
|
|
149
|
-
const portSuffix = this.startOptions.vncPort - constants_1.defaultVncPort;
|
|
150
|
-
vncStr = `-vnc :${portSuffix}`;
|
|
151
|
-
}
|
|
152
|
-
const stdioType = options.disableNSH ? 'pipe' : 'inherit';
|
|
153
|
-
// 启动goldfish的命令和参数
|
|
154
|
-
const cmd = `${emulatorBin} -nuttx -avd ${avdName} -avd-arch ${avdArch} -show-kernel -kernel ${nuttxBinPath} ${adbPortMappingStr} ${debugPortMappingStr} -qemu ${windowStr} ${vncStr} ${imageMountStr}`;
|
|
155
|
-
const spawnArgs = cmd.split(' ');
|
|
156
|
-
const spawnBin = spawnArgs.shift();
|
|
157
|
-
ColorConsole_1.default.log(`### Emulator ### Start CMD: ${cmd}`);
|
|
158
|
-
return new Promise((resolve) => {
|
|
159
|
-
var _a, _b;
|
|
160
|
-
this.goldfishProcess = (0, child_process_1.spawn)(spawnBin, spawnArgs, { stdio: stdioType, shell: true });
|
|
161
|
-
if (options.disableNSH) {
|
|
162
|
-
(_a = this.goldfishProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
|
|
163
|
-
console.log(data.toString());
|
|
164
|
-
if (data.toString().includes('(NSH)')) {
|
|
165
|
-
ColorConsole_1.default.log(`### Emulator ### Goldfish emulator starts successfully`);
|
|
166
|
-
resolve();
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
(_b = this.goldfishProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
|
|
170
|
-
console.log(data.toString());
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
else {
|
|
174
|
-
setTimeout(() => {
|
|
175
|
-
ColorConsole_1.default.log(`### Emulator ### Goldfish emulator starts successfully`);
|
|
176
|
-
resolve();
|
|
177
|
-
}, 2000);
|
|
178
|
-
}
|
|
179
|
-
this.goldfishProcess.on('exit', (code) => {
|
|
180
|
-
ColorConsole_1.default.error(`### Emulator ### Goldfish emulator exited with code ${code}`);
|
|
181
|
-
});
|
|
182
|
-
});
|
|
92
|
+
return (0, utils_1.tryRun)(() => __awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
const devices = yield adbMiwt.getAdbDevices();
|
|
94
|
+
ColorConsole_1.default.log(`### Emulator ### adb devices: ${JSON.stringify(devices)}`);
|
|
95
|
+
const curDev = devices.find((t) => t.sn === this.sn);
|
|
96
|
+
return (curDev === null || curDev === void 0 ? void 0 : curDev.status) === 'device';
|
|
97
|
+
}), 10, 500);
|
|
183
98
|
});
|
|
184
99
|
}
|
|
185
|
-
/**
|
|
100
|
+
/**
|
|
101
|
+
* 通过adb连接模拟器。
|
|
102
|
+
* 时间限制为 @param timeout 秒,超时则表示连接失败
|
|
103
|
+
* @returns
|
|
104
|
+
*/
|
|
186
105
|
connectGoldfish() {
|
|
187
|
-
return __awaiter(this,
|
|
188
|
-
let adbConnected = false;
|
|
106
|
+
return __awaiter(this, arguments, void 0, function* (timeout = 10) {
|
|
107
|
+
let adbConnected = false; // adb是否连接成功
|
|
108
|
+
let enableLoop = true; // 是否允许循环,用于终止adb的连接
|
|
109
|
+
let needKill = false;
|
|
189
110
|
const connectFn = () => __awaiter(this, void 0, void 0, function* () {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const str = adbMiwt.execAdbCmdSync(adbConnectCmd);
|
|
111
|
+
while (enableLoop && !adbConnected) {
|
|
112
|
+
if (needKill) {
|
|
113
|
+
const adbKillCmd = `adb kill-server`;
|
|
114
|
+
ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${adbKillCmd}`);
|
|
115
|
+
yield adbMiwt.execAdbCmdAsync(adbKillCmd);
|
|
116
|
+
}
|
|
117
|
+
const str = yield this.connectDevice();
|
|
198
118
|
ColorConsole_1.default.log(`### Emulator ### ${str}`);
|
|
119
|
+
// 查询模拟器的状态是否为“device”
|
|
199
120
|
const devices = yield adbMiwt.getAdbDevices();
|
|
200
121
|
ColorConsole_1.default.log(`### Emulator ### adb devices: ${JSON.stringify(devices)}`);
|
|
201
|
-
|
|
202
|
-
|
|
122
|
+
const curDev = devices.find((t) => t.sn === this.sn);
|
|
123
|
+
if ((curDev === null || curDev === void 0 ? void 0 : curDev.status) === 'offline')
|
|
124
|
+
needKill = true;
|
|
125
|
+
adbConnected = devices.some((item) => item.sn === this.sn && item.status === 'device');
|
|
203
126
|
}
|
|
204
|
-
Promise.resolve(adbConnected);
|
|
127
|
+
return Promise.resolve(adbConnected);
|
|
205
128
|
});
|
|
206
|
-
yield Promise.race([
|
|
129
|
+
const res = yield Promise.race([
|
|
207
130
|
connectFn(),
|
|
208
131
|
new Promise((resolve) => {
|
|
209
|
-
setTimeout(() =>
|
|
132
|
+
setTimeout(() => {
|
|
133
|
+
if (!adbConnected) {
|
|
134
|
+
enableLoop = false;
|
|
135
|
+
}
|
|
136
|
+
// 超时则认为adb没有连接成功
|
|
137
|
+
resolve(false);
|
|
138
|
+
}, timeout * 1000);
|
|
210
139
|
})
|
|
211
140
|
]);
|
|
212
|
-
return
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
/** 将打包后的文件推到挂载的快应用目录 */
|
|
216
|
-
pushRpk(sourceRoot) {
|
|
217
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
218
|
-
const sn = `127.0.0.1:${this.adbPort}`;
|
|
219
|
-
const { package: appPackageName } = UxFileUtils_1.default.getMainfestInfo(this.projectPath);
|
|
220
|
-
const appRunDir = '/data/app';
|
|
221
|
-
ColorConsole_1.default.log(`### Emulator ### Pushing ${appPackageName} to ${appRunDir}`);
|
|
222
|
-
(0, child_process_1.execSync)(`adb -s ${sn} shell mkdir ${appRunDir}/${appPackageName}`, { stdio: 'ignore' });
|
|
223
|
-
yield adbMiwt.execAdbCmdAsync(`adb -s ${sn} push ${sourceRoot}/* ${appRunDir}/${appPackageName}`);
|
|
224
|
-
ColorConsole_1.default.log(`### Emulator ### Push ${appPackageName} to ${appRunDir} successfully`);
|
|
141
|
+
return res;
|
|
225
142
|
});
|
|
226
143
|
}
|
|
227
144
|
/** 杀死进程 */
|
|
@@ -246,8 +163,8 @@ class CommonInstance {
|
|
|
246
163
|
}
|
|
247
164
|
/** 停止模拟器并释放相关资源 */
|
|
248
165
|
stop() {
|
|
249
|
-
var _a;
|
|
250
166
|
return __awaiter(this, void 0, void 0, function* () {
|
|
167
|
+
var _a, _b;
|
|
251
168
|
if (this.goldfishProcess) {
|
|
252
169
|
// Linux删除goldfishProcess后,子进程仍存在,导致模拟器窗口未关闭
|
|
253
170
|
// 这里的作用是手动删除创建出来的子进程
|
|
@@ -255,29 +172,34 @@ class CommonInstance {
|
|
|
255
172
|
// process.kill(this.goldfishProcess.pid + 1)
|
|
256
173
|
// }
|
|
257
174
|
this.killProcess(this.goldfishProcess);
|
|
258
|
-
|
|
175
|
+
if ((_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.avdName) {
|
|
176
|
+
const emulatorProcessFlag = `-avd ${(_b = this.startOptions) === null || _b === void 0 ? void 0 : _b.avdName} -avd-arch`;
|
|
177
|
+
yield (0, utils_1.killProcessByCmd)(emulatorProcessFlag);
|
|
178
|
+
}
|
|
259
179
|
this.goldfishProcess = undefined;
|
|
260
180
|
}
|
|
261
181
|
});
|
|
262
182
|
}
|
|
263
183
|
/** 重启模拟器 */
|
|
264
184
|
restart() {
|
|
265
|
-
this
|
|
266
|
-
|
|
185
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
186
|
+
yield this.stop();
|
|
187
|
+
this.start(this.startOptions);
|
|
188
|
+
});
|
|
267
189
|
}
|
|
268
190
|
/** 创建server */
|
|
269
191
|
createWebsockeServer() {
|
|
270
|
-
var _a;
|
|
271
192
|
return __awaiter(this, void 0, void 0, function* () {
|
|
193
|
+
var _a;
|
|
272
194
|
const wsServer = new ws_1.WebSocketServer({
|
|
273
|
-
port: (_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.serverPort
|
|
195
|
+
port: (_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.serverPort
|
|
274
196
|
});
|
|
275
|
-
wsServer.on('connection', socket => {
|
|
197
|
+
wsServer.on('connection', (socket) => {
|
|
276
198
|
ColorConsole_1.default.success(`### App Socket server ### Websocket connects to websocket server`);
|
|
277
|
-
socket.on('error', err => {
|
|
199
|
+
socket.on('error', (err) => {
|
|
278
200
|
ColorConsole_1.default.error(`### App Socket server ### Websocket server error: ${err.message}`);
|
|
279
201
|
});
|
|
280
|
-
socket.on('message', data => {
|
|
202
|
+
socket.on('message', (data) => {
|
|
281
203
|
const message = JSON.parse(data.toString());
|
|
282
204
|
ColorConsole_1.default.log(`### App Socket server ### Websocket server get data: ${data}`);
|
|
283
205
|
if (message.type === 'restart') {
|
|
@@ -290,5 +212,32 @@ class CommonInstance {
|
|
|
290
212
|
});
|
|
291
213
|
});
|
|
292
214
|
}
|
|
215
|
+
connectDevice() {
|
|
216
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
217
|
+
const adbConnectCmd = `adb connect ${this.sn}`;
|
|
218
|
+
ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${adbConnectCmd}`);
|
|
219
|
+
let pending = true;
|
|
220
|
+
const p1 = adbMiwt.execAdbCmdAsync(adbConnectCmd);
|
|
221
|
+
let timer;
|
|
222
|
+
// 超过一定时间还没有连接成功,则 kill-server 后再试
|
|
223
|
+
// 用于处理 adb connect 一直 pending 的情况
|
|
224
|
+
const p2 = new Promise((resolve) => {
|
|
225
|
+
timer = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
226
|
+
if (pending) {
|
|
227
|
+
const adbKillCmd = `adb kill-server`;
|
|
228
|
+
ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${adbKillCmd}`);
|
|
229
|
+
yield adbMiwt.execAdbCmdAsync(adbKillCmd);
|
|
230
|
+
}
|
|
231
|
+
resolve();
|
|
232
|
+
}), 10 * 1000);
|
|
233
|
+
});
|
|
234
|
+
p1.then((r) => {
|
|
235
|
+
pending = false;
|
|
236
|
+
clearTimeout(timer);
|
|
237
|
+
console.log(r);
|
|
238
|
+
});
|
|
239
|
+
return Promise.race([p1, p2]);
|
|
240
|
+
});
|
|
241
|
+
}
|
|
293
242
|
}
|
|
294
243
|
exports.default = CommonInstance;
|
package/lib/instance/dev.d.ts
CHANGED
|
@@ -1,15 +1,51 @@
|
|
|
1
|
+
import readline from 'readline';
|
|
1
2
|
import { INewGoldfishInstanceParams, IStartOptions } from '../typing/Instance';
|
|
2
3
|
import CommonInstance from './common';
|
|
3
4
|
declare class GoldfishInstance extends CommonInstance {
|
|
4
|
-
|
|
5
|
+
appDir: string;
|
|
6
|
+
emulatorStartedFlag: string;
|
|
7
|
+
stdoutReadline: readline.Interface;
|
|
8
|
+
stderrReadline: readline.Interface;
|
|
5
9
|
constructor(params: INewGoldfishInstanceParams);
|
|
6
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* 1. 启动模拟器
|
|
12
|
+
* 2. 启动成功后,adb连接模拟器
|
|
13
|
+
* 3. 连接成功后,在模拟器中启动快应用
|
|
14
|
+
*/
|
|
7
15
|
start(options: IStartOptions): Promise<void>;
|
|
8
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* 在模拟器中启动快应用
|
|
18
|
+
* 通过vapp命令启动,调试时需额外配置--jsdebugger参数
|
|
19
|
+
* @param options
|
|
20
|
+
*/
|
|
9
21
|
startupQuickApp(options: IStartOptions): Promise<void>;
|
|
10
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* 启动模拟器
|
|
24
|
+
* 1. 通过options生成模拟器的启动命令
|
|
25
|
+
* 2. 执行启动命令
|
|
26
|
+
* 3. 判断模拟器是否启动成功
|
|
27
|
+
* 3.1 若disableNSH=true,输出流中匹配到(NSH),认为模拟器启动成功了
|
|
28
|
+
* 3.2 若disableNSH=false,认为2s过后模拟器启动成功了
|
|
29
|
+
* @param options
|
|
30
|
+
* @returns
|
|
31
|
+
*/
|
|
11
32
|
startGoldfish(options: IStartOptions): Promise<void>;
|
|
12
|
-
/**
|
|
13
|
-
|
|
33
|
+
/**
|
|
34
|
+
* 将目录通过adb push到模拟器中
|
|
35
|
+
*/
|
|
36
|
+
pushAndInstall(rpkPath?: string): Promise<void>;
|
|
37
|
+
/** 通过命令行启动时,在watich模式下监听websocket消息,
|
|
38
|
+
* 在文件发生变动时,重新启动应用
|
|
39
|
+
*/
|
|
40
|
+
restart(): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* 重新推送,然后重启应用
|
|
43
|
+
*/
|
|
44
|
+
pushAndReloadApp(): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* 重启应用
|
|
47
|
+
*/
|
|
48
|
+
reloadApp(): Promise<void>;
|
|
49
|
+
reboot(): Promise<boolean>;
|
|
14
50
|
}
|
|
15
51
|
export default GoldfishInstance;
|
package/lib/instance/dev.js
CHANGED
|
@@ -38,47 +38,59 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
38
38
|
const ColorConsole_1 = __importDefault(require("@aiot-toolkit/shared-utils/lib/ColorConsole"));
|
|
39
39
|
const adbMiwt = __importStar(require("@miwt/adb"));
|
|
40
40
|
const child_process_1 = require("child_process");
|
|
41
|
+
const readline_1 = __importDefault(require("readline"));
|
|
42
|
+
const os_1 = __importDefault(require("os"));
|
|
41
43
|
const path_1 = __importDefault(require("path"));
|
|
42
|
-
const
|
|
43
|
-
const constants_1 = require("../static/constants");
|
|
44
|
+
const Instance_1 = require("../typing/Instance");
|
|
44
45
|
const common_1 = __importDefault(require("./common"));
|
|
46
|
+
const emulatorutil_1 = require("../emulatorutil");
|
|
45
47
|
class GoldfishInstance extends common_1.default {
|
|
46
48
|
constructor(params) {
|
|
47
49
|
super(params);
|
|
48
|
-
this.
|
|
50
|
+
this.appDir = '/data/app';
|
|
51
|
+
this.emulatorStartedFlag = '(NSH)';
|
|
49
52
|
}
|
|
50
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* 1. 启动模拟器
|
|
55
|
+
* 2. 启动成功后,adb连接模拟器
|
|
56
|
+
* 3. 连接成功后,在模拟器中启动快应用
|
|
57
|
+
*/
|
|
51
58
|
start(options) {
|
|
52
59
|
return __awaiter(this, void 0, void 0, function* () {
|
|
53
60
|
this.startOptions = options;
|
|
61
|
+
this.sn = `emulator-${this.startOptions.adbPort}`;
|
|
54
62
|
// 启动模拟器
|
|
55
63
|
yield this.startGoldfish(options);
|
|
56
|
-
const connected = yield this.
|
|
64
|
+
const connected = yield this.isConnected();
|
|
57
65
|
if (connected) {
|
|
58
66
|
ColorConsole_1.default.log('### Emulator ### Goldfish emulator connected successfully');
|
|
59
67
|
if (this.isFirstStart && this.startOptions.serverPort) {
|
|
60
68
|
yield this.createWebsockeServer();
|
|
61
69
|
}
|
|
62
|
-
|
|
63
|
-
const buildedFilesPath = this.isRpk ? this.projectPath : path_1.default.resolve(this.projectPath, './build');
|
|
64
|
-
yield this.pushRpk(buildedFilesPath);
|
|
70
|
+
yield this.pushAndInstall();
|
|
65
71
|
// 在模拟器中启动快应用
|
|
66
72
|
this.startupQuickApp(options);
|
|
67
73
|
this.isFirstStart = false;
|
|
68
74
|
}
|
|
69
75
|
else {
|
|
70
|
-
|
|
76
|
+
const msg = '### Emulator ### Failed to connect emulator, please check whether the adb is normal';
|
|
77
|
+
ColorConsole_1.default.throw(msg);
|
|
78
|
+
throw new Error(msg);
|
|
71
79
|
}
|
|
72
80
|
});
|
|
73
81
|
}
|
|
74
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* 在模拟器中启动快应用
|
|
84
|
+
* 通过vapp命令启动,调试时需额外配置--jsdebugger参数
|
|
85
|
+
* @param options
|
|
86
|
+
*/
|
|
75
87
|
startupQuickApp(options) {
|
|
76
88
|
return __awaiter(this, void 0, void 0, function* () {
|
|
77
89
|
try {
|
|
78
90
|
const { package: packageName } = this.projectInfo;
|
|
79
|
-
let vappCmd = `adb -s
|
|
91
|
+
let vappCmd = `adb -s ${this.sn} shell vapp app/${packageName} &`;
|
|
80
92
|
if (options.devtool) {
|
|
81
|
-
vappCmd = `adb -s
|
|
93
|
+
vappCmd = `adb -s ${this.sn} shell vapp --jsdebugger=10.0.2.15:101 app/${packageName} &`;
|
|
82
94
|
}
|
|
83
95
|
ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${vappCmd}`);
|
|
84
96
|
// vapp进程会一直pending,不会退出。这里必须加stdio: 'ignore',否则快应用无法运行成功
|
|
@@ -89,75 +101,42 @@ class GoldfishInstance extends common_1.default {
|
|
|
89
101
|
}
|
|
90
102
|
});
|
|
91
103
|
}
|
|
92
|
-
/**
|
|
104
|
+
/**
|
|
105
|
+
* 启动模拟器
|
|
106
|
+
* 1. 通过options生成模拟器的启动命令
|
|
107
|
+
* 2. 执行启动命令
|
|
108
|
+
* 3. 判断模拟器是否启动成功
|
|
109
|
+
* 3.1 若disableNSH=true,输出流中匹配到(NSH),认为模拟器启动成功了
|
|
110
|
+
* 3.2 若disableNSH=false,认为2s过后模拟器启动成功了
|
|
111
|
+
* @param options
|
|
112
|
+
* @returns
|
|
113
|
+
*/
|
|
93
114
|
startGoldfish(options) {
|
|
94
|
-
var _a;
|
|
95
115
|
return __awaiter(this, void 0, void 0, function* () {
|
|
96
|
-
const {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
this.adbPort = yield portfinder_1.default.getPortPromise({ port: this.adbPort });
|
|
102
|
-
ColorConsole_1.default.log(`### Emulator ### adb port: ${this.adbPort}`);
|
|
103
|
-
if (!avdImagePath) {
|
|
104
|
-
return ColorConsole_1.default.throw(`### Emulator ### Unable to find vela image via avd`);
|
|
105
|
-
}
|
|
106
|
-
const nuttxBinPath = path_1.default.resolve(avdImagePath, 'nuttx');
|
|
107
|
-
ColorConsole_1.default.log(`### Emulator ### nuttx path: ${nuttxBinPath}`);
|
|
108
|
-
// 端口映射
|
|
109
|
-
let portMappingStr = `-network-user-mode-options hostfwd=tcp:127.0.0.1:${this.adbPort}-10.0.2.15:5555`;
|
|
110
|
-
if (devtool) {
|
|
111
|
-
portMappingStr += `,hostfwd=tcp:127.0.0.1:${this.debugPort}-10.0.2.15:101`;
|
|
112
|
-
}
|
|
113
|
-
// 文件系统配置,第一次使用fatfs镜像挂载,后续使用adb push更新应用
|
|
114
|
-
const systemImageBin = path_1.default.resolve(this.sdkHome, 'tools/image/system.img');
|
|
115
|
-
const dataImageBin = path_1.default.resolve(this.sdkHome, 'tools/image/data.img');
|
|
116
|
-
const imageMountStr = `-drive index=0,id=system,if=none,format=raw,file=${systemImageBin} \
|
|
117
|
-
-device virtio-blk-device,bus=virtio-mmio-bus.0,drive=system \
|
|
118
|
-
-drive index=1,id=userdata,if=none,format=raw,file=${dataImageBin} \
|
|
119
|
-
-device virtio-blk-device,bus=virtio-mmio-bus.1,drive=userdata \
|
|
120
|
-
-device virtio-snd,bus=virtio-mmio-bus.2 -allow-host-audio -semihosting`;
|
|
121
|
-
// vnc配置
|
|
122
|
-
let windowStr = '';
|
|
123
|
-
let vncStr = '';
|
|
124
|
-
if ((_a = this.startOptions) === null || _a === void 0 ? void 0 : _a.vncPort) {
|
|
125
|
-
windowStr = '-no-window';
|
|
126
|
-
const portSuffix = this.startOptions.vncPort - constants_1.defaultVncPort;
|
|
127
|
-
vncStr = `-vnc :${portSuffix}`;
|
|
128
|
-
}
|
|
129
|
-
const stdioType = options.disableNSH ? 'pipe' : 'inherit';
|
|
130
|
-
// 启动goldfish的命令和参数
|
|
131
|
-
const cmd = `${emulatorBin} -nuttx -avd ${avdName} -avd-arch ${avdArch} -show-kernel -kernel ${nuttxBinPath} ${portMappingStr} ${windowStr} -qemu ${vncStr} ${imageMountStr}`;
|
|
116
|
+
const { origin = Instance_1.IStartOrigin.Terminal } = options;
|
|
117
|
+
// 启动模拟器的命令和参数
|
|
118
|
+
const cmd = emulatorutil_1.EmulatorCmd.createDevCmd(options, this.sdkHome, this.avdHome);
|
|
119
|
+
if (!cmd)
|
|
120
|
+
return;
|
|
132
121
|
const spawnArgs = cmd.split(' ');
|
|
133
122
|
const spawnBin = spawnArgs.shift();
|
|
134
|
-
ColorConsole_1.default.log(`### Emulator ### Start CMD: ${cmd}`);
|
|
123
|
+
ColorConsole_1.default.log(`### Emulator ### Start CMD dev: ${cmd}`);
|
|
135
124
|
return new Promise((resolve) => {
|
|
136
125
|
var _a, _b;
|
|
137
|
-
this.goldfishProcess = (0, child_process_1.spawn)(spawnBin, spawnArgs, { stdio:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
(_b = this.goldfishProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
|
|
152
|
-
const stderrCb = options.stderrCallback || console.log;
|
|
153
|
-
stderrCb(data.toString());
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
setTimeout(() => {
|
|
158
|
-
ColorConsole_1.default.log(`### Emulator ### Goldfish emulator starts successfully`);
|
|
159
|
-
resolve();
|
|
160
|
-
}, 2000);
|
|
126
|
+
this.goldfishProcess = (0, child_process_1.spawn)(spawnBin, spawnArgs, { stdio: 'pipe', shell: true });
|
|
127
|
+
// 利用 readline 接口可解决子进程日志换行的问题
|
|
128
|
+
this.stdoutReadline = readline_1.default.createInterface({
|
|
129
|
+
input: this.goldfishProcess.stdout
|
|
130
|
+
});
|
|
131
|
+
this.stderrReadline = readline_1.default.createInterface({
|
|
132
|
+
input: this.goldfishProcess.stderr
|
|
133
|
+
});
|
|
134
|
+
(_a = this.stderrReadline) === null || _a === void 0 ? void 0 : _a.on('line', (data) => {
|
|
135
|
+
const stderrCb = options.stderrCallback || console.log;
|
|
136
|
+
stderrCb(data.toString());
|
|
137
|
+
});
|
|
138
|
+
if (origin === Instance_1.IStartOrigin.Terminal) {
|
|
139
|
+
process.stdin.pipe(this.goldfishProcess.stdin);
|
|
161
140
|
}
|
|
162
141
|
this.goldfishProcess.on('exit', (code) => {
|
|
163
142
|
ColorConsole_1.default.error(`### Emulator ### Goldfish emulator exited with code ${code}`);
|
|
@@ -165,19 +144,94 @@ class GoldfishInstance extends common_1.default {
|
|
|
165
144
|
options.exitCallback(code);
|
|
166
145
|
}
|
|
167
146
|
});
|
|
147
|
+
(_b = this.stdoutReadline) === null || _b === void 0 ? void 0 : _b.on('line', (data) => {
|
|
148
|
+
const msg = data.toString();
|
|
149
|
+
const stdoutCb = options.stdoutCallback || console.log;
|
|
150
|
+
stdoutCb(msg);
|
|
151
|
+
if (emulatorutil_1.EmulatorLog.devIsStart(msg)) {
|
|
152
|
+
ColorConsole_1.default.log(`### Emulator ### Goldfish emulator starts successfully`);
|
|
153
|
+
resolve();
|
|
154
|
+
}
|
|
155
|
+
});
|
|
168
156
|
});
|
|
169
157
|
});
|
|
170
158
|
}
|
|
171
|
-
/**
|
|
172
|
-
|
|
159
|
+
/**
|
|
160
|
+
* 将目录通过adb push到模拟器中
|
|
161
|
+
*/
|
|
162
|
+
pushAndInstall(rpkPath) {
|
|
173
163
|
return __awaiter(this, void 0, void 0, function* () {
|
|
174
|
-
const sn =
|
|
164
|
+
const sn = this.sn;
|
|
175
165
|
const { package: appPackageName } = this.projectInfo;
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
166
|
+
const sourceRoot = rpkPath || this.isRpk ? this.projectPath : path_1.default.join(this.projectPath, './build');
|
|
167
|
+
// 获取最后一层目录,比如build
|
|
168
|
+
const basename = path_1.default.basename(sourceRoot);
|
|
169
|
+
if (os_1.default.platform() === 'win32' || this.projectPath.indexOf(' ') > 0) {
|
|
170
|
+
// windows系统或者项目路径有空格:1. adb push目录;2. 在模拟器中使用mv命令重命名
|
|
171
|
+
// 注意 sourceRoot 中可能会有空格
|
|
172
|
+
const pushCmd = `adb -s ${sn} push "${sourceRoot}" ${this.appDir}`;
|
|
173
|
+
ColorConsole_1.default.info(`### Emulator ### push CMD: ${pushCmd}`);
|
|
174
|
+
yield adbMiwt.execAdbCmdAsync(pushCmd);
|
|
175
|
+
// 在 windows 上 mv 命令不能更新原有的文件,需要先删除
|
|
176
|
+
const rmCmd = `adb -s ${sn} shell rm -r ${this.appDir}/${appPackageName}`;
|
|
177
|
+
ColorConsole_1.default.info(`### Emulator ### mv CMD: ${rmCmd}`);
|
|
178
|
+
yield adbMiwt.execAdbCmdAsync(rmCmd);
|
|
179
|
+
const mvCmd = `adb -s ${sn} shell mv ${this.appDir}/${basename} ${this.appDir}/${appPackageName}`;
|
|
180
|
+
ColorConsole_1.default.info(`### Emulator ### mv CMD: ${mvCmd}`);
|
|
181
|
+
yield adbMiwt.execAdbCmdAsync(mvCmd);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
// 支持通配符处理: 1. 模拟器中mkdir创建目录 2. adb push ./XXXXX/* /XXX
|
|
185
|
+
const sourcePath = path_1.default.join(sourceRoot, './*');
|
|
186
|
+
const mkdirCmd = `adb -s ${sn} shell mkdir ${this.appDir}/${appPackageName}`;
|
|
187
|
+
ColorConsole_1.default.log(`### Emulator ### mkdir CMD: ${mkdirCmd}`);
|
|
188
|
+
yield adbMiwt.execAdbCmdAsync(mkdirCmd);
|
|
189
|
+
const pushCmd = `adb -s ${sn} push ${sourcePath} ${this.appDir}/${appPackageName}`;
|
|
190
|
+
ColorConsole_1.default.log(`### Emulator ### pushCmd CMD: ${pushCmd}`);
|
|
191
|
+
yield adbMiwt.execAdbCmdAsync(pushCmd);
|
|
192
|
+
}
|
|
193
|
+
ColorConsole_1.default.info(`### Emulator push to ${this.appDir}/${appPackageName} successfully`);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
/** 通过命令行启动时,在watich模式下监听websocket消息,
|
|
197
|
+
* 在文件发生变动时,重新启动应用
|
|
198
|
+
*/
|
|
199
|
+
restart() {
|
|
200
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
201
|
+
this.pushAndReloadApp();
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* 重新推送,然后重启应用
|
|
206
|
+
*/
|
|
207
|
+
pushAndReloadApp() {
|
|
208
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
209
|
+
try {
|
|
210
|
+
yield this.pushAndInstall();
|
|
211
|
+
yield this.reboot();
|
|
212
|
+
this.reloadApp();
|
|
213
|
+
}
|
|
214
|
+
catch (e) {
|
|
215
|
+
ColorConsole_1.default.error(`${e}`);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* 重启应用
|
|
221
|
+
*/
|
|
222
|
+
reloadApp() {
|
|
223
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
224
|
+
const { package: appPackageName } = this.projectInfo;
|
|
225
|
+
this.startupQuickApp(this.startOptions);
|
|
226
|
+
ColorConsole_1.default.info(`### Emulator start ${appPackageName} successfully`);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
reboot() {
|
|
230
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
231
|
+
const rebootCmd = `adb -s ${this.sn} shell reboot`;
|
|
232
|
+
ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${rebootCmd}`);
|
|
233
|
+
yield adbMiwt.execAdbCmdAsync(rebootCmd);
|
|
234
|
+
return this.isConnected();
|
|
181
235
|
});
|
|
182
236
|
}
|
|
183
237
|
}
|