From e87415f7bbe5db3df4a48744ee2da42ba373bf0c Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Fri, 24 Mar 2017 00:15:44 +0200 Subject: [PATCH] Fix Android builds when gradle args and project dir have spaces In case you try building application, it will fail in case the following conditions are applied: * build on Windows * build for Android * build in release * path to project dir has spaces * path to certificate (keystore) has spaces OR the certificate(keystore) name itself has spaces OR the password has spaces The problem is in Node.js's `child_process` when `spawn` is used - on Windows it has issues when both the command (in our case this is the FULL PATH to gradlew.bat executable in platforms dir of the project) and ANY of the arguments passed to `child_process's spawn` have spaces. As we are also setting the current working directory of the spawned process to be the path to `/platforms/android`, change the `command` passed to `spawn` to be just `gradlew`. This way we'll never have spaces in the command and we'll not hit this issue. --- lib/services/android-project-service.ts | 40 ++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 8baaa94d97..a3fb160f66 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -6,6 +6,7 @@ import * as projectServiceBaseLib from "./platform-project-service-base"; import { DeviceAndroidDebugBridge } from "../common/mobile/android/device-android-debug-bridge"; import { EOL } from "os"; import { Configurations } from "../common/constants"; +import { SpawnOptions } from "child_process"; export class AndroidProjectService extends projectServiceBaseLib.PlatformProjectServiceBase implements IPlatformProjectService { private static VALUES_DIRNAME = "values"; @@ -253,18 +254,15 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject buildOptions.unshift("--stacktrace"); buildOptions.unshift("--debug"); } + buildOptions.unshift("buildapk"); - let gradleBin = path.join(projectRoot, "gradlew"); - if (this.$hostInfo.isWindows) { - gradleBin += ".bat"; // cmd command line parsing rules are weird. Avoid issues with quotes. See https://github.com/apache/cordova-android/blob/master/bin/templates/cordova/lib/builders/GradleBuilder.js for another approach - } this.$childProcess.on(constants.BUILD_OUTPUT_EVENT_NAME, (data: any) => { this.emit(constants.BUILD_OUTPUT_EVENT_NAME, data); }); - await this.spawn(gradleBin, - buildOptions, - { stdio: buildConfig.buildOutputStdio || "inherit", cwd: this.getPlatformData(projectData).projectRoot }, + + await this.executeGradleCommand(this.getPlatformData(projectData).projectRoot, buildOptions, + { stdio: buildConfig.buildOutputStdio || "inherit" }, { emitOptions: { eventName: constants.BUILD_OUTPUT_EVENT_NAME }, throwError: true }); } else { this.$errors.failWithoutHelp("Cannot complete build because this project is ANT-based." + EOL + @@ -417,24 +415,13 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } public stopServices(projectRoot: string): Promise { - let gradleBin = path.join(projectRoot, "gradlew"); - if (this.$hostInfo.isWindows) { - gradleBin += ".bat"; - } - - return this.$childProcess.spawnFromEvent(gradleBin, ["--stop", "--quiet"], "close", { stdio: "inherit", cwd: projectRoot }); + return this.executeGradleCommand(projectRoot, ["--stop", "--quiet"]); } public async cleanProject(projectRoot: string, projectData: IProjectData): Promise { const buildOptions = this.getBuildOptions({ release: false }, projectData); buildOptions.unshift("clean"); - - let gradleBin = path.join(projectRoot, "gradlew"); - if (this.$hostInfo.isWindows) { - gradleBin += ".bat"; - } - - await this.spawn(gradleBin, buildOptions, { stdio: "inherit", cwd: this.getPlatformData(projectData).projectRoot }); + await this.executeGradleCommand(projectRoot, buildOptions); } public async cleanDeviceTempFolder(deviceIdentifier: string, projectData: IProjectData): Promise { @@ -504,6 +491,19 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject return versionInManifest; } + + private async executeGradleCommand(projectRoot: string, gradleArgs: string[], childProcessOpts?: SpawnOptions, spawnFromEventOptions?: ISpawnFromEventOptions): Promise { + const gradlew = this.$hostInfo.isWindows ? "gradlew.bat" : "./gradlew"; + + childProcessOpts = childProcessOpts || {}; + childProcessOpts.cwd = childProcessOpts.cwd || projectRoot; + childProcessOpts.stdio = childProcessOpts.stdio || "inherit"; + + return await this.spawn(gradlew, + gradleArgs, + childProcessOpts, + spawnFromEventOptions); + } } $injector.register("androidProjectService", AndroidProjectService);