From 8e01a077bf462cb00789947a6561a36539a649c0 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Tue, 15 Dec 2020 17:32:53 +0800 Subject: [PATCH] Find Java version from release file --- src/configurationProvider.ts | 9 ++-- src/launchCommand.ts | 94 +++++++++++++++++++++++++++--------- 2 files changed, 77 insertions(+), 26 deletions(-) diff --git a/src/configurationProvider.ts b/src/configurationProvider.ts index 0899545d..d26a9013 100644 --- a/src/configurationProvider.ts +++ b/src/configurationProvider.ts @@ -12,7 +12,7 @@ import { buildWorkspace } from "./build"; import { populateStepFilters, substituteFilterVariables } from "./classFilter"; import * as commands from "./commands"; import * as lsPlugin from "./languageServerPlugin"; -import { addMoreHelpfulVMArgs, detectLaunchCommandStyle, validateRuntime } from "./launchCommand"; +import { addMoreHelpfulVMArgs, getJavaVersion, getShortenApproachForCLI, validateRuntimeCompatibility } from "./launchCommand"; import { logger, Type } from "./logger"; import { mainClassPicker } from "./mainClassPicker"; import { resolveJavaProcess } from "./processPicker"; @@ -271,17 +271,18 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration // Populate the class filters to the debug configuration. await populateStepFilters(config); + const targetJavaVersion: number = await getJavaVersion(config.javaExec); // Auto add '--enable-preview' vmArgs if the java project enables COMPILER_PB_ENABLE_PREVIEW_FEATURES flag. if (await lsPlugin.detectPreviewFlag(config.mainClass, config.projectName)) { config.vmArgs = (config.vmArgs || "") + " --enable-preview"; - validateRuntime(config); + validateRuntimeCompatibility(targetJavaVersion); } // Add more helpful vmArgs. - await addMoreHelpfulVMArgs(config); + await addMoreHelpfulVMArgs(config, targetJavaVersion); if (!config.shortenCommandLine || config.shortenCommandLine === "auto") { - config.shortenCommandLine = await detectLaunchCommandStyle(config); + config.shortenCommandLine = await getShortenApproachForCLI(config, targetJavaVersion); } if (process.platform === "win32" && config.console !== "internalConsole") { diff --git a/src/launchCommand.ts b/src/launchCommand.ts index 00ccf2c8..75a6d201 100644 --- a/src/launchCommand.ts +++ b/src/launchCommand.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. import * as cp from "child_process"; +import * as fs from "fs"; import * as _ from "lodash"; import * as path from "path"; import * as vscode from "vscode"; @@ -17,19 +18,25 @@ enum shortenApproach { const HELPFUL_NPE_VMARGS = "-XX:+ShowCodeDetailsInExceptionMessages"; -export async function detectLaunchCommandStyle(config: vscode.DebugConfiguration): Promise { - const javaExec: string = config.javaExec || path.join(await getJavaHome(), "bin", "java"); - const javaVersion = await checkJavaVersion(javaExec); - const recommendedShortenApproach = javaVersion <= 8 ? shortenApproach.jarmanifest : shortenApproach.argfile; +/** + * Returns the recommended approach to shorten the command line length. + * @param config the launch configuration + * @param runtimeVersion the target runtime version + */ +export async function getShortenApproachForCLI(config: vscode.DebugConfiguration, runtimeVersion: number): Promise { + const recommendedShortenApproach = runtimeVersion <= 8 ? shortenApproach.jarmanifest : shortenApproach.argfile; return (await shouldShortenIfNecessary(config)) ? recommendedShortenApproach : shortenApproach.none; } -export async function validateRuntime(config: vscode.DebugConfiguration) { +/** + * Validates whether the specified runtime version could be supported by the Java tooling. + * @param runtimeVersion the target runtime version + */ +export async function validateRuntimeCompatibility(runtimeVersion: number) { try { const platformSettings = await fetchPlatformSettings(); if (platformSettings && platformSettings.latestSupportedJavaVersion) { const latestSupportedVersion = flattenMajorVersion(platformSettings.latestSupportedJavaVersion); - const runtimeVersion = await checkJavaVersion(config.javaExec || path.join(await getJavaHome(), "bin", "java")); if (latestSupportedVersion < runtimeVersion) { showWarningMessageWithTroubleshooting({ message: "The compiled classes are not compatible with the runtime JDK. To mitigate the issue, please refer to \"Learn More\".", @@ -42,11 +49,14 @@ export async function validateRuntime(config: vscode.DebugConfiguration) { } } -export async function addMoreHelpfulVMArgs(config: vscode.DebugConfiguration) { +/** + * Add some helpful VM arguments to the launch configuration based on the target runtime version. + * @param config the launch configuration + * @param runtimeVersion the target runtime version + */ +export async function addMoreHelpfulVMArgs(config: vscode.DebugConfiguration, runtimeVersion: number) { try { - const javaExec = config.javaExec || path.join(await getJavaHome(), "bin", "java"); - const version = await checkJavaVersion(javaExec); - if (version >= 14) { + if (runtimeVersion >= 14) { // JEP-358: https://openjdk.java.net/jeps/358 if (config.vmArgs && config.vmArgs.indexOf(HELPFUL_NPE_VMARGS) >= 0) { return; @@ -59,23 +69,63 @@ export async function addMoreHelpfulVMArgs(config: vscode.DebugConfiguration) { } } -function checkJavaVersion(javaExec: string): Promise { - return new Promise((resolve, _reject) => { - cp.execFile(javaExec, ["-version"], {}, (_error, _stdout, stderr) => { - const javaVersion = parseMajorVersion(stderr); - resolve(javaVersion); - }); - }); +/** + * Returns the target runtime version. If the javaExec is not specified, then return the current Java version + * that the Java tooling used. + * @param javaExec the path of the Java executable + */ +export async function getJavaVersion(javaExec: string): Promise { + javaExec = javaExec || path.join(await getJavaHome(), "bin", "java"); + let javaVersion = await checkVersionInReleaseFile(path.resolve(javaExec, "..", "..")); + if (!javaVersion) { + javaVersion = await checkVersionByCLI(javaExec); + } + return javaVersion; } -function parseMajorVersion(content: string): number { - const regexp = /version "(.*)"/g; - const match = regexp.exec(content); - if (!match) { +async function checkVersionInReleaseFile(javaHome: string): Promise { + if (!javaHome) { return 0; } + const releaseFile = path.join(javaHome, "release"); + if (!await fs.existsSync(releaseFile)) { + return 0; + } + + try { + const content = fs.readFileSync(releaseFile); + const regexp = /^JAVA_VERSION="(.*)"/gm; + const match = regexp.exec(content.toString()); + if (!match) { + return 0; + } + const majorVersion = flattenMajorVersion(match[1]); + return majorVersion; + } catch (error) { + // ignore + } + + return 0; +} - return flattenMajorVersion(match[1]); +/** + * Get version by parsing `JAVA_HOME/bin/java -version` + */ +async function checkVersionByCLI(javaExec: string): Promise { + if (!javaExec) { + return 0; + } + return new Promise((resolve) => { + cp.execFile(javaExec, ["-version"], {}, (_error, _stdout, stderr) => { + const regexp = /version "(.*)"/g; + const match = regexp.exec(stderr); + if (!match) { + return resolve(0); + } + const javaVersion = flattenMajorVersion(match[1]); + resolve(javaVersion); + }); + }); } function flattenMajorVersion(version: string): number {