@aiot-toolkit/emulator 2.0.2-beta.4 → 2.0.2-beta.6

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 CHANGED
@@ -1,2 +1,15 @@
1
+ ## emulator
1
2
 
2
- emulator tool
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 | 工具函数 |
@@ -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 = [];
@@ -10,33 +10,30 @@ declare class CommonInstance {
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
+ sn?: string;
21
20
  constructor(params: INewGoldfishInstanceParams);
22
21
  /** 获取模拟器二进制文件所在位置 */
23
22
  getEmulatorBinPath(): string;
24
23
  /** 在goldfish模拟器中运行快应用 */
25
24
  start(options: IStartOptions): Promise<void>;
26
- /** 在goldfish中启动快应用 */
27
- startupQuickApp(options: IStartOptions): void;
28
- /** 启动goldfish模拟器 */
29
- startGoldfish(options: IStartOptions): Promise<void>;
30
- /** 通过adb连接模拟器 */
31
- connectGoldfish(): Promise<boolean>;
32
- /** 将打包后的文件推到挂载的快应用目录 */
33
- pushRpk(sourceRoot: string): Promise<void>;
25
+ /**
26
+ * 通过adb连接模拟器。
27
+ * 时间限制为10s,超时则表示连接失败
28
+ * @returns
29
+ */
30
+ connectGoldfish(): Promise<unknown>;
34
31
  /** 杀死进程 */
35
32
  killProcess(currProcess?: ChildProcess): void;
36
33
  /** 停止模拟器并释放相关资源 */
37
34
  stop(): Promise<void>;
38
35
  /** 重启模拟器 */
39
- restart(): void;
36
+ restart(): Promise<void>;
40
37
  /** 创建server */
41
38
  createWebsockeServer(): Promise<void>;
42
39
  }
@@ -41,7 +41,6 @@ const adbMiwt = __importStar(require("@miwt/adb"));
41
41
  const child_process_1 = require("child_process");
42
42
  const os_1 = __importDefault(require("os"));
43
43
  const path_1 = __importDefault(require("path"));
44
- const portfinder_1 = __importDefault(require("portfinder"));
45
44
  const ws_1 = require("ws");
46
45
  const avd_1 = __importDefault(require("../avd"));
47
46
  const constants_1 = require("../static/constants");
@@ -51,8 +50,6 @@ const utils_1 = require("../utils");
51
50
  */
52
51
  class CommonInstance {
53
52
  constructor(params) {
54
- this.adbPort = 5555;
55
- this.debugPort = 10055;
56
53
  this.isFirstStart = true;
57
54
  this.projectPath = params.projectPath;
58
55
  this.sdkHome = params.sdkHome || constants_1.defaultSDKHome;
@@ -74,154 +71,44 @@ class CommonInstance {
74
71
  }
75
72
  /** 在goldfish模拟器中运行快应用 */
76
73
  start(options) {
77
- return __awaiter(this, void 0, void 0, function* () {
78
- this.startOptions = options;
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
- }
97
- });
98
- }
99
- /** 在goldfish中启动快应用 */
100
- startupQuickApp(options) {
101
- try {
102
- let vappCmd = `adb -s 127.0.0.1:${this.adbPort} shell vapp app/${this.projectInfo.package} &`;
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;
117
- return __awaiter(this, void 0, void 0, function* () {
118
- const { avdName, devtool } = options;
119
- const emulatorBin = this.getEmulatorBinPath();
120
- ColorConsole_1.default.log(`### Emulator ### emulator path: ${emulatorBin}`);
121
- const avdInfo = this.velaAvdCls.getVelaAvdInfo(avdName);
122
- const { avdArch, avdImagePath } = avdInfo;
123
- this.adbPort = yield portfinder_1.default.getPortPromise({ port: this.adbPort });
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
- });
183
- });
74
+ return __awaiter(this, void 0, void 0, function* () { });
184
75
  }
185
- /** 通过adb连接模拟器 */
76
+ /**
77
+ * 通过adb连接模拟器。
78
+ * 时间限制为10s,超时则表示连接失败
79
+ * @returns
80
+ */
186
81
  connectGoldfish() {
187
82
  return __awaiter(this, void 0, void 0, function* () {
188
- let adbConnected = false;
83
+ let adbConnected = false; // adb是否连接成功
84
+ let enableLoop = true; // 是否允许循环,用于终止adb的连接
189
85
  const connectFn = () => __awaiter(this, void 0, void 0, function* () {
190
- const sn = `127.0.0.1:${this.adbPort}`;
191
- while (!adbConnected) {
86
+ while (enableLoop && !adbConnected) {
192
87
  const adbKillCmd = `adb kill-server`;
193
88
  ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${adbKillCmd}`);
194
89
  adbMiwt.execAdbCmdSync(adbKillCmd);
195
- const adbConnectCmd = `adb connect ${sn}`;
90
+ const adbConnectCmd = `adb connect ${this.sn}`;
196
91
  ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${adbConnectCmd}`);
197
92
  const str = adbMiwt.execAdbCmdSync(adbConnectCmd);
198
93
  ColorConsole_1.default.log(`### Emulator ### ${str}`);
199
94
  const devices = yield adbMiwt.getAdbDevices();
200
95
  ColorConsole_1.default.log(`### Emulator ### adb devices: ${JSON.stringify(devices)}`);
201
96
  adbConnected =
202
- devices.filter((item) => item.sn === sn && item.status === 'device').length > 0;
97
+ devices.filter((item) => item.sn === this.sn && item.status === 'device').length > 0;
203
98
  }
204
- Promise.resolve(adbConnected);
99
+ return Promise.resolve(adbConnected);
205
100
  });
206
- yield Promise.race([
101
+ const res = yield Promise.race([
207
102
  connectFn(),
208
103
  new Promise((resolve) => {
209
- setTimeout(() => resolve(false), 600 * 1000);
104
+ setTimeout(() => {
105
+ enableLoop = false;
106
+ // 超时则认为adb没有连接成功
107
+ resolve(false);
108
+ }, 10 * 1000);
210
109
  })
211
110
  ]);
212
- return adbConnected;
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`);
111
+ return res;
225
112
  });
226
113
  }
227
114
  /** 杀死进程 */
@@ -262,8 +149,10 @@ class CommonInstance {
262
149
  }
263
150
  /** 重启模拟器 */
264
151
  restart() {
265
- this.stop();
266
- this.start(this.startOptions);
152
+ return __awaiter(this, void 0, void 0, function* () {
153
+ yield this.stop();
154
+ this.start(this.startOptions);
155
+ });
267
156
  }
268
157
  /** 创建server */
269
158
  createWebsockeServer() {
@@ -3,13 +3,33 @@ import CommonInstance from './common';
3
3
  declare class GoldfishInstance extends CommonInstance {
4
4
  private appRunDir;
5
5
  constructor(params: INewGoldfishInstanceParams);
6
- /** 在goldfish模拟器中运行快应用 */
6
+ /**
7
+ * 1. 启动模拟器
8
+ * 2. 启动成功后,adb连接模拟器
9
+ * 3. 连接成功后,在模拟器中启动快应用
10
+ */
7
11
  start(options: IStartOptions): Promise<void>;
8
- /** 在goldfish中启动快应用 */
12
+ /**
13
+ * 在模拟器中启动快应用
14
+ * 通过vapp命令启动,调试时需额外配置--jsdebugger参数
15
+ * @param options
16
+ */
9
17
  startupQuickApp(options: IStartOptions): Promise<void>;
10
- /** 启动goldfish模拟器 */
18
+ /**
19
+ * 启动模拟器
20
+ * 1. 通过options生成模拟器的启动命令
21
+ * 2. 执行启动命令
22
+ * 3. 判断模拟器是否启动成功
23
+ * 3.1 若disableNSH=true,输出流中匹配到(NSH),认为模拟器启动成功了
24
+ * 3.2 若disableNSH=false,认为2s过后模拟器启动成功了
25
+ * @param options
26
+ * @returns
27
+ */
11
28
  startGoldfish(options: IStartOptions): Promise<void>;
12
- /** 将打包后的文件推到挂载的快应用目录 */
29
+ /**
30
+ * 将目录通过adb push到模拟器中
31
+ * @param sourceRoot
32
+ */
13
33
  pushRpk(sourceRoot: string): Promise<void>;
14
34
  }
15
35
  export default GoldfishInstance;
@@ -39,18 +39,23 @@ const ColorConsole_1 = __importDefault(require("@aiot-toolkit/shared-utils/lib/C
39
39
  const adbMiwt = __importStar(require("@miwt/adb"));
40
40
  const child_process_1 = require("child_process");
41
41
  const path_1 = __importDefault(require("path"));
42
- const portfinder_1 = __importDefault(require("portfinder"));
43
42
  const constants_1 = require("../static/constants");
44
43
  const common_1 = __importDefault(require("./common"));
44
+ const os_1 = __importDefault(require("os"));
45
45
  class GoldfishInstance extends common_1.default {
46
46
  constructor(params) {
47
47
  super(params);
48
48
  this.appRunDir = '/data/app';
49
49
  }
50
- /** 在goldfish模拟器中运行快应用 */
50
+ /**
51
+ * 1. 启动模拟器
52
+ * 2. 启动成功后,adb连接模拟器
53
+ * 3. 连接成功后,在模拟器中启动快应用
54
+ */
51
55
  start(options) {
52
56
  return __awaiter(this, void 0, void 0, function* () {
53
57
  this.startOptions = options;
58
+ this.sn = `127.0.0.1:${this.startOptions.adbPort}`;
54
59
  // 启动模拟器
55
60
  yield this.startGoldfish(options);
56
61
  const connected = yield this.connectGoldfish();
@@ -71,14 +76,18 @@ class GoldfishInstance extends common_1.default {
71
76
  }
72
77
  });
73
78
  }
74
- /** 在goldfish中启动快应用 */
79
+ /**
80
+ * 在模拟器中启动快应用
81
+ * 通过vapp命令启动,调试时需额外配置--jsdebugger参数
82
+ * @param options
83
+ */
75
84
  startupQuickApp(options) {
76
85
  return __awaiter(this, void 0, void 0, function* () {
77
86
  try {
78
87
  const { package: packageName } = this.projectInfo;
79
- let vappCmd = `adb -s 127.0.0.1:${this.adbPort} shell vapp app/${packageName} &`;
88
+ let vappCmd = `adb -s 127.0.0.1:${options.adbPort} shell vapp app/${packageName} &`;
80
89
  if (options.devtool) {
81
- vappCmd = `adb -s 127.0.0.1:${this.adbPort} shell vapp --jsdebugger=10.0.2.15:101 app/${packageName} &`;
90
+ vappCmd = `adb -s 127.0.0.1:${options.adbPort} shell vapp --jsdebugger=10.0.2.15:101 app/${packageName} &`;
82
91
  }
83
92
  ColorConsole_1.default.log(`### Emulator ### Excuting adb cmd: ${vappCmd}`);
84
93
  // vapp进程会一直pending,不会退出。这里必须加stdio: 'ignore',否则快应用无法运行成功
@@ -89,7 +98,16 @@ class GoldfishInstance extends common_1.default {
89
98
  }
90
99
  });
91
100
  }
92
- /** 启动goldfish模拟器 */
101
+ /**
102
+ * 启动模拟器
103
+ * 1. 通过options生成模拟器的启动命令
104
+ * 2. 执行启动命令
105
+ * 3. 判断模拟器是否启动成功
106
+ * 3.1 若disableNSH=true,输出流中匹配到(NSH),认为模拟器启动成功了
107
+ * 3.2 若disableNSH=false,认为2s过后模拟器启动成功了
108
+ * @param options
109
+ * @returns
110
+ */
93
111
  startGoldfish(options) {
94
112
  var _a;
95
113
  return __awaiter(this, void 0, void 0, function* () {
@@ -98,21 +116,20 @@ class GoldfishInstance extends common_1.default {
98
116
  ColorConsole_1.default.log(`### Emulator ### emulator path: ${emulatorBin}`);
99
117
  const avdInfo = this.velaAvdCls.getVelaAvdInfo(avdName);
100
118
  const { avdArch, avdImagePath } = avdInfo;
101
- this.adbPort = yield portfinder_1.default.getPortPromise({ port: this.adbPort });
102
- ColorConsole_1.default.log(`### Emulator ### adb port: ${this.adbPort}`);
119
+ ColorConsole_1.default.log(`### Emulator ### adb port: ${options.adbPort}`);
103
120
  if (!avdImagePath) {
104
121
  return ColorConsole_1.default.throw(`### Emulator ### Unable to find vela image via avd`);
105
122
  }
106
123
  const nuttxBinPath = path_1.default.resolve(avdImagePath, 'nuttx');
107
124
  ColorConsole_1.default.log(`### Emulator ### nuttx path: ${nuttxBinPath}`);
108
125
  // 端口映射
109
- let portMappingStr = `-network-user-mode-options hostfwd=tcp:127.0.0.1:${this.adbPort}-10.0.2.15:5555`;
126
+ let portMappingStr = `-network-user-mode-options hostfwd=tcp:127.0.0.1:${options.adbPort}-10.0.2.15:5555`;
110
127
  if (devtool) {
111
- portMappingStr += `,hostfwd=tcp:127.0.0.1:${this.debugPort}-10.0.2.15:101`;
128
+ portMappingStr += `,hostfwd=tcp:127.0.0.1:${options.debugPort}-10.0.2.15:101`;
112
129
  }
113
130
  // 文件系统配置,第一次使用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');
131
+ const systemImageBin = path_1.default.resolve(avdImagePath, 'system.img');
132
+ const dataImageBin = path_1.default.resolve(avdImagePath, 'data.img');
116
133
  const imageMountStr = `-drive index=0,id=system,if=none,format=raw,file=${systemImageBin} \
117
134
  -device virtio-blk-device,bus=virtio-mmio-bus.0,drive=system \
118
135
  -drive index=1,id=userdata,if=none,format=raw,file=${dataImageBin} \
@@ -135,23 +152,26 @@ class GoldfishInstance extends common_1.default {
135
152
  return new Promise((resolve) => {
136
153
  var _a, _b;
137
154
  this.goldfishProcess = (0, child_process_1.spawn)(spawnBin, spawnArgs, { stdio: stdioType, shell: true });
155
+ (_a = this.goldfishProcess.stderr) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
156
+ const stderrCb = options.stderrCallback || console.log;
157
+ stderrCb(data.toString());
158
+ });
159
+ this.goldfishProcess.on('exit', (code) => {
160
+ ColorConsole_1.default.error(`### Emulator ### Goldfish emulator exited with code ${code}`);
161
+ if (options.exitCallback) {
162
+ options.exitCallback(code);
163
+ }
164
+ });
138
165
  if (options.disableNSH) {
139
- (_a = this.goldfishProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
140
- if (options.stdoutCallback) {
141
- options.stdoutCallback(data);
142
- }
143
- else {
144
- console.log(data.toString());
145
- }
166
+ (_b = this.goldfishProcess.stdout) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
167
+ const msg = data.toString();
168
+ const stdoutCb = options.stdoutCallback || console.log;
169
+ stdoutCb(msg);
146
170
  if (data.toString().includes('(NSH)')) {
147
171
  ColorConsole_1.default.log(`### Emulator ### Goldfish emulator starts successfully`);
148
172
  resolve();
149
173
  }
150
174
  });
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
175
  }
156
176
  else {
157
177
  setTimeout(() => {
@@ -159,25 +179,31 @@ class GoldfishInstance extends common_1.default {
159
179
  resolve();
160
180
  }, 2000);
161
181
  }
162
- this.goldfishProcess.on('exit', (code) => {
163
- ColorConsole_1.default.error(`### Emulator ### Goldfish emulator exited with code ${code}`);
164
- if (options.exitCallback) {
165
- options.exitCallback(code);
166
- }
167
- });
168
182
  });
169
183
  });
170
184
  }
171
- /** 将打包后的文件推到挂载的快应用目录 */
185
+ /**
186
+ * 将目录通过adb push到模拟器中
187
+ * @param sourceRoot
188
+ */
172
189
  pushRpk(sourceRoot) {
173
190
  return __awaiter(this, void 0, void 0, function* () {
174
- const sn = `127.0.0.1:${this.adbPort}`;
191
+ const sn = `127.0.0.1:${this.startOptions.adbPort}`;
175
192
  const { package: appPackageName } = this.projectInfo;
176
- const sourcePath = path_1.default.resolve(sourceRoot, './*');
177
- ColorConsole_1.default.log(`### Emulator ### Pushing ${appPackageName} to ${this.appRunDir}`);
178
- yield adbMiwt.execAdbCmdAsync(`adb -s ${sn} shell mkdir ${this.appRunDir}/${appPackageName}`);
179
- yield adbMiwt.execAdbCmdAsync(`adb -s ${sn} push ${sourcePath} ${this.appRunDir}/${appPackageName}`);
180
- ColorConsole_1.default.log(`### Emulator ### Push ${appPackageName} to ${this.appRunDir} successfully`);
193
+ // 获取最后一层目录,比如build
194
+ const basename = path_1.default.basename(sourceRoot);
195
+ if (os_1.default.platform() === 'win32' || this.projectPath.indexOf(' ') > 0) {
196
+ // windows系统或者项目路径有空格:1. adb push目录;2. 在模拟器中使用mv命令重命名
197
+ yield adbMiwt.execAdbCmdAsync(`adb -s ${sn} push ${sourceRoot} ${this.appRunDir}`);
198
+ yield adbMiwt.execAdbCmdAsync(`adb -s ${sn} shell mv ${this.appRunDir}/${basename} ${this.appRunDir}/${appPackageName}`);
199
+ }
200
+ else {
201
+ // 支持通配符处理: 1. 模拟器中mkdir创建目录 2. adb push ./XXXXX/* /XXX
202
+ const sourcePath = path_1.default.resolve(sourceRoot, './*');
203
+ yield adbMiwt.execAdbCmdAsync(`adb -s ${sn} shell mkdir ${this.appRunDir}/${appPackageName}`);
204
+ yield adbMiwt.execAdbCmdAsync(`adb -s ${sn} push "${sourcePath}" ${this.appRunDir}/${appPackageName}`);
205
+ }
206
+ ColorConsole_1.default.info(`### Emulator push to ${this.appRunDir}/${appPackageName} successfully`);
181
207
  });
182
208
  }
183
209
  }
@@ -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 };
@@ -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);
@@ -2,7 +2,7 @@ import { INewGoldfishInstanceParams, IStartOptions } from '../typing/Instance';
2
2
  import CommonInstance from './common';
3
3
  /**
4
4
  * MiwearInstance
5
- * 针对 vela4.0 的镜像
5
+ * 针对 Vela正式版(4.0)的镜像
6
6
  */
7
7
  declare class MiwearInstance extends CommonInstance {
8
8
  private params;
@@ -10,20 +10,71 @@ declare class MiwearInstance extends CommonInstance {
10
10
  private debugSocket?;
11
11
  private reconnectCount;
12
12
  constructor(params: INewGoldfishInstanceParams);
13
- /** 在goldfish模拟器中运行快应用 */
13
+ /**
14
+ * 1. 启动模拟器
15
+ * 2. 启动成功后,adb连接模拟器
16
+ * 3. 连接成功后,在模拟器中启动快应用
17
+ */
14
18
  start(options: IStartOptions): Promise<void>;
15
- /** 启动goldfish模拟器 */
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
- /** 通过adb连接模拟器 */
18
- connectGoldfish(): Promise<boolean>;
19
- /** 在goldfish中启动快应用 */
30
+ /**
31
+ * 通过adb连接模拟器。
32
+ * 时间限制为2分钟,超时则表示连接失败
33
+ * 注意:两次connect之间不能等待,否则在disableNSH=true且有vncPort的模式下,模拟器启动成功了,但是vnc可能还没连接上
34
+ * 时间限制不能太短,否则模拟器里面的adbd进程还没初始化成功
35
+ * @returns
36
+ */
37
+ connectGoldfish(): Promise<unknown>;
38
+ /**
39
+ * 在模拟器中启动快应用
40
+ * 1. 检查release目录是否有打包好的rpk
41
+ * 2. 检查当前是否为调试模式.
42
+ * 若是,将debugger_ip.cfg推到模拟器的/data/目录下
43
+ * 若否,则要删除模拟器中已有的data/debugger_ip.cfg
44
+ * 3. 调用installRpkToAppList将当前快应用安装到模拟器的应用列表中
45
+ * @param options
46
+ */
20
47
  startupQuickApp(options: IStartOptions): Promise<void>;
21
- /** 将快应用安装到应用列表 */
48
+ /**
49
+ * 将快应用安装到模拟器的应用列表
50
+ * 1. 使用adb push将打包好的rpk推到模拟器的/data/quickapp/app/
51
+ * 2. nsh中调用pm install命令安装应用
52
+ * @param rpkPath rpk的绝对目录
53
+ * @param targetDir 要将rpk放到模拟器的哪个目录下
54
+ */
22
55
  installRpkToAppList(rpkPath: string, targetDir: string): Promise<void>;
56
+ /** 连接模拟器中的调试服务,创建debugSocket
57
+ * 主要用于热更新时,通过debugSocket通知调试服务更新页面
58
+ * 设置了重连机制,会重复连接8次
59
+ */
23
60
  initDebugSocket(): Promise<void>;
24
- /** 通知模拟器更新 */
61
+ /** 通知模拟器更新
62
+ * 1. 确保已经成功连接模拟器中的调试服务
63
+ * 2. 使用adb push将build文件下的内容推到/data/quickapp/app/${packageName}
64
+ * 3. 发送Page.reload命令给调试服务,通知更新
65
+ */
25
66
  handleUpdate(): Promise<void>;
26
- /** 创建server */
67
+ /** 热更新时push目录 */
68
+ pushBuild(): Promise<void>;
69
+ /** 在模拟器中重启快应用(基于am命令,需要保证镜像中已经有am功能)
70
+ * 1. 使用adb push将build文件下的内容推到/data/quickapp/app/${packageName}
71
+ * 2. nsh中执行am stop命令退出快应用
72
+ * 3. nsh中执行am start命令启动快应用
73
+ */
74
+ reloadApp(): Promise<void>;
75
+ /**
76
+ * 创建server端,监听打包过程中client端发来的消息
77
+ */
27
78
  createWebsockeServer(): Promise<void>;
28
79
  }
29
80
  export default MiwearInstance;