From b8a305047c1a7ccd2ed4663b5ff2612587026360 Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Wed, 9 Mar 2016 12:22:47 +0200 Subject: [PATCH] Verify JAVA_HOME is set correctly Our system requirements point that JAVA_HOME must be set, but `tns doctor` is not strict about this. With version 1.7.0 of the runtime, `jarsigner` from $JAVA_HOME/bin/jarsigner will be called explicitly, so we must be sure JAVA_HOME is correct. `jarsigner` does not exist in JRE, so add logic to call it in order to verify that the currently set JAVA_HOME is correct. Validate the information in the following order: 1) Check if JAVA_HOME is set. In case it is not, you cannot build for Android (`tns build android` will fail before starting). 2) Check if JAVA_HOME is correct by executing JAVA_HOME/bin/jarsigner - in case it does not exist - fail 3) Check if the version of the JAVA is correct by calling JAVA_HOME/bin/javac -version and compare it with min required one. In case it's lower - fail. `tns doctor` will print warning in case any of the above check fails. `tns build android` and any other Android related build operations will fail. --- lib/android-tools-info.ts | 57 ++++++++++++++++++++----- lib/declarations.ts | 6 +-- lib/services/android-project-service.ts | 2 +- lib/services/doctor-service.ts | 2 +- 4 files changed, 51 insertions(+), 16 deletions(-) diff --git a/lib/android-tools-info.ts b/lib/android-tools-info.ts index 9b2b794cdf..afbf5eeb54 100644 --- a/lib/android-tools-info.ts +++ b/lib/android-tools-info.ts @@ -156,30 +156,65 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { }).future()(); } - public validateJavacVersion(installedJavaVersion: string, options?: {showWarningsAsErrors: boolean}): IFuture { + public validateJava(javacVersion: string, options?: {showWarningsAsErrors: boolean}): IFuture { return ((): boolean => { - let hasProblemWithJavaVersion = false; if(options) { this.showWarningsAsErrors = options.showWarningsAsErrors; } - let additionalMessage = "You will not be able to build your projects for Android." + EOL + + let helpfulMessage = "You will not be able to build your projects for Android." + EOL + "To be able to build for Android, verify that you have installed The Java Development Kit (JDK) and configured it according to system requirements as" + EOL + " described in https://github.com/NativeScript/nativescript-cli#system-requirements."; - let matchingVersion = (installedJavaVersion || "").match(AndroidToolsInfo.VERSION_REGEX); - if(matchingVersion && matchingVersion[1]) { - if(semver.lt(matchingVersion[1], AndroidToolsInfo.MIN_JAVA_VERSION)) { - hasProblemWithJavaVersion = true; - this.printMessage(`Javac version ${installedJavaVersion} is not supported. You have to install at least ${AndroidToolsInfo.MIN_JAVA_VERSION}.`, additionalMessage); + + let hasProblemWithJavaHome = this.validateJavaHome(helpfulMessage).wait(); + let hasProblemWithJavaVersion: boolean; + if(!hasProblemWithJavaHome) { + hasProblemWithJavaVersion = this.validateJavacVersion(javacVersion, helpfulMessage); + } + + return hasProblemWithJavaHome || hasProblemWithJavaVersion; + }).future()(); + } + + private validateJavaHome(helpfulMessage: string): IFuture { + return ((): boolean => { + let hasProblemWithJavaHome = false; + let javaHome = process.env.JAVA_HOME; + + if(javaHome) { + // validate jarsigner as it does not exist in JRE, but is mandatory for JDK and Android Runtime + let jarSigner = path.join(javaHome, "bin", "jarsigner"); + let childProcessResult = this.$childProcess.spawnFromEvent(jarSigner, [], "close", {}, { throwError: false }).wait(); + this.$logger.trace(`Result of calling jarsigner from path: ${jarSigner}:`, childProcessResult); + if(childProcessResult.stderr || childProcessResult.exitCode !== 0) { + hasProblemWithJavaHome = true; + this.printMessage("JAVA_HOME environment variable points to incorrect path. Make sure it points to the installation directory of JDK.", helpfulMessage); } } else { - hasProblemWithJavaVersion = true; - this.printMessage("Error executing command 'javac'. Make sure you have installed The Java Development Kit (JDK) and set JAVA_HOME environment variable.", additionalMessage); + hasProblemWithJavaHome = true; + this.printMessage("JAVA_HOME environment variable is not set.", helpfulMessage); } - return hasProblemWithJavaVersion; + return hasProblemWithJavaHome; }).future()(); } + private validateJavacVersion(installedJavaVersion: string, helpfulMessage: string): boolean { + let hasProblemWithJavaVersion = false; + let matchingVersion = (installedJavaVersion || "").match(AndroidToolsInfo.VERSION_REGEX); + if(matchingVersion && matchingVersion[1]) { + if(semver.lt(matchingVersion[1], AndroidToolsInfo.MIN_JAVA_VERSION)) { + hasProblemWithJavaVersion = true; + this.printMessage(`Javac version ${installedJavaVersion} is not supported. You have to install at least ${AndroidToolsInfo.MIN_JAVA_VERSION}.`, helpfulMessage); + } + } else { + hasProblemWithJavaVersion = true; + this.printMessage("Error executing command 'javac'. Make sure you have installed The Java Development Kit (JDK) and set JAVA_HOME environment variable.", helpfulMessage); + } + + return hasProblemWithJavaVersion; + } + public getPathToAdbFromAndroidHome(): IFuture { return (() => { if(this.androidHome) { diff --git a/lib/declarations.ts b/lib/declarations.ts index bd2a9dc91c..581830a2be 100644 --- a/lib/declarations.ts +++ b/lib/declarations.ts @@ -160,12 +160,12 @@ interface IAndroidToolsInfo { validateInfo(options?: {showWarningsAsErrors: boolean, validateTargetSdk: boolean}): IFuture; /** - * Validates the information about required JAVA version. - * @param {string} installedJavaVersion The JAVA version that will be checked. + * Validates the information about required JAVA version and JAVA_HOME. + * @param {string} javacVersion The JAVA version that will be checked. * @param {any} options Defines if the warning messages should treated as error. * @return {boolean} True if there are detected issues, false otherwise. */ - validateJavacVersion(installedJavaVersion: string, options?: {showWarningsAsErrors: boolean}): IFuture; + validateJava(javacVersion: string, options?: {showWarningsAsErrors: boolean}): IFuture; /** * Returns the path to `android` executable. It should be `$ANDROID_HOME/tools/android`. diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 777db27d25..ebd521ed9f 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -88,7 +88,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject // this call will fail in case `android` is not set correctly. this.$androidToolsInfo.getPathToAndroidExecutable({showWarningsAsErrors: true}).wait(); - this.$androidToolsInfo.validateJavacVersion(this.$sysInfo.getSysInfo(path.join(__dirname, "..", "..", "package.json")).wait().javacVersion, {showWarningsAsErrors: true}).wait(); + this.$androidToolsInfo.validateJava(this.$sysInfo.getSysInfo(path.join(__dirname, "..", "..", "package.json")).wait().javacVersion, {showWarningsAsErrors: true}).wait(); }).future()(); } diff --git a/lib/services/doctor-service.ts b/lib/services/doctor-service.ts index edb32b1a24..3a66f3d767 100644 --- a/lib/services/doctor-service.ts +++ b/lib/services/doctor-service.ts @@ -87,7 +87,7 @@ class DoctorService implements IDoctorService { } let androidToolsIssues = this.$androidToolsInfo.validateInfo().wait(); - let javaVersionIssue = this.$androidToolsInfo.validateJavacVersion(sysInfo.javacVersion).wait(); + let javaVersionIssue = this.$androidToolsInfo.validateJava(sysInfo.javacVersion).wait(); return result || androidToolsIssues || javaVersionIssue; }