From f0480a19086450a4637b3f5c17763d79f765a7bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AE=80=E9=9D=99=E5=87=A1?= <30424139+wtto00@users.noreply.github.com> Date: Sun, 24 Sep 2023 15:04:52 +0800 Subject: [PATCH 1/2] chore: ci --- .github/workflows/test-publish.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/test-publish.yml b/.github/workflows/test-publish.yml index 0d275e3..2d1fabe 100644 --- a/.github/workflows/test-publish.yml +++ b/.github/workflows/test-publish.yml @@ -2,8 +2,6 @@ name: 'TestPublish' on: pull_request: push: - branches: - - main tags: - 'v*' From 7bd61d172e07a88887745343995aa6513ebdf550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AE=80=E9=9D=99=E5=87=A1?= <30424139+wtto00@users.noreply.github.com> Date: Mon, 25 Sep 2023 01:21:39 +0800 Subject: [PATCH 2/2] fix: type define & log debug & listImages changed --- .eslintrc.json | 5 ++- README-ZH_CN.md | 20 ++++++------ README.md | 20 ++++++------ package.json | 17 ++++------ pnpm-lock.yaml | 18 +++++++++++ rollup.config.mjs | 11 ++----- spec/afterStart.test.ts | 6 ++-- spec/emulatorAVD.test.ts | 6 ++-- spec/start.test.ts | 1 - src/emulator.ts | 70 +++++++++++----------------------------- src/index.ts | 42 +++++++++++++++++++++--- src/util.ts | 26 +++++++++++---- 12 files changed, 132 insertions(+), 110 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 90ec3d5..be4c1f8 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -21,6 +21,9 @@ "quotes": ["error", "single"], "semi": ["error", "always"], "@typescript-eslint/ban-types": "off", - "@typescript-eslint/prefer-nullish-coalescing": "off" + "@typescript-eslint/prefer-nullish-coalescing": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/no-unsafe-member-access": "off" } } diff --git a/README-ZH_CN.md b/README-ZH_CN.md index 549141d..630bcbe 100644 --- a/README-ZH_CN.md +++ b/README-ZH_CN.md @@ -134,14 +134,14 @@ android }); ``` -| field | type | required | default | note | -| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------- | ----------------------------------------------------------------------------------- | -| apiLevel | number | false | - | 平台系统镜像的API级别 - 例如,Android Marshmallow的级别为23,Android 10的级别为29。 | -| target | 'default'
'google_apis'
'playstore'
'android-wear'
'android-wear-cn'
'android-tv'
'google-tv'
'aosp_atd '
'google_atd' | false | 'default' | 系统镜像的目标。 | -| arch | 'x86_64'
'x86'
'arm64-v8a'
'armeabi-v7a' | false | 'x86_64' | 系统镜像的CPU架构 | -| package | string | true | - | 此模拟器的系统镜像的路径(例如 'system-images;android-19;google_apis;x86')。 | -| name | string | false | - | 新模拟器的名称。 | -| force | boolean | false | - | 创建虚拟设备(覆盖现有的模拟器)。 | +| field | type | required | default | note | +| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------------- | ----------------------------------------------------------------------------------- | +| apiLevel | number | false | - | 平台系统镜像的API级别 - 例如,Android Marshmallow的级别为23,Android 10的级别为29。 | +| target | 'default'
'google_apis'
'playstore'
'android-wear'
'android-wear-cn'
'android-tv'
'google-tv'
'aosp_atd '
'google_atd' | false | 'default' | 系统镜像的目标。 | +| arch | 'x86_64'
'x86'
'arm64-v8a'
'armeabi-v7a' | false | 当前系统CPU架构 | 系统镜像的CPU架构 | +| package | string | true | - | 此模拟器的系统镜像的路径(例如 'system-images;android-19;google_apis;x86')。 | +| name | string | false | - | 新模拟器的名称。 | +| force | boolean | false | - | 创建虚拟设备(覆盖现有的模拟器)。 | - 如果你传了`package`,则参数`apiLevel`,`target`,`arch`将被会略。如果你没有传参`package`,则`apiLevel`参数是必须的。 @@ -361,7 +361,7 @@ android .then((res) => { res.forEach((item) => { console.log( - `name: ${item.name}, type: ${item.type}, sdk: ${item.sdk}, vendor: ${item.vendor}, arch: ${item.arch}` + `name: ${item.name}, type: ${item.type}, sdk: ${item.sdk}, target: ${item.target}, arch: ${item.arch}` ); }); }) @@ -380,7 +380,7 @@ android .then((res) => { res.forEach((item) => { console.log( - `name: ${item.name}, type: ${item.type}, sdk: ${item.sdk}, vendor: ${item.vendor}, arch: ${item.arch}` + `name: ${item.name}, type: ${item.type}, sdk: ${item.sdk}, target: ${item.target}, arch: ${item.arch}` ); }); }) diff --git a/README.md b/README.md index ac028e4..aacf92c 100644 --- a/README.md +++ b/README.md @@ -134,14 +134,14 @@ android }); ``` -| field | type | required | default | note | -| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------- | ------------------------------------------------------------------------------------------------ | -| apiLevel | number | false | - | API level of the platform system image - e.g. 23 for Android Marshmallow, 29 for Android 10. | -| target | 'default'
'google_apis'
'playstore'
'android-wear'
'android-wear-cn'
'android-tv'
'google-tv'
'aosp_atd '
'google_atd' | false | 'default' | Target of the system image . | -| arch | 'x86_64'
'x86'
'arm64-v8a'
'armeabi-v7a' | false | 'x86_64' | CPU architecture of the system image | -| package | string | false | - | Package path of the system image for this AVD (e.g. 'system-images;android-19;google_apis;x86'). | -| name | string | false | - | Name of the new AVD. | -| force | boolean | false | - | Forces creation (overwrites an existing AVD) | +| field | type | required | default | note | +| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------- | ------------------------------------------------------------------------------------------------ | +| apiLevel | number | false | - | API level of the platform system image - e.g. 23 for Android Marshmallow, 29 for Android 10. | +| target | 'default'
'google_apis'
'playstore'
'android-wear'
'android-wear-cn'
'android-tv'
'google-tv'
'aosp_atd '
'google_atd' | false | 'default' | Target of the system image . | +| arch | 'x86_64'
'x86'
'arm64-v8a'
'armeabi-v7a' | false | Current system CPU architecture | CPU architecture of the system image | +| package | string | false | - | Package path of the system image for this AVD (e.g. 'system-images;android-19;google_apis;x86'). | +| name | string | false | - | Name of the new AVD. | +| force | boolean | false | - | Forces creation (overwrites an existing AVD) | - If you pass a `package`, the parameters `apiLevel`, `target`, and `arch` will be ignored. If you don't pass a `package`, the `apiLevel` parameter is required. @@ -361,7 +361,7 @@ android .then((res) => { res.forEach((item) => { console.log( - `name: ${item.name}, type: ${item.type}, sdk: ${item.sdk}, vendor: ${item.vendor}, arch: ${item.arch}` + `name: ${item.name}, type: ${item.type}, sdk: ${item.sdk}, target: ${item.target}, arch: ${item.arch}` ); }); }) @@ -380,7 +380,7 @@ android .then((res) => { res.forEach((item) => { console.log( - `name: ${item.name}, type: ${item.type}, sdk: ${item.sdk}, vendor: ${item.vendor}, arch: ${item.arch}` + `name: ${item.name}, type: ${item.type}, sdk: ${item.sdk}, target: ${item.target}, arch: ${item.arch}` ); }); }) diff --git a/package.json b/package.json index bd2682a..e0eede8 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,16 @@ { "name": "@wtto00/android-tools", - "version": "1.0.1", + "version": "1.0.2", "description": "Node module for managing and controlling the Android Devices.", "type": "module", - "main": "dist/lib/index.js", - "module": "dist/esm/index.js", + "main": "dist/lib/index.cjs", + "module": "dist/esm/index.mjs", "types": "dist/index.d.ts", "exports": { ".": { - "import": "./dist/esm/index.js", - "require": "./dist/lib/index.js", + "import": "./dist/esm/index.mjs", + "require": "./dist/lib/index.cjs", "types": "./dist/index.d.ts" - }, - "./util": { - "import": "./dist/esm/util.js", - "require": "./dist/lib/util.js", - "types": "./dist/util.d.ts" } }, "files": [ @@ -53,10 +48,12 @@ "@rollup/plugin-commonjs": "^25.0.4", "@rollup/plugin-node-resolve": "^15.2.1", "@rollup/plugin-typescript": "^11.1.3", + "@types/debug": "^4.1.9", "@types/jest": "^29.5.4", "@types/node": "^20.6.1", "@typescript-eslint/eslint-plugin": "^6.7.0", "@typescript-eslint/parser": "^6.7.0", + "debug": "^4.3.4", "eslint": "^8.49.0", "husky": "^8.0.3", "jest": "^29.7.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 04e9f12..d9aeb81 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ devDependencies: '@rollup/plugin-typescript': specifier: ^11.1.3 version: 11.1.3(rollup@3.29.1)(tslib@2.6.2)(typescript@5.2.2) + '@types/debug': + specifier: ^4.1.9 + version: 4.1.9 '@types/jest': specifier: ^29.5.4 version: 29.5.4 @@ -26,6 +29,9 @@ devDependencies: '@typescript-eslint/parser': specifier: ^6.7.0 version: 6.7.0(eslint@8.49.0)(typescript@5.2.2) + debug: + specifier: ^4.3.4 + version: 4.3.4 eslint: specifier: ^8.49.0 version: 8.49.0 @@ -975,6 +981,13 @@ packages: '@babel/types': 7.22.19 dev: true + /@types/debug@4.1.9: + resolution: + { integrity: sha512-8Hz50m2eoS56ldRlepxSBa6PWEVCtzUo/92HgLc2qTMnotJNIm7xP+UZhyWoYsyOdd5dxZ+NZLb24rsKyFs2ow== } + dependencies: + '@types/ms': 0.7.31 + dev: true + /@types/estree@1.0.1: resolution: { integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== } @@ -1019,6 +1032,11 @@ packages: { integrity: sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ== } dev: true + /@types/ms@0.7.31: + resolution: + { integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== } + dev: true + /@types/node@20.6.1: resolution: { integrity: sha512-4LcJvuXQlv4lTHnxwyHQZ3uR9Zw2j7m1C9DfuwoTFQQP4Pmu04O6IfLYgMmHoOCt0nosItLLZAH+sOrRE0Bo8g== } diff --git a/rollup.config.mjs b/rollup.config.mjs index 5201c8f..4ac2257 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -6,10 +6,10 @@ import commonjs from '@rollup/plugin-commonjs'; export default defineConfig([ { - input: ['src/index.ts', 'src/util.ts'], + input: 'src/index.ts', output: [ - { dir: 'dist/esm', format: 'esm' }, - { dir: 'dist/lib', format: 'cjs' } + { file: 'dist/esm/index.mjs', format: 'esm' }, + { file: 'dist/lib/index.cjs', format: 'cjs' } ], plugins: [typescript(), resolve(), commonjs()] }, @@ -17,10 +17,5 @@ export default defineConfig([ input: 'src/index.ts', output: [{ file: 'dist/index.d.ts', format: 'esm' }], plugins: [resolve(), commonjs(), dts({ respectExternal: true })] - }, - { - input: 'src/util.ts', - output: [{ file: 'dist/util.d.ts', format: 'esm' }], - plugins: [resolve(), commonjs(), dts({ respectExternal: true })] } ]); diff --git a/spec/afterStart.test.ts b/spec/afterStart.test.ts index d0a5b87..4d697f1 100644 --- a/spec/afterStart.test.ts +++ b/spec/afterStart.test.ts @@ -1,6 +1,5 @@ import path from 'path'; import Android from '../src/index.js'; -import { EmulatorGpu } from '../src/emulator.js'; const android = new Android(); @@ -10,7 +9,7 @@ beforeAll(async () => { let avdName = ''; if (avds.length === 0) { avdName = `TestCreate_${Math.random().toString().substring(2)}`; - const images = (await android.listImages()).filter((item) => item.vendor === 'default' && item.arch === 'x86_64'); + const images = (await android.listImages()).filter((item) => item.target === 'default' && item.arch === 'x86_64'); if (images.length === 0) return; await android.createAVD({ name: avdName, apiLevel: 31, force: false }); } else { @@ -18,13 +17,12 @@ beforeAll(async () => { } const res = await android.start({ avd: avdName, - verbose: true, noaudio: true, noBootAnim: true, noSnapshot: true, noSnapshotSave: true, noWindow: true, - gpu: EmulatorGpu.SWIFTSHADER_INDIRECT + gpu: 'swiftshader_indirect' }); emulatorId = res.id; if (emulatorId) { diff --git a/spec/emulatorAVD.test.ts b/spec/emulatorAVD.test.ts index 15cead6..2253424 100644 --- a/spec/emulatorAVD.test.ts +++ b/spec/emulatorAVD.test.ts @@ -5,7 +5,7 @@ describe('emulator AVD', () => { test('create AVD', async () => { const avdName = `TestCreate_${Math.random()}`; - const images = (await android.listInstalledImages()).filter((item) => item.vendor === 'default'); + const images = (await android.listInstalledImages()).filter((item) => item.target === 'default'); if (images.length === 0) return; await android.createAVD({ name: avdName, package: images[images.length - 1].name, force: false }); const res = await android.hasAVD(avdName); @@ -16,7 +16,7 @@ describe('emulator AVD', () => { test('create an existing AVD', async () => { const avds = await android.listAVDs(); if (avds.length === 0) return; - const images = (await android.listInstalledImages()).filter((item) => item.vendor === 'default'); + const images = (await android.listInstalledImages()).filter((item) => item.target === 'default'); if (images.length === 0) return; await expect(async () => { await android.createAVD({ name: avds[0].Name, package: images[images.length - 1].name }); @@ -26,7 +26,7 @@ describe('emulator AVD', () => { test('force create an existing AVD', async () => { const avds = await android.listAVDs(); if (avds.length === 0) return; - const images = (await android.listInstalledImages()).filter((item) => item.vendor === 'default'); + const images = (await android.listInstalledImages()).filter((item) => item.target === 'default'); if (images.length === 0) return; await android.createAVD({ name: avds[0].Name, package: images[images.length - 1].name, force: true }); const res = await android.hasAVD(avds[0].Name); diff --git a/spec/start.test.ts b/spec/start.test.ts index 9211f5f..98ed745 100644 --- a/spec/start.test.ts +++ b/spec/start.test.ts @@ -6,7 +6,6 @@ describe('start', () => { test('start a non-existent AVD.', async () => { await expect(async () => { await android.start({ - verbose: true, avd: `${Math.random()}`, noaudio: true, noBootAnim: true, diff --git a/src/emulator.ts b/src/emulator.ts index 0bbe84d..07c1e8a 100644 --- a/src/emulator.ts +++ b/src/emulator.ts @@ -45,24 +45,18 @@ export interface EmulatorOptions { noAccel?: boolean; } -export enum SystemImageTarget { - DEFAULT = 'default', - GOOGLE_APIS = 'google_apis', - PLAYSTORE = 'playstore', - ANDROID_WEAR = 'android-wear', - android_wear_cn = 'android-wear-cn', - android_tv = 'android-tv', - google_tv = 'google-tv', - AOSP_ATD = 'aosp_atd', - GOOGLE_ATD = 'google_atd' -} +export type SystemImageTarget = + | 'default' + | 'google_apis' + | 'playstore' + | 'android-wear' + | 'android-wear-cn' + | 'android-tv' + | 'google-tv' + | 'aosp_atd' + | 'google_atd'; -export enum Arch { - X86 = 'x86', - X86_64 = 'x86_64', - ARM64_V8A = 'arm64-v8a', - ARMEABI_V7A = 'armeabi-v7a' -} +export type Arch = 'x86' | 'x86_64' | 'arm64-v8a' | 'armeabi-v7a'; /** * Use -camera-back to control emulation of a camera facing back. @@ -92,42 +86,14 @@ export type CameraFront = 'emulated' | 'none' | (string & {}); * The 'auto' mode is the default. In this mode, the hw.gpu.enabled setting * in the AVD's hardware-qemu.ini file will determine whether GPU emulation * is enabled. + * @value **auto** Auto-select the renderer. + * @value **auto-no-window** Auto-select the renderer when running headless. This will use the same gpu selection mechanism as running without the "-no-window" flag and the "-gpu auto" option. See auto for details on the behavior. + * @value **host** Use the host system's OpenGL driver. + * @value **swiftshader_indirect** Use SwiftShader software renderer on the host, which can be beneficial if you are experiencing issues with your GPU drivers or need to run on systems without GPUs. + * @value **angle_indirect** Use ANGLE, an OpenGL ES to D3D11 renderer (Windows 7 SP1 + Platform update, Windows 8.1+, or Windows 10 only). + * @value **guest** Use guest-side software rendering. For advanced users only. Warning: slow! In API 28 and later, guest rendering is not supported, and will fall back automatically to swiftshader_indirect. */ -export enum EmulatorGpu { - /** Auto-select the renderer. */ - AUTO = 'auto', - /** - * Auto-select the renderer when - * running headless. This will use the same - * gpu selection mechanism as running without - * the "-no-window" flag and the "-gpu auto" - * option. See auto for details on the behavior. - */ - AUTO_NO_WINDOW = 'auto-no-window', - /** Use the host system's OpenGL driver. */ - HOST = 'host', - /** - * Use SwiftShader software renderer on the - * host, which can be beneficial if you are - * experiencing issues with your GPU drivers - * or need to run on systems without GPUs. - */ - SWIFTSHADER_INDIRECT = 'swiftshader_indirect', - /** - * Use ANGLE, an OpenGL ES to D3D11 renderer - * (Windows 7 SP1 + Platform update, - * Windows 8.1+, or Windows 10 only). - */ - ANGLE_INDIRECT = 'angle_indirect', - /** - * Use guest-side software rendering. For - * advanced users only. Warning: slow! - * In API 28 and later, guest rendering - * is not supported, and will fall back - * automatically to swiftshader_indirect. - */ - GUEST = 'guest' -} +export type EmulatorGpu = 'auto' | 'auto-no-window' | 'host' | 'swiftshader_indirect' | 'angle_indirect' | 'guest'; /** * Android Emulator usage: emulator [options] [-qemu args] diff --git a/src/index.ts b/src/index.ts index f1ecc8f..6f7d052 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ import path from 'path'; import { filterGroupImages, + getLocalArch, isExecExpectedResult, processKeyValueGroups, retry, @@ -8,6 +9,12 @@ import { spawnWaitFor } from './util.js'; import { Arch, EmulatorOptions, SystemImageTarget } from './emulator.js'; +import Debug from 'debug'; + +export * from './util.js'; +export * from './emulator.js'; + +const log = Debug('android-tools'); export interface AndroidOptions { /** The location of the `adb` executable file relative to `ANDROID_HOME` */ @@ -18,6 +25,8 @@ export interface AndroidOptions { sdkmanager?: string; /** The location of the `emulator` executable file relative to `ANDROID_HOME` */ emulator?: string; + /** Print debug output or not */ + debug?: boolean; } export type AndroidExecBin = 'adbBin' | 'avdmanagerBin' | 'sdkmanagerBin' | 'emulatorBin'; @@ -76,6 +85,7 @@ class Android { emulatorBin: string = process.env.emulatorBin ?? ''; constructor(props?: AndroidOptions) { + if (!props?.debug) Debug.disable(); this.setAdbBinPath(props?.adb); this.setAvdmanagerBinPath(props?.avdmanager); this.setSdkmanagerBinPath(props?.sdkmanager); @@ -87,6 +97,7 @@ class Android { * @param execPath The location of the `adb` executable file relative to `ANDROID_HOME` */ setAdbBinPath(execPath?: string) { + log('setAdbBinPath: %s', JSON.stringify({ execPath })); if (process.env.ANDROID_HOME) { if (execPath) { this.adbBin = path.resolve(process.env.ANDROID_HOME, execPath); @@ -107,6 +118,7 @@ class Android { * @param execPath The location of the `avdmanager` executable file relative to `ANDROID_HOME` */ setAvdmanagerBinPath(execPath?: string) { + log('setAvdmanagerBinPath: %s', JSON.stringify({ execPath })); if (process.env.ANDROID_HOME) { if (execPath) { this.avdmanagerBin = path.resolve(process.env.ANDROID_HOME, execPath); @@ -128,6 +140,7 @@ class Android { * @param execPath The location of the `sdkmanager` executable file relative to `ANDROID_HOME` */ setSdkmanagerBinPath(execPath?: string) { + log('setSdkmanagerBinPath: %s', JSON.stringify({ execPath })); if (process.env.ANDROID_HOME) { if (execPath) { this.sdkmanagerBin = path.resolve(process.env.ANDROID_HOME, execPath); @@ -148,6 +161,7 @@ class Android { * @param execPath The location of the `emulator` executable file relative to `ANDROID_HOME` */ setEmulatorBinPath(execPath?: string) { + log('setEmulatorBinPath: %s', JSON.stringify({ execPath })); if (process.env.ANDROID_HOME) { if (execPath) { this.emulatorBin = path.resolve(process.env.ANDROID_HOME, execPath); @@ -168,8 +182,10 @@ class Android { * Returns a promise that is resolved with an object that has the following properties. * @param options */ - async start(options: Required> & Omit) { + async start(options: Required> & Omit) { + log('start: %s', JSON.stringify(options)); const opts = []; + options.verbose = true; for (const key in options) { const val = options[key as keyof EmulatorOptions]; const cliKey = '-' + key.replace(/[A-Z]/g, (matched) => `-${matched.toLocaleLowerCase()}`); @@ -194,6 +210,7 @@ class Android { * @param emulatorId id of emulator */ async waitForDevice(emulatorId: string) { + log('waitForDevice: %s', JSON.stringify({ emulatorId })); await this.adb(emulatorId, 'wait-for-device', 300000); } @@ -202,6 +219,7 @@ class Android { * @param emulatorId id of emulator */ async ensureReady(emulatorId: string) { + log('ensureReady: %s', JSON.stringify({ emulatorId })); await this.waitForDevice(emulatorId); await retry(async () => { const proc = await this.adb(emulatorId, 'shell getprop sys.boot_completed'); @@ -215,14 +233,13 @@ class Android { * @param name name of AVD */ async createAVD(options: CreateAVDOptions) { + log('createAVD: %s', JSON.stringify(options)); if (!options.name) return Promise.reject(Error('Missing name parameter.')); if (!options.package && !options.apiLevel) return Promise.reject(Error('Either the parameter "package" or "apiLevel" must be present.')); const systemImage = options.package || - `system-images;android-${options.apiLevel};${options.target || SystemImageTarget.DEFAULT};${ - options.arch || Arch.X86_64 - }`; + `system-images;android-${options.apiLevel};${options.target || 'default'};${options.arch || getLocalArch()}`; // Downloading the SDK takes a lot of time, so it's best to download it in advance. await this.sdkmanager(`--install ${systemImage}`, 600000); let cmdParams = `-n ${options.name} -k ${systemImage}`; @@ -235,6 +252,7 @@ class Android { * @param avdName Name of AVD. */ async hasAVD(avdName: string) { + log('hasAVD: %s', JSON.stringify({ avdName })); const avds = await this.listAVDs(); return ( avds.filter((avd) => { @@ -248,6 +266,7 @@ class Android { * @param emulatorId Id of emulator. */ async stop(emulatorId: string) { + log('stop: %s', JSON.stringify({ emulatorId })); return await this.adb(emulatorId, 'emu kill'); } @@ -255,6 +274,7 @@ class Android { * Wait until the device is stopped. */ async waitForStop(emulatorId: string) { + log('waitForStop: %s', JSON.stringify({ emulatorId })); await retry(async () => { await this.stop(emulatorId); const devices = await this.devices(); @@ -268,6 +288,7 @@ class Android { * @param packageName name of package */ async isInstalled(emulatorId: string, packageName: string) { + log('isInstalled: %s, %s', JSON.stringify({ emulatorId, packageName })); const packages = await this.listPackages(emulatorId); const isInstalled = packages.includes(packageName); return isInstalled; @@ -279,6 +300,7 @@ class Android { * @param apkPath path of apk file */ async install(emulatorId: string, apkPath: string) { + log('install: %s, %s', JSON.stringify({ emulatorId, apkPath })); const process = await this.adb(emulatorId, `install ${apkPath}`); if (process.output.match(/Success/)) return; throw new Error('Could not parse output of adb command'); @@ -290,6 +312,7 @@ class Android { * @param key https://developer.android.com/reference/android/view/KeyEvent */ async inputKeyEvent(emulatorId: string, key: number) { + log('inputKeyEvent: %s, %d', JSON.stringify({ emulatorId, key })); return await this.adb(emulatorId, 'shell input keyevent ' + key); } @@ -297,6 +320,7 @@ class Android { * List connected devices */ async devices() { + log('devices: '); const proc = await this.adb('devices'); const lines = proc.output.split('\n'); lines.shift(); @@ -317,6 +341,7 @@ class Android { * @param emulatorId id of emulator */ async listPackages(emulatorId: string) { + log('listPackages: %s', JSON.stringify({ emulatorId })); const proc = await this.adb(emulatorId, 'shell pm list packages'); const lines = proc.output.split('\n'); return lines @@ -335,6 +360,7 @@ class Android { * List the available device list for creating emulators in the current system. */ async listDevices() { + log('listDevices: '); const proc = await this.avdmanager('list device'); return processKeyValueGroups(proc.output); } @@ -343,6 +369,7 @@ class Android { * List all AVDs created on this machine. */ async listAVDs() { + log('listAVDs: '); const proc = await this.avdmanager('list avd'); return processKeyValueGroups(proc.output); } @@ -351,6 +378,7 @@ class Android { * List available Android targets. */ async listTargets() { + log('listTargets: '); const proc = await this.avdmanager('list target'); return processKeyValueGroups(proc.output); } @@ -359,6 +387,7 @@ class Android { * List available Android images on this machine. */ async listImages() { + log('listImages: '); const proc = await this.sdkmanager('--list'); return filterGroupImages(proc.output); } @@ -367,6 +396,7 @@ class Android { * List installed Android images on this machine. */ async listInstalledImages() { + log('listInstalledImages: '); const proc = await this.sdkmanager('--list_installed'); return filterGroupImages(proc.output); } @@ -378,6 +408,7 @@ class Android { * @param timeout Execution timeout */ async adb(emulatorId: string, cmd?: string, timeout?: number) { + log('adb: %s', JSON.stringify({ emulatorId, cmd, timeout })); if (cmd) return await spawnExec(`${this.adbBin} -s ${emulatorId} ${cmd}`, timeout); return await spawnExec(`${this.adbBin} ${emulatorId}`, timeout); } @@ -388,6 +419,7 @@ class Android { * @param timeout Execution timeout */ async avdmanager(cmd: string, timeout?: number) { + log('avdmanager: %s', JSON.stringify({ cmd, timeout })); return await spawnExec(`${this.avdmanagerBin} ${cmd}`, timeout); } @@ -397,6 +429,7 @@ class Android { * @param timeout Execution timeout */ async sdkmanager(cmd: string, timeout?: number) { + log('sdkmanager: %s', JSON.stringify({ cmd, timeout })); return await spawnExec(`${this.sdkmanagerBin} ${cmd}`, timeout); } @@ -406,6 +439,7 @@ class Android { * @param timeout Execution timeout */ async emulator(cmd: string, timeout?: number) { + log('emulator: %s', JSON.stringify({ cmd, timeout })); return await spawnExec(`${this.emulatorBin} ${cmd}`, timeout); } } diff --git a/src/util.ts b/src/util.ts index 80ac779..881f320 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,6 +1,7 @@ import { spawn, spawnSync } from 'node:child_process'; import type { ChildProcessByStdio, ChildProcessWithoutNullStreams } from 'node:child_process'; import type internal from 'node:stream'; +import { Arch, SystemImageTarget } from './emulator.js'; export function winBatAdapt(cmd: string) { if (process.platform === 'win32' && (cmd.endsWith('sdkmanager') || cmd.endsWith('avdmanager'))) { @@ -166,18 +167,29 @@ export function processKeyValueGroups(str: string) { export function filterGroupImages(output: string) { const lines = output.split('\n'); + const localArch = getLocalArch(); return lines .map((line) => line.split('|')[0].trim()) .filter((line) => line.startsWith('system-images;')) .map((name) => { - const [type, sdk, vendor, arch] = name.split(';'); - return { name, type, sdk, vendor, arch }; + const [type, sdk, target, arch] = name.split(';'); + return { name, type, sdk, target, arch } as { + name: string; + type: string; + sdk: string; + target: SystemImageTarget; + arch: Arch; + }; }) .filter((item) => { - if (process.arch === 'arm') return item.arch === 'armeabi-v7a'; - if (process.arch === 'arm64') return item.arch === 'arm64-v8a'; - if (process.arch === 'ia32') return item.arch === 'x86'; - if (process.arch === 'x64') return item.arch === 'x86_64'; - return false; + return item.arch === localArch; }); } + +export function getLocalArch() { + if (process.arch === 'arm') return 'armeabi-v7a'; + if (process.arch === 'arm64') return 'arm64-v8a'; + if (process.arch === 'ia32') return 'x86'; + if (process.arch === 'x64') return 'x86_64'; + return ''; +}