diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ce578541a..4542de74b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -61,11 +61,11 @@ jobs: npm run lint npm test - - name: Java 15 - uses: actions/setup-java@v3 + - uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: 15 + java-version: 19 + - uses: actions/cache@v3 id: avd-cache with: diff --git a/.github/workflows/manually.yml b/.github/workflows/manually.yml index 5b1338f3d..33d3b39e2 100644 --- a/.github/workflows/manually.yml +++ b/.github/workflows/manually.yml @@ -51,17 +51,14 @@ jobs: npm run lint npm test - - name: Java 15 - uses: actions/setup-java@v3 + - uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: 15 - - uses: actions/cache@v3 + java-version: 19 + + - uses: gradle/gradle-build-action@v2 with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }} + gradle-home-cache-cleanup: true - name: run action uses: ./ diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e109aa82..3b2ef91e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Change Log +## v2.28.0 + +* Add `emulator-boot-timeout` to support configuring maximum time waiting for emulator boot. - [#326](https://github.com/ReactiveCircus/android-emulator-runner/pull/326) +* Support non-integer `api-level`. - [#317](https://github.com/ReactiveCircus/android-emulator-runner/pull/317) +* Replace deprecated `ANDROID_SDK_ROOT` with `ANDROID_HOME`. - [304](https://github.com/ReactiveCircus/android-emulator-runner/pull/304) +* Update SDK command-line tools to `9.0`. - [#331](https://github.com/ReactiveCircus/android-emulator-runner/pull/331) +* Update SDK build tools to `33.0.2`. - [#331](https://github.com/ReactiveCircus/android-emulator-runner/pull/331) + ## v2.27.0 * Added `pre-emulator-launch-script` to support running script after creating the AVD and before launching the emulator. - [#247](https://github.com/ReactiveCircus/android-emulator-runner/pull/247) @nilsreichardt. diff --git a/README.md b/README.md index a9eee0cdd..05056d5b8 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,11 @@ The old ARM-based emulators were slow and are no longer supported by Google. The This presents a challenge when running emulators on CI especially when running emulators within a docker container, because **Nested Virtualization** must be supported by the host VM which isn't the case for most cloud-based CI providers due to infrastructural limits. If you want to learn more about Emulators on CI, here's an article [Yang](https://github.com/ychescale9) wrote: [Running Android Instrumented Tests on CI](https://dev.to/ychescale9/running-android-emulators-on-ci-from-bitrise-io-to-github-actions-3j76). -## HAXM support on Github's MacOS Runners +## A note on VM Acceleration and why we don't need HAXM anymore -The 10.x **macOS** VM provided by **GitHub Actions** had **HAXM** [pre-installed](https://github.com/actions/runner-images/blob/main/images/macos/macos-10.15-Readme.md). However, Github's [macOS-11](https://github.com/actions/runner-images/blob/main/images/macos/macos-11-Readme.md) and [macOS-12](https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md) VMs **no longer** come pre-installed with HAXM. See [here](https://github.com/actions/runner-images/issues/183#issuecomment-610723516) and [here](https://github.com/actions/runner-images/issues/6388) for more info. +According to [this documentation](https://developer.android.com/studio/run/emulator-acceleration#vm-mac), "on Mac OS X v10.10 Yosemite and higher, the Android Emulator uses the built-in [Hypervisor.Framework](https://developer.apple.com/documentation/hypervisor) by default, and falls back to using Intel HAXM if Hypervisor.Framework fails to initialize." This means that **HAXM is only needed to achieve VM Acceleration if this default Hypervisor is not available on macOS machines.** -**To run with HAXM on the macOS-11 and macOS-12 agents, you must install HAXM before starting your emulator**. See: [this snippet](https://gist.github.com/mrk-han/a0a11ed9bed966bb8b775ff55f2a87e9) for more info. This will enable VM acceleration, but as of right now running with GPU acceleration, e.g. `emulator -gpu host` is not possible with Github's standard runners. - -For Linux, Nested virtualization is possible on a self-hosted or 3rd party runner, but the VM will need to be hosted on a compatible machine that allows you to [enable KVM](https://developer.android.com/studio/run/emulator-acceleration#vm-linux), or is already configured with - for example the AWS EC2 Bare Metal instances. **The Github-hosted Linux runners are not currently KVM compatible.** +**Note**: Manually enabling and downloading HAXM is not recommended because it is redundant and not needed (see above), and for users of macOS 10.13 High Sierra and higher: macOS 10.13 [disables installation of kernel extensions by default](https://developer.apple.com/library/archive/technotes/tn2459/_index.html#//apple_ref/doc/uid/DTS40017658). Because Intel HAXM is a kernel extension, we would need to manually enable its installation on the base runner VM. Furthermore, manually trying to install HAXM on a Github Runner [brings up a popup](https://github.com/ReactiveCircus/android-emulator-runner/discussions/286#discussioncomment-4026120) which further hinders tests from running. ## Purpose @@ -156,6 +154,7 @@ jobs: | `disk-size` | Optional | N/A | Disk size, or partition size to use for this AVD. Either in bytes or KB, MB or GB, when denoted with K, M or G. - e.g. `2048M` | | `avd-name` | Optional | `test` | Custom AVD name used for creating the Android Virtual Device. | | `force-avd-creation` | Optional | `true` | Whether to force create the AVD by overwriting an existing AVD with the same name as `avd-name` - `true` or `false`. | +| `emulator-boot-timeout` | Optional | `600` | Emulator boot timeout in seconds. If it takes longer to boot, the action would fail - e.g. `300` for 5 minutes. | | `emulator-options` | Optional | See below | Command-line options used when launching the emulator (replacing all default options) - e.g. `-no-window -no-snapshot -camera-back emulated`. | | `disable-animations` | Optional | `true` | Whether to disable animations - `true` or `false`. | | `disable-spellchecker` | Optional | `false` | Whether to disable spellchecker - `true` or `false`. | @@ -210,5 +209,6 @@ These are some of the open-source projects using (or used) **Android Emulator Ru - [dotanuki-labs/norris](https://github.com/dotanuki-labs/norris/blob/master/.github/workflows/main.yml) - [tinylog-org/tinylog](https://github.com/tinylog-org/tinylog/blob/v3.0/.github/workflows/build.yaml) - [hzi-braunschweig/SORMAS-Project](https://github.com/hzi-braunschweig/SORMAS-Project/blob/development/.github/workflows/sormas_app_ci.yml) +- [ACRA/acra](https://github.com/ACRA/acra/blob/master/.github/workflows/test.yml) If you are using **Android Emulator Runner** and want your project included in the list, please feel free to open a pull request. diff --git a/__tests__/input-validator.test.ts b/__tests__/input-validator.test.ts index ba7e20eb4..59201f414 100644 --- a/__tests__/input-validator.test.ts +++ b/__tests__/input-validator.test.ts @@ -32,6 +32,14 @@ describe('api-level validator tests', () => { validator.checkApiLevel('29'); }; expect(func2).not.toThrow(); + const func3 = () => { + validator.checkApiLevel('UpsideDownCake-ext5'); + }; + expect(func3).not.toThrow(); + const func4 = () => { + validator.checkApiLevel('TiramisuPrivacySandbox'); + }; + expect(func4).not.toThrow(); }); }); diff --git a/action-types.yml b/action-types.yml index 1c1d4617c..49cbb7368 100644 --- a/action-types.yml +++ b/action-types.yml @@ -35,6 +35,8 @@ inputs: type: string force-avd-creation: type: boolean + emulator-boot-timeout: + type: integer emulator-options: type: string disable-animations: diff --git a/action.yml b/action.yml index 13ba8e368..a988b3712 100644 --- a/action.yml +++ b/action.yml @@ -33,6 +33,9 @@ inputs: force-avd-creation: description: 'whether to force create the AVD by overwriting an existing AVD with the same name as `avd-name` - `true` or `false`' default: 'true' + emulator-boot-timeout: + description: 'Emulator boot timeout in seconds. If it takes longer to boot, the action would fail - e.g. `300` for 5 minutes' + default: '600' emulator-options: description: 'command-line options used when launching the emulator - e.g. `-no-window -no-snapshot -camera-back emulated`' default: '-no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim' diff --git a/lib/emulator-manager.js b/lib/emulator-manager.js index c0fa07c79..97b4a435b 100644 --- a/lib/emulator-manager.js +++ b/lib/emulator-manager.js @@ -35,11 +35,10 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.killEmulator = exports.launchEmulator = void 0; const exec = __importStar(require("@actions/exec")); const fs = __importStar(require("fs")); -const EMULATOR_BOOT_TIMEOUT_SECONDS = 600; /** * Creates and launches a new AVD instance with the specified configurations. */ -function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard) { +function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard) { return __awaiter(this, void 0, void 0, function* () { try { console.log(`::group::Launch Emulator`); @@ -73,7 +72,7 @@ function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, heapSiz } // start emulator console.log('Starting emulator.'); - yield exec.exec(`sh -c \\"${process.env.ANDROID_SDK_ROOT}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], { + yield exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], { listeners: { stderr: (data) => { if (data.toString().includes('invalid command-line parameter')) { @@ -83,7 +82,7 @@ function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, heapSiz }, }); // wait for emulator to complete booting - yield waitForDevice(); + yield waitForDevice(emulatorBootTimeout); yield exec.exec(`adb shell input keyevent 82`); if (disableAnimations) { console.log('Disabling animations.'); @@ -125,12 +124,12 @@ exports.killEmulator = killEmulator; /** * Wait for emulator to boot. */ -function waitForDevice() { +function waitForDevice(emulatorBootTimeout) { return __awaiter(this, void 0, void 0, function* () { let booted = false; let attempts = 0; const retryInterval = 2; // retry every 2 seconds - const maxAttempts = EMULATOR_BOOT_TIMEOUT_SECONDS / 2; + const maxAttempts = emulatorBootTimeout / 2; while (!booted) { try { let result = ''; diff --git a/lib/input-validator.js b/lib/input-validator.js index 95959a5ad..783935843 100644 --- a/lib/input-validator.js +++ b/lib/input-validator.js @@ -6,6 +6,8 @@ exports.VALID_TARGETS = ['default', 'google_apis', 'aosp_atd', 'google_atd', 'go exports.VALID_ARCHS = ['x86', 'x86_64', 'arm64-v8a']; exports.VALID_CHANNELS = ['stable', 'beta', 'dev', 'canary']; function checkApiLevel(apiLevel) { + if (apiLevel.startsWith('UpsideDownCake') || apiLevel === 'TiramisuPrivacySandbox') + return; if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) { throw new Error(`Unexpected API level: '${apiLevel}'.`); } diff --git a/lib/main.js b/lib/main.js index a4fd8d3f3..1bbc7d012 100644 --- a/lib/main.js +++ b/lib/main.js @@ -61,9 +61,8 @@ function run() { } } // API level of the platform and system image - const apiLevelInput = core.getInput('api-level', { required: true }); - (0, input_validator_1.checkApiLevel)(apiLevelInput); - const apiLevel = Number(apiLevelInput); + const apiLevel = core.getInput('api-level', { required: true }); + (0, input_validator_1.checkApiLevel)(apiLevel); console.log(`API level: ${apiLevel}`); // target of the system image const targetInput = core.getInput('target'); @@ -100,6 +99,9 @@ function run() { (0, input_validator_1.checkForceAvdCreation)(forceAvdCreationInput); const forceAvdCreation = forceAvdCreationInput === 'true'; console.log(`force avd creation: ${forceAvdCreation}`); + // Emulator boot timeout seconds + const emulatorBootTimeout = parseInt(core.getInput('emulator-boot-timeout'), 10); + console.log(`Emulator boot timeout: ${emulatorBootTimeout}`); // emulator options const emulatorOptions = core.getInput('emulator-options').trim(); console.log(`emulator options: ${emulatorOptions}`); @@ -191,7 +193,7 @@ function run() { console.log(`::endgroup::`); } // launch an emulator - yield (0, emulator_manager_1.launchEmulator)(apiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard); + yield (0, emulator_manager_1.launchEmulator)(apiLevel, target, arch, profile, cores, ramSize, heapSize, sdcardPathOrSize, diskSize, avdName, forceAvdCreation, emulatorBootTimeout, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard); // execute the custom script try { // move to custom working directory if set diff --git a/lib/sdk-installer.js b/lib/sdk-installer.js index d3b60abb4..ce320386d 100644 --- a/lib/sdk-installer.js +++ b/lib/sdk-installer.js @@ -38,9 +38,10 @@ const exec = __importStar(require("@actions/exec")); const io = __importStar(require("@actions/io")); const tc = __importStar(require("@actions/tool-cache")); const fs = __importStar(require("fs")); -const BUILD_TOOLS_VERSION = '33.0.0'; -const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-8512546_latest.zip'; -const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-8512546_latest.zip'; +const BUILD_TOOLS_VERSION = '33.0.2'; +// SDK command-line tools 9.0 +const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-9477386_latest.zip'; +const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip'; /** * Installs & updates the Android SDK for the macOS platform, including SDK platform for the chosen API level, latest build tools, platform tools, Android Emulator, * and the system image for the chosen API level, CPU arch, and target. @@ -51,9 +52,9 @@ function installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndk console.log(`::group::Install Android SDK`); const isOnMac = process.platform === 'darwin'; if (!isOnMac) { - yield exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_SDK_ROOT} -R`); + yield exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_HOME} -R`); } - const cmdlineToolsPath = `${process.env.ANDROID_SDK_ROOT}/cmdline-tools`; + const cmdlineToolsPath = `${process.env.ANDROID_HOME}/cmdline-tools`; if (!fs.existsSync(cmdlineToolsPath)) { console.log('Installing new cmdline-tools.'); const sdkUrl = isOnMac ? CMDLINE_TOOLS_URL_MAC : CMDLINE_TOOLS_URL_LINUX; @@ -62,13 +63,13 @@ function installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndk yield io.mv(`${cmdlineToolsPath}/cmdline-tools`, `${cmdlineToolsPath}/latest`); } // add paths for commandline-tools and platform-tools - core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_SDK_ROOT}/platform-tools`); + core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_HOME}/platform-tools`); // set standard AVD path core.exportVariable('ANDROID_AVD_HOME', `${process.env.HOME}/.android/avd`); // accept all Android SDK licenses yield exec.exec(`sh -c \\"yes | sdkmanager --licenses > /dev/null"`); console.log('Installing latest build tools, platform tools, and platform.'); - yield exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiLevel}' > /dev/null"`); + yield exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools > /dev/null"`); console.log('Installing latest emulator.'); yield exec.exec(`sh -c \\"sdkmanager --install emulator --channel=${channelId} > /dev/null"`); if (emulatorBuild) { @@ -76,7 +77,7 @@ function installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndk // TODO find out the correct download URLs for all build ids const downloadUrlSuffix = Number(emulatorBuild.charAt(0)) > 6 ? `_x64-${emulatorBuild}` : `-${emulatorBuild}`; yield exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}${downloadUrlSuffix}.zip`); - yield exec.exec(`unzip -o -q emulator.zip -d ${process.env.ANDROID_SDK_ROOT}`); + yield exec.exec(`unzip -o -q emulator.zip -d ${process.env.ANDROID_HOME}`); yield io.rmRF('emulator.zip'); } console.log('Installing system images.'); diff --git a/package-lock.json b/package-lock.json index a48bf651a..b3c8b495c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4143,9 +4143,9 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "bin": { "json5": "lib/cli.js" @@ -5290,9 +5290,9 @@ } }, "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "dependencies": { "minimist": "^1.2.0" @@ -8679,9 +8679,9 @@ "dev": true }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, "kleur": { @@ -9481,9 +9481,9 @@ }, "dependencies": { "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "requires": { "minimist": "^1.2.0" diff --git a/renovate.json b/renovate.json new file mode 100644 index 000000000..39a2b6e9a --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base" + ] +} diff --git a/src/emulator-manager.ts b/src/emulator-manager.ts index 5fca23e82..4f6910472 100644 --- a/src/emulator-manager.ts +++ b/src/emulator-manager.ts @@ -1,13 +1,11 @@ import * as exec from '@actions/exec'; import * as fs from 'fs'; -const EMULATOR_BOOT_TIMEOUT_SECONDS = 600; - /** * Creates and launches a new AVD instance with the specified configurations. */ export async function launchEmulator( - apiLevel: number, + apiLevel: string, target: string, arch: string, profile: string, @@ -18,6 +16,7 @@ export async function launchEmulator( diskSize: string, avdName: string, forceAvdCreation: boolean, + emulatorBootTimeout: number, emulatorOptions: string, disableAnimations: boolean, disableSpellChecker: boolean, @@ -66,7 +65,7 @@ export async function launchEmulator( // start emulator console.log('Starting emulator.'); - await exec.exec(`sh -c \\"${process.env.ANDROID_SDK_ROOT}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], { + await exec.exec(`sh -c \\"${process.env.ANDROID_HOME}/emulator/emulator -avd "${avdName}" ${emulatorOptions} &"`, [], { listeners: { stderr: (data: Buffer) => { if (data.toString().includes('invalid command-line parameter')) { @@ -77,7 +76,7 @@ export async function launchEmulator( }); // wait for emulator to complete booting - await waitForDevice(); + await waitForDevice(emulatorBootTimeout); await exec.exec(`adb shell input keyevent 82`); if (disableAnimations) { @@ -114,11 +113,11 @@ export async function killEmulator(): Promise { /** * Wait for emulator to boot. */ -async function waitForDevice(): Promise { +async function waitForDevice(emulatorBootTimeout: number): Promise { let booted = false; let attempts = 0; const retryInterval = 2; // retry every 2 seconds - const maxAttempts = EMULATOR_BOOT_TIMEOUT_SECONDS / 2; + const maxAttempts = emulatorBootTimeout / 2; while (!booted) { try { let result = ''; diff --git a/src/input-validator.ts b/src/input-validator.ts index 7cf1e1b04..e52d7923b 100644 --- a/src/input-validator.ts +++ b/src/input-validator.ts @@ -4,6 +4,7 @@ export const VALID_ARCHS: Array = ['x86', 'x86_64', 'arm64-v8a']; export const VALID_CHANNELS: Array = ['stable', 'beta', 'dev', 'canary']; export function checkApiLevel(apiLevel: string): void { + if (apiLevel.startsWith('UpsideDownCake') || apiLevel === 'TiramisuPrivacySandbox') return; if (isNaN(Number(apiLevel)) || !Number.isInteger(Number(apiLevel))) { throw new Error(`Unexpected API level: '${apiLevel}'.`); } diff --git a/src/main.ts b/src/main.ts index 48827a912..0f21a2031 100644 --- a/src/main.ts +++ b/src/main.ts @@ -40,9 +40,8 @@ async function run() { } // API level of the platform and system image - const apiLevelInput = core.getInput('api-level', { required: true }); - checkApiLevel(apiLevelInput); - const apiLevel = Number(apiLevelInput); + const apiLevel = core.getInput('api-level', { required: true }); + checkApiLevel(apiLevel); console.log(`API level: ${apiLevel}`); // target of the system image @@ -90,6 +89,10 @@ async function run() { const forceAvdCreation = forceAvdCreationInput === 'true'; console.log(`force avd creation: ${forceAvdCreation}`); + // Emulator boot timeout seconds + const emulatorBootTimeout = parseInt(core.getInput('emulator-boot-timeout'), 10); + console.log(`Emulator boot timeout: ${emulatorBootTimeout}`); + // emulator options const emulatorOptions = core.getInput('emulator-options').trim(); console.log(`emulator options: ${emulatorOptions}`); @@ -206,6 +209,7 @@ async function run() { diskSize, avdName, forceAvdCreation, + emulatorBootTimeout, emulatorOptions, disableAnimations, disableSpellchecker, diff --git a/src/sdk-installer.ts b/src/sdk-installer.ts index 29526d7e1..b20541886 100644 --- a/src/sdk-installer.ts +++ b/src/sdk-installer.ts @@ -4,24 +4,25 @@ import * as io from '@actions/io'; import * as tc from '@actions/tool-cache'; import * as fs from 'fs'; -const BUILD_TOOLS_VERSION = '33.0.0'; -const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-8512546_latest.zip'; -const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-8512546_latest.zip'; +const BUILD_TOOLS_VERSION = '33.0.2'; +// SDK command-line tools 9.0 +const CMDLINE_TOOLS_URL_MAC = 'https://dl.google.com/android/repository/commandlinetools-mac-9477386_latest.zip'; +const CMDLINE_TOOLS_URL_LINUX = 'https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip'; /** * Installs & updates the Android SDK for the macOS platform, including SDK platform for the chosen API level, latest build tools, platform tools, Android Emulator, * and the system image for the chosen API level, CPU arch, and target. */ -export async function installAndroidSdk(apiLevel: number, target: string, arch: string, channelId: number, emulatorBuild?: string, ndkVersion?: string, cmakeVersion?: string): Promise { +export async function installAndroidSdk(apiLevel: string, target: string, arch: string, channelId: number, emulatorBuild?: string, ndkVersion?: string, cmakeVersion?: string): Promise { try { console.log(`::group::Install Android SDK`); const isOnMac = process.platform === 'darwin'; if (!isOnMac) { - await exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_SDK_ROOT} -R`); + await exec.exec(`sh -c \\"sudo chown $USER:$USER ${process.env.ANDROID_HOME} -R`); } - const cmdlineToolsPath = `${process.env.ANDROID_SDK_ROOT}/cmdline-tools`; + const cmdlineToolsPath = `${process.env.ANDROID_HOME}/cmdline-tools`; if (!fs.existsSync(cmdlineToolsPath)) { console.log('Installing new cmdline-tools.'); const sdkUrl = isOnMac ? CMDLINE_TOOLS_URL_MAC : CMDLINE_TOOLS_URL_LINUX; @@ -31,7 +32,7 @@ export async function installAndroidSdk(apiLevel: number, target: string, arch: } // add paths for commandline-tools and platform-tools - core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_SDK_ROOT}/platform-tools`); + core.addPath(`${cmdlineToolsPath}/latest:${cmdlineToolsPath}/latest/bin:${process.env.ANDROID_HOME}/platform-tools`); // set standard AVD path core.exportVariable('ANDROID_AVD_HOME', `${process.env.HOME}/.android/avd`); @@ -41,7 +42,7 @@ export async function installAndroidSdk(apiLevel: number, target: string, arch: console.log('Installing latest build tools, platform tools, and platform.'); - await exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools 'platforms;android-${apiLevel}' > /dev/null"`); + await exec.exec(`sh -c \\"sdkmanager --install 'build-tools;${BUILD_TOOLS_VERSION}' platform-tools > /dev/null"`); console.log('Installing latest emulator.'); await exec.exec(`sh -c \\"sdkmanager --install emulator --channel=${channelId} > /dev/null"`); @@ -51,7 +52,7 @@ export async function installAndroidSdk(apiLevel: number, target: string, arch: // TODO find out the correct download URLs for all build ids const downloadUrlSuffix = Number(emulatorBuild.charAt(0)) > 6 ? `_x64-${emulatorBuild}` : `-${emulatorBuild}`; await exec.exec(`curl -fo emulator.zip https://dl.google.com/android/repository/emulator-${isOnMac ? 'darwin' : 'linux'}${downloadUrlSuffix}.zip`); - await exec.exec(`unzip -o -q emulator.zip -d ${process.env.ANDROID_SDK_ROOT}`); + await exec.exec(`unzip -o -q emulator.zip -d ${process.env.ANDROID_HOME}`); await io.rmRF('emulator.zip'); } console.log('Installing system images.'); diff --git a/test-fixture/.gitignore b/test-fixture/.gitignore new file mode 100644 index 000000000..4ea5f9b4c --- /dev/null +++ b/test-fixture/.gitignore @@ -0,0 +1,8 @@ +.gradle +build/ +target/ +out/ +local.properties +.idea/ +*.iml +*.DS_Store diff --git a/test-fixture/app/build.gradle b/test-fixture/app/build.gradle index 82b4d4f50..8fe9a639a 100644 --- a/test-fixture/app/build.gradle +++ b/test-fixture/app/build.gradle @@ -3,7 +3,7 @@ apply plugin: 'kotlin-android' android { compileSdkVersion 33 - buildToolsVersion "33.0.0" + buildToolsVersion "33.0.2" defaultConfig { applicationId "com.example.testapp" @@ -15,18 +15,21 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { + compilerOptions { + jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'androidx.appcompat:appcompat:1.3.1' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + implementation 'androidx.appcompat:appcompat:1.6.1' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' } diff --git a/test-fixture/app/proguard-rules.pro b/test-fixture/app/proguard-rules.pro deleted file mode 100644 index f1b424510..000000000 --- a/test-fixture/app/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile diff --git a/test-fixture/build.gradle b/test-fixture/build.gradle index 346ab7a88..86735090e 100644 --- a/test-fixture/build.gradle +++ b/test-fixture/build.gradle @@ -1,14 +1,13 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.7.0' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.2.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'com.android.tools.build:gradle:7.4.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/test-fixture/gradle.properties b/test-fixture/gradle.properties index de0777f01..cadf77342 100644 --- a/test-fixture/gradle.properties +++ b/test-fixture/gradle.properties @@ -2,26 +2,11 @@ org.gradle.parallel=true org.gradle.configureondemand=true org.gradle.caching=true -# Enable Kotlin incremental compilation -kotlin.incremental=true - # Enable parallel tasks execution for Kotlin Gradle plugin kotlin.parallel.tasks.in.project=true # Kotlin code style kotlin.code.style=official -# Run kapt directly using Gradle workers -kapt.use.worker.api=true - -# Enable incremental annotation processor for KAPT -kapt.incremental.apt=true - -# Turn off AP discovery in compile path to enable compile avoidance -kapt.include.compile.classpath=false - -# Use R8 instead of ProGuard for code shrinking. -android.enableR8.fullMode=true - # Enable AndroidX android.useAndroidX=true diff --git a/test-fixture/gradle/wrapper/gradle-wrapper.jar b/test-fixture/gradle/wrapper/gradle-wrapper.jar index f6b961fd5..ccebba771 100644 Binary files a/test-fixture/gradle/wrapper/gradle-wrapper.jar and b/test-fixture/gradle/wrapper/gradle-wrapper.jar differ diff --git a/test-fixture/gradle/wrapper/gradle-wrapper.properties b/test-fixture/gradle/wrapper/gradle-wrapper.properties index 5969f534d..bdc9a83b1 100644 --- a/test-fixture/gradle/wrapper/gradle-wrapper.properties +++ b/test-fixture/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Nov 26 18:34:05 AEDT 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip diff --git a/test-fixture/gradlew b/test-fixture/gradlew index cccdd3d51..79a61d421 100755 --- a/test-fixture/gradlew +++ b/test-fixture/gradlew @@ -1,78 +1,129 @@ -#!/usr/bin/env sh +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -81,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -89,84 +140,105 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" fi +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + exec "$JAVACMD" "$@" diff --git a/test-fixture/gradlew.bat b/test-fixture/gradlew.bat index e95643d6a..6689b85be 100644 --- a/test-fixture/gradlew.bat +++ b/test-fixture/gradlew.bat @@ -1,4 +1,20 @@ -@if "%DEBUG%" == "" @echo off +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -9,19 +25,23 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +55,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,38 +65,26 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal