Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/configurationProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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") {
Expand Down
94 changes: 72 additions & 22 deletions src/launchCommand.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -17,19 +18,25 @@ enum shortenApproach {

const HELPFUL_NPE_VMARGS = "-XX:+ShowCodeDetailsInExceptionMessages";

export async function detectLaunchCommandStyle(config: vscode.DebugConfiguration): Promise<shortenApproach> {
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<shortenApproach> {
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\".",
Expand All @@ -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;
Expand All @@ -59,23 +69,63 @@ export async function addMoreHelpfulVMArgs(config: vscode.DebugConfiguration) {
}
}

function checkJavaVersion(javaExec: string): Promise<number> {
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<number> {
javaExec = javaExec || path.join(await getJavaHome(), "bin", "java");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have to differ java.exe vs java by platforms?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, running "java" and "java.exe" in shell both works in windows.

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<number> {
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<number> {
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 {
Expand Down