From 5e7ba5ad7dc644c420a2a279fe35daf4366712a7 Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Fri, 29 Dec 2017 10:08:47 +0200 Subject: [PATCH 1/2] Add check for JAVA 9 Currently Gradle cannot work with JAVA 9, so detect if it has been used and break the build for Android. Also the check will print warning when `tns doctor` is called. Also update submodule, where the following change is applied: Fix detection of Javac version The command `javac -version` prints result to stderr when JAVA 8 is used and to stdout when JAVA 9 is used. Current check in CLI uses the stderr output, so when JAVA 9 is installed it fails to detect the correct version. In order to support both JAVA 8 and JAVA 9, capture both stdout and stderr and get the version from there. Also remove unneeded check for Java version - we care about JAVA Compiler, which is included in JDK. --- lib/android-tools-info.ts | 9 +++++++-- lib/common | 2 +- lib/services/doctor-service.ts | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/android-tools-info.ts b/lib/android-tools-info.ts index 020b1b3dcd..9d4d03e02c 100644 --- a/lib/android-tools-info.ts +++ b/lib/android-tools-info.ts @@ -10,6 +10,7 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { private static REQUIRED_BUILD_TOOLS_RANGE_PREFIX = ">=23"; private static VERSION_REGEX = /((\d+\.){2}\d+)/; private static MIN_JAVA_VERSION = "1.8.0"; + private static MAX_JAVA_VERSION = "1.9.0"; private showWarningsAsErrors: boolean; private toolsInfo: IAndroidToolsInfoData; @@ -111,10 +112,14 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { + "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 " + this.$staticConfig.SYS_REQUIREMENTS_LINK; const matchingVersion = (installedJavaVersion || "").match(AndroidToolsInfo.VERSION_REGEX); - if (matchingVersion && matchingVersion[1]) { - if (semver.lt(matchingVersion[1], AndroidToolsInfo.MIN_JAVA_VERSION)) { + const installedJavaCompilerVersion = matchingVersion && matchingVersion[1]; + if (installedJavaCompilerVersion) { + if (semver.lt(installedJavaCompilerVersion, 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); + } else if (semver.gt(installedJavaCompilerVersion, AndroidToolsInfo.MAX_JAVA_VERSION)) { + hasProblemWithJavaVersion = true; + this.printMessage(`Javac version ${installedJavaVersion} is not supported. You have to install version ${AndroidToolsInfo.MIN_JAVA_VERSION}.`, additionalMessage); } } else { hasProblemWithJavaVersion = true; diff --git a/lib/common b/lib/common index 0d981032ba..487e70ea01 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 0d981032bad65fcbf429ac1e81b89f99442ca2ff +Subproject commit 487e70ea015feeb002016139536563795b0da83f diff --git a/lib/services/doctor-service.ts b/lib/services/doctor-service.ts index a03a52a888..d7979b1f14 100644 --- a/lib/services/doctor-service.ts +++ b/lib/services/doctor-service.ts @@ -104,9 +104,9 @@ class DoctorService implements IDoctorService { } const androidToolsIssues = this.$androidToolsInfo.validateInfo(); - const javaVersionIssue = await this.$androidToolsInfo.validateJavacVersion(sysInfo.javacVersion); + const javaCompilerVersionIssue = await this.$androidToolsInfo.validateJavacVersion(sysInfo.javacVersion); const pythonIssues = await this.validatePythonPackages(); - const doctorResult = result || androidToolsIssues || javaVersionIssue || pythonIssues; + const doctorResult = result || androidToolsIssues || javaCompilerVersionIssue || pythonIssues; if (!configOptions || configOptions.trackResult) { await this.$analyticsService.track("DoctorEnvironmentSetup", doctorResult ? "incorrect" : "correct"); From 31bd3a5632333f2501ea9f94427417137bcb3a13 Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Tue, 2 Jan 2018 23:31:11 +0200 Subject: [PATCH 2/2] Handle case when Javac version is a single number (9 for example) In some cases javac version is a single number, for example 9. In this case our validation fails to detect the correct version and to check if we support this version. In order to resolve this issue, use the `appendZeroesToVersion` method in order to make the versin semver valid. Change the return type of `validateJavacVersion` method - it does not require to return a Promise. Add unit tests for `validateJavacVersion` method. --- lib/android-tools-info.ts | 12 +-- lib/common | 2 +- lib/declarations.d.ts | 2 +- lib/services/android-project-service.ts | 2 +- lib/services/doctor-service.ts | 2 +- test/android-tools-info.ts | 112 ++++++++++++++++++++++++ test/stubs.ts | 2 +- 7 files changed, 124 insertions(+), 10 deletions(-) create mode 100644 test/android-tools-info.ts diff --git a/lib/android-tools-info.ts b/lib/android-tools-info.ts index 9d4d03e02c..9d92d71969 100644 --- a/lib/android-tools-info.ts +++ b/lib/android-tools-info.ts @@ -2,6 +2,7 @@ import * as path from "path"; import * as semver from "semver"; import { EOL } from "os"; import { cache } from "./common/decorators"; +import { appendZeroesToVersion } from './common/helpers'; export class AndroidToolsInfo implements IAndroidToolsInfo { private static ANDROID_TARGET_PREFIX = "android"; @@ -102,7 +103,7 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { return detectedErrors || !isAndroidHomeValid; } - public async validateJavacVersion(installedJavaVersion: string, options?: { showWarningsAsErrors: boolean }): Promise { + public validateJavacVersion(installedJavacVersion: string, options?: { showWarningsAsErrors: boolean }): boolean { let hasProblemWithJavaVersion = false; if (options) { this.showWarningsAsErrors = options.showWarningsAsErrors; @@ -111,15 +112,16 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { const additionalMessage = "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 " + this.$staticConfig.SYS_REQUIREMENTS_LINK; - const matchingVersion = (installedJavaVersion || "").match(AndroidToolsInfo.VERSION_REGEX); + + const matchingVersion = appendZeroesToVersion(installedJavacVersion || "", 3).match(AndroidToolsInfo.VERSION_REGEX); const installedJavaCompilerVersion = matchingVersion && matchingVersion[1]; if (installedJavaCompilerVersion) { if (semver.lt(installedJavaCompilerVersion, 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); - } else if (semver.gt(installedJavaCompilerVersion, AndroidToolsInfo.MAX_JAVA_VERSION)) { + this.printMessage(`Javac version ${installedJavacVersion} is not supported. You have to install at least ${AndroidToolsInfo.MIN_JAVA_VERSION}.`, additionalMessage); + } else if (semver.gte(installedJavaCompilerVersion, AndroidToolsInfo.MAX_JAVA_VERSION)) { hasProblemWithJavaVersion = true; - this.printMessage(`Javac version ${installedJavaVersion} is not supported. You have to install version ${AndroidToolsInfo.MIN_JAVA_VERSION}.`, additionalMessage); + this.printMessage(`Javac version ${installedJavacVersion} is not supported. You have to install version ${AndroidToolsInfo.MIN_JAVA_VERSION}.`, additionalMessage); } } else { hasProblemWithJavaVersion = true; diff --git a/lib/common b/lib/common index 487e70ea01..d3fa315437 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 487e70ea015feeb002016139536563795b0da83f +Subproject commit d3fa31543765bbd6c95b96464c421659fc13d8aa diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index e302f966b9..dc8cab466c 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -490,7 +490,7 @@ interface IAndroidToolsInfo { * @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 }): Promise; + validateJavacVersion(installedJavaVersion: string, options?: { showWarningsAsErrors: boolean }): boolean; /** * Validates if ANDROID_HOME environment variable is set correctly. diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 95d56fa403..73dfa4c7bd 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -138,7 +138,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject const javaCompilerVersion = await this.$sysInfo.getJavaCompilerVersion(); - await this.$androidToolsInfo.validateJavacVersion(javaCompilerVersion, { showWarningsAsErrors: true }); + this.$androidToolsInfo.validateJavacVersion(javaCompilerVersion, { showWarningsAsErrors: true }); await this.$androidToolsInfo.validateInfo({ showWarningsAsErrors: true, validateTargetSdk: true }); } diff --git a/lib/services/doctor-service.ts b/lib/services/doctor-service.ts index d7979b1f14..e71a825a98 100644 --- a/lib/services/doctor-service.ts +++ b/lib/services/doctor-service.ts @@ -104,7 +104,7 @@ class DoctorService implements IDoctorService { } const androidToolsIssues = this.$androidToolsInfo.validateInfo(); - const javaCompilerVersionIssue = await this.$androidToolsInfo.validateJavacVersion(sysInfo.javacVersion); + const javaCompilerVersionIssue = this.$androidToolsInfo.validateJavacVersion(sysInfo.javacVersion); const pythonIssues = await this.validatePythonPackages(); const doctorResult = result || androidToolsIssues || javaCompilerVersionIssue || pythonIssues; diff --git a/test/android-tools-info.ts b/test/android-tools-info.ts new file mode 100644 index 0000000000..f548462455 --- /dev/null +++ b/test/android-tools-info.ts @@ -0,0 +1,112 @@ +import { Yok } from "../lib/common/yok"; +import { AndroidToolsInfo } from "../lib/android-tools-info"; +import { EOL } from "os"; +import { format } from "util"; +import { assert } from "chai"; + +interface ITestData { + javacVersion: string; + expectedResult: boolean; + warnings?: string[]; +} + +describe("androidToolsInfo", () => { + let loggedWarnings: string[] = []; + let loggedMarkdownMessages: string[] = []; + const sysRequirementsLink = ""; + + const additionalMsg = "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 " + sysRequirementsLink; + + beforeEach(() => { + loggedWarnings = []; + loggedMarkdownMessages = []; + }); + + const createTestInjector = (): IInjector => { + const testInjector = new Yok(); + testInjector.register("childProcess", {}); + testInjector.register("errors", { + failWithoutHelp: (message: string, ...args: any[]): any => { + const loggedError = format(message, args); + throw new Error(loggedError); + } + }); + testInjector.register("fs", {}); + testInjector.register("hostInfo", {}); + testInjector.register("logger", { + warn: (...args: string[]): void => { + loggedWarnings.push(format.apply(null, args)); + }, + + printMarkdown: (...args: string[]): void => { + loggedMarkdownMessages.push(format.apply(null, args)); + } + }); + testInjector.register("options", {}); + testInjector.register("staticConfig", { + SYS_REQUIREMENTS_LINK: sysRequirementsLink + }); + return testInjector; + }; + + describe("validateJavacVersion", () => { + const testData: ITestData[] = [ + { + javacVersion: "1.8.0", + expectedResult: false + }, + { + javacVersion: "1.8.0_152", + expectedResult: false + }, + { + javacVersion: "9", + expectedResult: true, + warnings: ["Javac version 9 is not supported. You have to install version 1.8.0."] + }, + { + javacVersion: "9.0.1", + expectedResult: true, + warnings: ["Javac version 9.0.1 is not supported. You have to install version 1.8.0."] + }, + { + javacVersion: "1.7.0", + expectedResult: true, + warnings: ["Javac version 1.7.0 is not supported. You have to install at least 1.8.0."] + }, + { + javacVersion: "1.7.0_132", + expectedResult: true, + warnings: ["Javac version 1.7.0_132 is not supported. You have to install at least 1.8.0."] + }, + { + javacVersion: null, + expectedResult: true, + warnings: ["Error executing command 'javac'. Make sure you have installed The Java Development Kit (JDK) and set JAVA_HOME environment variable."] + } + ]; + + _.each(testData, ({ javacVersion, expectedResult, warnings }) => { + it(`returns ${expectedResult} when version is ${javacVersion}`, () => { + const testInjector = createTestInjector(); + const androidToolsInfo = testInjector.resolve(AndroidToolsInfo); + assert.deepEqual(androidToolsInfo.validateJavacVersion(javacVersion), expectedResult); + if (warnings && warnings.length) { + assert.deepEqual(loggedWarnings, warnings); + assert.deepEqual(loggedMarkdownMessages, [additionalMsg]); + } else { + assert.equal(loggedWarnings.length, 0); + assert.equal(loggedMarkdownMessages.length, 0); + } + }); + }); + + it("throws error when passing showWarningsAsErrors to true and javac is not installed", () => { + const testInjector = createTestInjector(); + const androidToolsInfo = testInjector.resolve(AndroidToolsInfo); + assert.throws(() => androidToolsInfo.validateJavacVersion(null, { showWarningsAsErrors: true }), "Error executing command 'javac'. Make sure you have installed The Java Development Kit (JDK) and set JAVA_HOME environment variable."); + }); + }); +}); diff --git a/test/stubs.ts b/test/stubs.ts index 2f97a9f7f4..2e32020b30 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -532,7 +532,7 @@ export class AndroidToolsInfoStub implements IAndroidToolsInfo { return true; } - public async validateJavacVersion(installedJavaVersion: string, options?: { showWarningsAsErrors: boolean }): Promise { + public validateJavacVersion(installedJavaVersion: string, options?: { showWarningsAsErrors: boolean }): boolean { return true; }