From 9c3195c1ee726c45a45afbb7ab060c5ea8124234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raphael=20von=20der=20Gr=C3=BCn?= Date: Tue, 13 Apr 2021 12:16:43 +0200 Subject: [PATCH] refactor!: drop support for `android` SDK tool (#1083) * refactor(emulator)!: remove support for legacy `android` binary `emulator.list_images` now always uses the `avdmanager` binary. * refactor(android_sdk)!: remove support for legacy `android` binary `android_sdk.list_targets` now always uses the `avdmanager` binary. * refactor(check_reqs)!: do not look for legacy `android` binary * refactor: replace installation instructions involving `android` binary --- bin/templates/cordova/lib/android_sdk.js | 13 +- bin/templates/cordova/lib/check_reqs.js | 37 +----- bin/templates/cordova/lib/emulator.js | 52 +------- spec/fixtures/sdk25.2-android_list_avd.txt | 7 -- .../fixtures/sdk25.2-android_list_targets.txt | 116 ------------------ spec/unit/android_sdk.spec.js | 50 -------- spec/unit/check_reqs.spec.js | 30 +---- spec/unit/emulator.spec.js | 43 +------ 8 files changed, 7 insertions(+), 341 deletions(-) delete mode 100644 spec/fixtures/sdk25.2-android_list_avd.txt delete mode 100644 spec/fixtures/sdk25.2-android_list_targets.txt diff --git a/bin/templates/cordova/lib/android_sdk.js b/bin/templates/cordova/lib/android_sdk.js index 963d7f1d..5d9da08c 100755 --- a/bin/templates/cordova/lib/android_sdk.js +++ b/bin/templates/cordova/lib/android_sdk.js @@ -76,23 +76,12 @@ function parse_targets (output) { return targets; } -module.exports.list_targets_with_android = function () { - return execa('android', ['list', 'target']).then(result => parse_targets(result.stdout)); -}; - module.exports.list_targets_with_avdmanager = function () { return execa('avdmanager', ['list', 'target']).then(result => parse_targets(result.stdout)); }; module.exports.list_targets = function () { - return module.exports.list_targets_with_avdmanager().catch(function (err) { - // If there's an error, like avdmanager could not be found, we can try - // as a last resort, to run `android`, in case this is a super old - // SDK installation. - if (err && (err.code === 'ENOENT' || (err.stderr && err.stderr.match(/not recognized/)))) { - return module.exports.list_targets_with_android(); - } else throw err; - }).then(function (targets) { + return module.exports.list_targets_with_avdmanager().then(function (targets) { if (targets.length === 0) { return Promise.reject(new Error('No android targets (SDKs) installed!')); } diff --git a/bin/templates/cordova/lib/check_reqs.js b/bin/templates/cordova/lib/check_reqs.js index 82827ac0..cc62b6da 100644 --- a/bin/templates/cordova/lib/check_reqs.js +++ b/bin/templates/cordova/lib/check_reqs.js @@ -161,7 +161,6 @@ module.exports.check_android = function () { } } - var androidCmdPath = forgivingWhichSync('android'); var adbInPath = forgivingWhichSync('adb'); var avdmanagerInPath = forgivingWhichSync('avdmanager'); var hasAndroidHome = false; @@ -172,7 +171,7 @@ module.exports.check_android = function () { // First ensure ANDROID_HOME is set // If we have no hints (nothing in PATH), try a few default locations - if (!hasAndroidHome && !androidCmdPath && !adbInPath && !avdmanagerInPath) { + if (!hasAndroidHome && !adbInPath && !avdmanagerInPath) { if (process.env.ANDROID_HOME) { // Fallback to deprecated `ANDROID_HOME` variable maybeSetAndroidHome(path.join(process.env.ANDROID_HOME)); @@ -222,17 +221,6 @@ module.exports.check_android = function () { if (!hasAndroidHome) { // If we dont have ANDROID_SDK_ROOT, but we do have some tools on the PATH, try to infer from the tooling PATH. var parentDir, grandParentDir; - if (androidCmdPath) { - parentDir = path.dirname(androidCmdPath); - grandParentDir = path.dirname(parentDir); - if (path.basename(parentDir) === 'tools' || fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) { - maybeSetAndroidHome(grandParentDir); - } else { - throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' environment variable. Try setting it manually.\n' + - 'Detected \'android\' command at ' + parentDir + ' but no \'tools\' directory found near.\n' + - 'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'tools directory.'); - } - } if (adbInPath) { parentDir = path.dirname(adbInPath); grandParentDir = path.dirname(parentDir); @@ -265,9 +253,6 @@ module.exports.check_android = function () { '\nTry update it manually to point to valid SDK directory.'); } // Next let's make sure relevant parts of the SDK tooling is in our PATH - if (hasAndroidHome && !androidCmdPath) { - process.env.PATH += path.delimiter + path.join(process.env.ANDROID_SDK_ROOT, 'tools'); - } if (hasAndroidHome && !adbInPath) { process.env.PATH += path.delimiter + path.join(process.env.ANDROID_SDK_ROOT, 'platform-tools'); } @@ -278,18 +263,6 @@ module.exports.check_android = function () { }); }; -// TODO: is this actually needed? -module.exports.getAbsoluteAndroidCmd = function () { - var cmd = forgivingWhichSync('android'); - if (cmd.length === 0) { - cmd = forgivingWhichSync('sdkmanager'); - } - if (module.exports.isWindows()) { - return '"' + cmd + '"'; - } - return cmd.replace(/(\s)/g, '\\$1'); -}; - module.exports.check_android_target = function (originalError) { // valid_target can look like: // android-19 @@ -301,13 +274,7 @@ module.exports.check_android_target = function (originalError) { if (targets.indexOf(desired_api_level) >= 0) { return targets; } - var androidCmd = module.exports.getAbsoluteAndroidCmd(); - var msg = 'Please install Android target / API level: "' + desired_api_level + '".\n\n' + - 'Hint: Open the SDK manager by running: ' + androidCmd + '\n' + - 'You will require:\n' + - '1. "SDK Platform" for API level ' + desired_api_level + '\n' + - '2. "Android SDK Platform-tools (latest)\n' + - '3. "Android SDK Build-tools" (latest)'; + var msg = `Please install the Android SDK Platform "platforms;${desired_api_level}"`; if (originalError) { msg = originalError + '\n' + msg; } diff --git a/bin/templates/cordova/lib/emulator.js b/bin/templates/cordova/lib/emulator.js index 7a5fbd17..0a2f8bf0 100644 --- a/bin/templates/cordova/lib/emulator.js +++ b/bin/templates/cordova/lib/emulator.js @@ -99,48 +99,6 @@ module.exports.list_images_using_avdmanager = function () { }); }; -module.exports.list_images_using_android = function () { - return execa('android', ['list', 'avd']).then(({ stdout: output }) => { - var response = output.split('\n'); - var emulator_list = []; - for (var i = 1; i < response.length; i++) { - // To return more detailed information use img_obj - var img_obj = {}; - if (response[i].match(/Name:\s/)) { - img_obj.name = response[i].split('Name: ')[1].replace('\r', ''); - if (response[i + 1].match(/Device:\s/)) { - i++; - img_obj.device = response[i].split('Device: ')[1].replace('\r', ''); - } - if (response[i + 1].match(/Path:\s/)) { - i++; - img_obj.path = response[i].split('Path: ')[1].replace('\r', ''); - } - if (response[i + 1].match(/\(API\slevel\s/) || (response[i + 2] && response[i + 2].match(/\(API\slevel\s/))) { - i++; - var secondLine = response[i + 1].match(/\(API\slevel\s/) ? response[i + 1] : ''; - img_obj.target = (response[i] + secondLine).split('Target: ')[1].replace('\r', ''); - } - if (response[i + 1].match(/ABI:\s/)) { - i++; - img_obj.abi = response[i].split('ABI: ')[1].replace('\r', ''); - } - if (response[i + 1].match(/Skin:\s/)) { - i++; - img_obj.skin = response[i].split('Skin: ')[1].replace('\r', ''); - } - - emulator_list.push(img_obj); - } - /* To just return a list of names use this - if (response[i].match(/Name:\s/)) { - emulator_list.push(response[i].split('Name: ')[1].replace('\r', ''); - } */ - } - return emulator_list; - }); -}; - /** * Returns a Promise for a list of emulator images in the form of objects * { @@ -156,10 +114,8 @@ module.exports.list_images = function () { return Promise.resolve().then(function () { if (forgivingWhichSync('avdmanager')) { return module.exports.list_images_using_avdmanager(); - } else if (forgivingWhichSync('android')) { - return module.exports.list_images_using_android(); } else { - return Promise.reject(new CordovaError('Could not find either `android` or `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?')); + return Promise.reject(new CordovaError('Could not find `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?')); } }).then(function (avds) { // In case we're missing the Android OS version string from the target description, add it. @@ -252,11 +208,7 @@ module.exports.start = function (emulator_ID, boot_timeout) { return best.name; } - var androidCmd = check_reqs.getAbsoluteAndroidCmd(); - return Promise.reject(new CordovaError('No emulator images (avds) found.\n' + - '1. Download desired System Image by running: ' + androidCmd + ' sdk\n' + - '2. Create an AVD by running: ' + androidCmd + ' avd\n' + - 'HINT: For a faster emulator, use an Intel System Image and install the HAXM device driver\n')); + return Promise.reject(new CordovaError('No emulator images (avds) found')); }); }).then(function (emulatorId) { return self.get_available_port().then(function (port) { diff --git a/spec/fixtures/sdk25.2-android_list_avd.txt b/spec/fixtures/sdk25.2-android_list_avd.txt deleted file mode 100644 index 22f80a42..00000000 --- a/spec/fixtures/sdk25.2-android_list_avd.txt +++ /dev/null @@ -1,7 +0,0 @@ -Available Android Virtual Devices: - Name: QWR - Device: Nexus 5 (Google) - Path: /Users/shazron/.android/avd/QWR.avd - Target: Android 7.1.1 (API level 25) - Tag/ABI: google_apis/x86_64 - Skin: 1080x1920 diff --git a/spec/fixtures/sdk25.2-android_list_targets.txt b/spec/fixtures/sdk25.2-android_list_targets.txt deleted file mode 100644 index b21e4bd3..00000000 --- a/spec/fixtures/sdk25.2-android_list_targets.txt +++ /dev/null @@ -1,116 +0,0 @@ -Available Android targets: ----------- -id: 1 or "android-20" - Name: Android 4.4W.2 - Type: Platform - API level: 20 - Revision: 2 - Skins: HVGA, QVGA, WQVGA400, WQVGA432, WSVGA, WVGA800 (default), WVGA854, WXGA720, WXGA800, WXGA800-7in - Tag/ABIs : no ABIs. ----------- -id: 2 or "android-21" - Name: Android 5.0.1 - Type: Platform - API level: 21 - Revision: 2 - Skins: HVGA, QVGA, WQVGA400, WQVGA432, WSVGA, WVGA800 (default), WVGA854, WXGA720, WXGA800, WXGA800-7in - Tag/ABIs : android-tv/armeabi-v7a, android-tv/x86, default/armeabi-v7a, default/x86, default/x86_64 ----------- -id: 3 or "android-22" - Name: Android 5.1.1 - Type: Platform - API level: 22 - Revision: 2 - Skins: HVGA, QVGA, WQVGA400, WQVGA432, WSVGA, WVGA800 (default), WVGA854, WXGA720, WXGA800, WXGA800-7in - Tag/ABIs : android-tv/armeabi-v7a, android-tv/x86, default/armeabi-v7a, default/x86, default/x86_64 ----------- -id: 4 or "android-MNC" - Name: Android M (Preview) - Type: Platform - API level: MNC - Revision: 1 - Skins: HVGA, QVGA, WQVGA400, WQVGA432, WSVGA, WVGA800 (default), WVGA854, WXGA720, WXGA800, WXGA800-7in - Tag/ABIs : no ABIs. ----------- -id: 5 or "android-23" - Name: Android 6.0 - Type: Platform - API level: 23 - Revision: 3 - Skins: HVGA, QVGA, WQVGA400, WQVGA432, WSVGA, WVGA800 (default), WVGA854, WXGA720, WXGA800, WXGA800-7in - Tag/ABIs : android-tv/armeabi-v7a, android-tv/x86, default/x86, default/x86_64 ----------- -id: 6 or "android-N" - Name: Android N (Preview) - Type: Platform - API level: N - Revision: 3 - Skins: HVGA, QVGA, WQVGA400, WQVGA432, WSVGA, WVGA800 (default), WVGA854, WXGA720, WXGA800, WXGA800-7in - Tag/ABIs : no ABIs. ----------- -id: 7 or "android-24" - Name: Android 7.0 - Type: Platform - API level: 24 - Revision: 2 - Skins: HVGA, QVGA, WQVGA400, WQVGA432, WSVGA, WVGA800 (default), WVGA854, WXGA720, WXGA800, WXGA800-7in - Tag/ABIs : android-tv/x86, default/x86, default/x86_64 ----------- -id: 8 or "android-25" - Name: Android 7.1.1 - Type: Platform - API level: 25 - Revision: 3 - Skins: HVGA, QVGA, WQVGA400, WQVGA432, WSVGA, WVGA800 (default), WVGA854, WXGA720, WXGA800, WXGA800-7in - Tag/ABIs : android-tv/x86, google_apis/x86, google_apis/x86_64 ----------- -id: 9 or "Google Inc.:Google APIs:21" - Name: Google APIs - Type: Add-On - Vendor: Google Inc. - Revision: 1 - Description: Android + Google APIs - Based on Android 5.0.1 (API level 21) - Libraries: - * com.android.future.usb.accessory (usb.jar) - API for USB Accessories - * com.google.android.media.effects (effects.jar) - Collection of video effects - * com.google.android.maps (maps.jar) - API for Google Maps - Skins: HVGA, QVGA, WQVGA400, WQVGA432, WSVGA, WVGA800 (default), WVGA854, WXGA720, WXGA800, WXGA800-7in - Tag/ABIs : google_apis/armeabi-v7a, google_apis/x86, google_apis/x86_64 ----------- -id: 10 or "Google Inc.:Google APIs:22" - Name: Google APIs - Type: Add-On - Vendor: Google Inc. - Revision: 1 - Description: Android + Google APIs - Based on Android 5.1.1 (API level 22) - Libraries: - * com.android.future.usb.accessory (usb.jar) - API for USB Accessories - * com.google.android.media.effects (effects.jar) - Collection of video effects - * com.google.android.maps (maps.jar) - API for Google Maps - Skins: HVGA, QVGA, WQVGA400, WQVGA432, WSVGA, WVGA800 (default), WVGA854, WXGA720, WXGA800, WXGA800-7in - Tag/ABIs : google_apis/armeabi-v7a, google_apis/x86, google_apis/x86_64 ----------- -id: 11 or "Google Inc.:Google APIs:23" - Name: Google APIs - Type: Add-On - Vendor: Google Inc. - Revision: 1 - Description: Android + Google APIs - Based on Android 6.0 (API level 23) - Libraries: - * com.android.future.usb.accessory (usb.jar) - API for USB Accessories - * com.google.android.media.effects (effects.jar) - Collection of video effects - * com.google.android.maps (maps.jar) - API for Google Maps - Skins: HVGA, QVGA, WQVGA400, WQVGA432, WSVGA, WVGA800 (default), WVGA854, WXGA720, WXGA800, WXGA800-7in - Tag/ABIs : google_apis/armeabi-v7a, google_apis/x86, google_apis/x86_64 diff --git a/spec/unit/android_sdk.spec.js b/spec/unit/android_sdk.spec.js index 346bca9b..25c1929b 100644 --- a/spec/unit/android_sdk.spec.js +++ b/spec/unit/android_sdk.spec.js @@ -66,33 +66,6 @@ describe('android_sdk', () => { }); }); - describe('list_targets_with_android', () => { - it('should invoke `android` with the `list target` command and _not_ the `list targets` command, as the plural form is not supported in some Android SDK Tools versions', () => { - execaSpy.and.returnValue(Promise.resolve({ stdout: '' })); - android_sdk.list_targets_with_android(); - expect(execaSpy).toHaveBeenCalledWith('android', ['list', 'target']); - }); - - it('should parse and return results from `android list targets` command', () => { - const testTargets = fs.readFileSync(path.join('spec', 'fixtures', 'sdk25.2-android_list_targets.txt'), 'utf-8'); - execaSpy.and.returnValue(Promise.resolve({ stdout: testTargets })); - - return android_sdk.list_targets_with_android().then(list => { - ['Google Inc.:Google APIs:23', - 'Google Inc.:Google APIs:22', - 'Google Inc.:Google APIs:21', - 'android-25', - 'android-24', - 'android-N', - 'android-23', - 'android-MNC', - 'android-22', - 'android-21', - 'android-20'].forEach((target) => expect(list).toContain(target)); - }); - }); - }); - describe('list_targets_with_avdmanager', () => { it('should parse and return results from `avdmanager list target` command', () => { const testTargets = fs.readFileSync(path.join('spec', 'fixtures', 'sdk25.3-avdmanager_list_target.txt'), 'utf-8'); @@ -111,29 +84,6 @@ describe('android_sdk', () => { expect(avdmanager_spy).toHaveBeenCalled(); }); - it('should parse Android SDK installed target information with `android` command if list_targets_with_avdmanager fails with ENOENT', () => { - spyOn(android_sdk, 'list_targets_with_avdmanager').and.returnValue(Promise.reject({ code: 'ENOENT' })); - const avdmanager_spy = spyOn(android_sdk, 'list_targets_with_android').and.returnValue(Promise.resolve(['target1'])); - - return android_sdk.list_targets().then(targets => { - expect(avdmanager_spy).toHaveBeenCalled(); - expect(targets[0]).toEqual('target1'); - }); - }); - - it('should parse Android SDK installed target information with `android` command if list_targets_with_avdmanager fails with not-recognized error (Windows)', () => { - spyOn(android_sdk, 'list_targets_with_avdmanager').and.returnValue(Promise.reject({ - code: 1, - stderr: "'avdmanager' is not recognized as an internal or external commmand,\r\noperable program or batch file.\r\n" - })); - - const avdmanager_spy = spyOn(android_sdk, 'list_targets_with_android').and.returnValue(Promise.resolve(['target1'])); - return android_sdk.list_targets().then(targets => { - expect(avdmanager_spy).toHaveBeenCalled(); - expect(targets[0]).toEqual('target1'); - }); - }); - it('should throw an error if `avdmanager` command fails with an unknown error', () => { const errorMsg = 'Some random error'; spyOn(android_sdk, 'list_targets_with_avdmanager').and.returnValue(Promise.reject(errorMsg)); diff --git a/spec/unit/check_reqs.spec.js b/spec/unit/check_reqs.spec.js index a1fe4f9d..65472000 100644 --- a/spec/unit/check_reqs.spec.js +++ b/spec/unit/check_reqs.spec.js @@ -100,34 +100,6 @@ describe('check_reqs', function () { return path; }); }); - it('should set ANDROID_SDK_ROOT based on `android` command if command exists in a SDK-like directory structure', () => { - spyOn(fs, 'existsSync').and.returnValue(true); - spyOn(which, 'sync').and.callFake(function (cmd) { - if (cmd === 'android') { - return '/android/sdk/tools/android'; - } else { - return null; - } - }); - return check_reqs.check_android().then(function () { - expect(process.env.ANDROID_SDK_ROOT).toEqual('/android/sdk'); - }); - }); - it('should error out if `android` command exists in a non-SDK-like directory structure', () => { - spyOn(which, 'sync').and.callFake(function (cmd) { - if (cmd === 'android') { - return '/just/some/random/path/android'; - } else { - return null; - } - }); - return check_reqs.check_android().then(() => { - fail('Expected promise to be rejected'); - }, err => { - expect(err).toEqual(jasmine.any(Error)); - expect(err.message).toContain('update your PATH to include valid path'); - }); - }); it('should set ANDROID_SDK_ROOT based on `adb` command if command exists in a SDK-like directory structure', () => { spyOn(fs, 'existsSync').and.returnValue(true); spyOn(which, 'sync').and.callFake(function (cmd) { @@ -398,7 +370,7 @@ describe('check_reqs', function () { fail('Expected promise to be rejected'); }, err => { expect(err).toEqual(jasmine.any(Error)); - expect(err.message).toContain('Please install Android target'); + expect(err.message).toContain('Please install the Android SDK Platform'); }); }); }); diff --git a/spec/unit/emulator.spec.js b/spec/unit/emulator.spec.js index 35079268..885f780e 100644 --- a/spec/unit/emulator.spec.js +++ b/spec/unit/emulator.spec.js @@ -49,33 +49,6 @@ describe('emulator', () => { }); }); - describe('list_images_using_android', () => { - it('should invoke `android` with the `list avd` command and _not_ the `list avds` command, as the plural form is not supported in some Android SDK Tools versions', () => { - const execaSpy = jasmine.createSpy('execa').and.returnValue(Promise.resolve({ stdout: '' })); - emu.__set__('execa', execaSpy); - - emu.list_images_using_android(); - expect(execaSpy).toHaveBeenCalledWith('android', ['list', 'avd']); - }); - - it('should properly parse details of SDK Tools pre-25.3.1 `android list avd` output', () => { - const avdList = fs.readFileSync(path.join('spec', 'fixtures', 'sdk25.2-android_list_avd.txt'), 'utf-8'); - - const execaSpy = jasmine.createSpy('execa').and.returnValue(Promise.resolve({ stdout: avdList })); - emu.__set__('execa', execaSpy); - - return emu.list_images_using_android().then(list => { - expect(list).toBeDefined(); - expect(list[0].name).toEqual('QWR'); - expect(list[0].device).toEqual('Nexus 5 (Google)'); - expect(list[0].path).toEqual('/Users/shazron/.android/avd/QWR.avd'); - expect(list[0].target).toEqual('Android 7.1.1 (API level 25)'); - expect(list[0].abi).toEqual('google_apis/x86_64'); - expect(list[0].skin).toEqual('1080x1920'); - }); - }); - }); - describe('list_images', () => { beforeEach(() => { spyOn(fs, 'realpathSync').and.callFake(cmd => cmd); @@ -91,16 +64,6 @@ describe('emulator', () => { }); }); - it('should delegate to `android` if `avdmanager` cant be found and `android` can', () => { - spyOn(which, 'sync').and.callFake(cmd => cmd !== 'avdmanager'); - - const android_spy = spyOn(emu, 'list_images_using_android').and.returnValue(Promise.resolve([])); - - return emu.list_images().then(() => { - expect(android_spy).toHaveBeenCalled(); - }); - }); - it('should correct api level information and fill in the blanks about api level if exists', () => { spyOn(which, 'sync').and.callFake(cmd => cmd === 'avdmanager'); spyOn(emu, 'list_images_using_avdmanager').and.returnValue(Promise.resolve([ @@ -132,7 +95,7 @@ describe('emulator', () => { () => fail('Unexpectedly resolved'), err => { expect(err).toBeDefined(); - expect(err.message).toContain('Could not find either `android` or `avdmanager`'); + expect(err.message).toContain('Could not find `avdmanager`'); } ); }); @@ -252,7 +215,6 @@ describe('emulator', () => { const port = 5555; let emulator; let AdbSpy; - let checkReqsSpy; let execaSpy; let whichSpy; @@ -269,9 +231,6 @@ describe('emulator', () => { AdbSpy.shell.and.returnValue(Promise.resolve()); emu.__set__('Adb', AdbSpy); - checkReqsSpy = jasmine.createSpyObj('create_reqs', ['getAbsoluteAndroidCmd']); - emu.__set__('check_reqs', checkReqsSpy); - execaSpy = jasmine.createSpy('execa').and.returnValue( jasmine.createSpyObj('spawnFns', ['unref']) );