From b19236ec4dcdc4bfa1efe35e86d3eecb526058fa Mon Sep 17 00:00:00 2001 From: Richard Willis Date: Tue, 28 Apr 2020 21:03:24 +0100 Subject: [PATCH 1/9] test --- .../java/com/github/badsyntax/gradletasks/GradleTasksUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/tasks-server/src/main/java/com/github/badsyntax/gradletasks/GradleTasksUtil.java b/tasks-server/src/main/java/com/github/badsyntax/gradletasks/GradleTasksUtil.java index dd8bece38..19a17af42 100644 --- a/tasks-server/src/main/java/com/github/badsyntax/gradletasks/GradleTasksUtil.java +++ b/tasks-server/src/main/java/com/github/badsyntax/gradletasks/GradleTasksUtil.java @@ -65,6 +65,7 @@ public final void onOutputChanged(ByteArrayOutputStream outputMessage) { } } }).setColorOutput(false); + rootProjectBuilder.withArguments("-Dgradle.user.home=testuserhome") GradleProject gradleProject = buildProject(rootProjectBuilder.get()); GradleBuild gradleBuild = GradleBuild.newBuilder().setProject(gradleProject).build(); GetBuildResult result = GetBuildResult.newBuilder().setBuild(gradleBuild).build(); From 7a21824db2f75bbb1f7fc11693f6d3d36f7496e8 Mon Sep 17 00:00:00 2001 From: Richard Willis Date: Wed, 29 Apr 2020 08:30:26 +0100 Subject: [PATCH 2/9] Add info on env vars --- README.md | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 16321d161..60de4c0d6 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,11 @@ Run Gradle tasks in VS Code. ## Features -This extension provides a UI layer over Gradle builds. It shows Gradle projects and tasks and allows you to run tasks within the context of the editor. +This extension provides a visual interface for your Gradle build. You can view Gradle projects and run Gradle tasks. This extension supports whatever Gradle supports and is language/project agnostic, but it can work nicely alongside other extensions like the [Java language support extension](https://github.com/redhat-developer/vscode-java). - 👉 [All Features](./FEATURES.md) -- 👉 [Architecture Overview](./ARCHITECTURE.md) ## Requirements @@ -38,12 +37,28 @@ This extension contributes the following settings: This extension supports the following settings: - `java.home`: Absolute path to JDK home folder used to launch the gradle daemons. (Contributed by [vscode-java](https://github.com/redhat-developer/vscode-java).) +- `java.import.gradle.home`: Setting for GRADLE_HOME. (Contributed by [vscode-java](https://github.com/redhat-developer/vscode-java).) + +## Supported Environment Variables + +Most of the standard Java & Gradle environment variables are supported: + +- `JAVE_HOME` (overridden by `java.home`) +- `GRADLE_USER_HOME` (overridden by `java.import.gradle.home`) + +### Setting Project Environment Variables -## Usage +You can use an environment manager like [direnv](https://direnv.net/) to set project specific environment variables, or set the variables in the terminal settings within `.vscode/settings.json`, for example: -Open a Gradle project to use the extension. The extension first starts a process to discover tasks, and progress for this process is reported in the statusbar. Once tasks are discovered, a "Gradle Tasks" view is displayed in the explorer view, where you can view the Gradle project & task hierarchy and run specific tasks. You can also run Gradle tasks via vscode tasks by executing the "Run Task" command from the Command Palette and choosing a Gradle task. +```json +{ + "terminal.integrated.env.osx": { + "GRADLE_USER_HOME": "${workspaceFolder}/.gradle" + } +} +``` -### Debugging JavaExec Tasks +## Debugging Debugging JavaExec Tasks ![Debug Screencast](images/debug-screencast.gif) @@ -63,7 +78,7 @@ To enable this feature you need to specify which tasks can be debugged within yo You should now see a `debug` command next to the `run` command in the Gradle Tasks view. The `debug` command will start the Gradle task with [jdwp](https://docs.oracle.com/en/java/javase/11/docs/specs/jpda/conninv.html#oracle-vm-invocation-options) `jvmArgs` and start the vscode Java debugger. -#### Debugging Limitations +### Debugging Limitations You'll need to remove any `jdwp` options that might have been set in your task configuration (eg via `jvmArgs`). @@ -160,10 +175,6 @@ The reason for the incompatibility is due to the extensions providing the same t -## Contributing - -Refer to [CONTRIBUTING.md](./CONTRIBUTING.md) for instructions on how to run the project. - ## Support For general support queries, use the [#gradle-tasks](https://vscode-dev-community.slack.com/archives/C011NUFTHLM) channel in the [slack development community workspace](https://aka.ms/vscode-dev-community), or @@ -171,10 +182,17 @@ For general support queries, use the [#gradle-tasks](https://vscode-dev-communit - 👉 [Submit a bug report](https://github.com/badsyntax/vscode-gradle/issues/new?assignees=badsyntax&labels=bug&template=bug_report.md&title=) - 👉 [Submit a feature request](https://github.com/badsyntax/vscode-gradle/issues/new?assignees=badsyntax&labels=enhancement&template=feature_request.md&title=) +## Contributing + +Refer to [CONTRIBUTING.md](./CONTRIBUTING.md) for instructions on how to run the project. + +- 👉 [Architecture Overview](./ARCHITECTURE.md) + ## Credits - Originally forked from [Cazzar/vscode-gradle](https://github.com/Cazzar/vscode-gradle) - Inspired by the built-in [npm extension](https://github.com/microsoft/vscode/tree/master/extensions/npm) +- Thanks to all who have submitted bug reports and feedback 👍 ## Release Notes From 6f99bbfa79d49e01ce76f04434328ffb1d80ecd6 Mon Sep 17 00:00:00 2001 From: Richard Willis Date: Wed, 29 Apr 2020 08:30:36 +0100 Subject: [PATCH 3/9] Add support for custom gradle user home setting --- extension/src/client.ts | 27 +++++++- extension/src/config.ts | 6 ++ extension/src/logger.ts | 16 ++--- extension/src/tasks.ts | 6 +- proto/gradle_tasks.proto | 22 +++++- .../gradletasks/GradleTasksService.java | 5 +- .../gradletasks/GradleTasksUtil.java | 67 +++++++++++++------ 7 files changed, 113 insertions(+), 36 deletions(-) diff --git a/extension/src/client.ts b/extension/src/client.ts index 319073e4c..d3a011ea8 100644 --- a/extension/src/client.ts +++ b/extension/src/client.ts @@ -17,6 +17,7 @@ import { Cancelled, GradleBuild, CancelRunTasksRequest, + Environment, } from './proto/gradle_tasks_pb'; import { GradleTasksClient as GrpcClient } from './proto/gradle_tasks_grpc_pb'; @@ -94,7 +95,10 @@ export class GradleTasksClient implements vscode.Disposable { } } - public async getBuild(projectFolder: string): Promise { + public async getBuild( + projectFolder: string, + gradleHome: string | null + ): Promise { this.statusBarItem.text = localize( 'client.refreshingTasks', '{0} Gradle: Refreshing Tasks', @@ -103,6 +107,9 @@ export class GradleTasksClient implements vscode.Disposable { this.statusBarItem.show(); const request = new GetBuildRequest(); request.setProjectDir(projectFolder); + if (gradleHome) { + request.setGradleHome(gradleHome); + } const getBuildStream = this.grpcClient!.getBuild(request); try { return await new Promise((resolve, reject) => { @@ -122,6 +129,9 @@ export class GradleTasksClient implements vscode.Disposable { case GetBuildReply.KindCase.GET_BUILD_RESULT: build = getBuildReply.getGetBuildResult()!.getBuild(); break; + case GetBuildReply.KindCase.ENVIRONMENT: + this.handleGetBuildEnvironment(getBuildReply.getEnvironment()!); + break; } }) .on('error', reject) @@ -148,6 +158,7 @@ export class GradleTasksClient implements vscode.Disposable { task: vscode.Task, args: string[] = [], javaDebugPort: number | null, + gradleHome: string | null, onOutput: (output: Output) => void ): Promise { this.statusBarItem.show(); @@ -155,10 +166,13 @@ export class GradleTasksClient implements vscode.Disposable { request.setProjectDir(projectFolder); request.setTask(task.definition.script); request.setJavaDebug(task.definition.javaDebug); + request.setArgsList(args); if (javaDebugPort !== null) { request.setJavaDebugPort(javaDebugPort); } - request.setArgsList(args); + if (gradleHome) { + request.setGradleHome(gradleHome); + } const runTaskStream = this.grpcClient!.runTask(request); try { await new Promise((resolve, reject) => { @@ -303,6 +317,15 @@ export class GradleTasksClient implements vscode.Disposable { } } + private handleGetBuildEnvironment(environment: Environment): void { + const javaEnv = environment.getJavaEnvironment()!; + const gradleEnv = environment.getGradleEnvironment()!; + logger.info('Java Home:', javaEnv.getJavaHome()); + logger.info('JVM Args:', javaEnv.getJvmArgsList().join(',')); + logger.info('Gradle User Home:', gradleEnv.getGradleUserHome()); + logger.info('Gradle Version:', gradleEnv.getGradleVersion()); + } + private handleRunTaskCancelled = ( task: vscode.Task, cancelled: Cancelled diff --git a/extension/src/config.ts b/extension/src/config.ts index 590c0987f..516b5c5c2 100644 --- a/extension/src/config.ts +++ b/extension/src/config.ts @@ -24,6 +24,12 @@ export function getConfigJavaHome(): string | null { .get('home', null); } +export function getConfigImportGradleHome(): string | null { + return vscode.workspace + .getConfiguration('java') + .get('import.gradle.home', null); +} + export function getConfigIsDebugEnabled(): boolean { return vscode.workspace .getConfiguration('gradle') diff --git a/extension/src/logger.ts b/extension/src/logger.ts index 756c1c86e..f305a2e28 100644 --- a/extension/src/logger.ts +++ b/extension/src/logger.ts @@ -22,21 +22,21 @@ export class Logger { return `[${type}] ${message}`; } - public info(message: string): void { - this.log(message, 'info'); + public info(...messages: string[]): void { + this.log(messages.join(' '), 'info'); } - public warning(message: string): void { - this.log(message, 'warning'); + public warning(...messages: string[]): void { + this.log(messages.join(' '), 'warning'); } - public error(message: string): void { - this.log(message, 'error'); + public error(...messages: string[]): void { + this.log(messages.join(' '), 'error'); } - public debug(message: string): void { + public debug(...messages: string[]): void { if (getConfigIsDebugEnabled() || isTest()) { - this.log(message, 'debug'); + this.log(messages.join(' '), 'debug'); } } diff --git a/extension/src/tasks.ts b/extension/src/tasks.ts index fb6ff218d..b6a2e3fd2 100644 --- a/extension/src/tasks.ts +++ b/extension/src/tasks.ts @@ -13,6 +13,7 @@ import { ConfigTaskPresentationOptionsPanelKind, ConfigTaskPresentationOptions, getConfigTaskPresentationOptions, + getConfigImportGradleHome, } from './config'; import { logger } from './logger'; import { GradleTasksClient } from './client'; @@ -216,7 +217,8 @@ export class GradleTaskProvider implements vscode.TaskProvider { private async getGradleBuild( projectFolder: vscode.WorkspaceFolder ): Promise { - return await this.client?.getBuild(projectFolder.uri.fsPath); + const gradleHome = getConfigImportGradleHome(); + return await this.client?.getBuild(projectFolder.uri.fsPath, gradleHome); } private getVSCodeTasksFromGradleProject( @@ -399,11 +401,13 @@ class CustomBuildTaskTerminal implements vscode.Pseudoterminal { try { const javaDebugEnabled = this.task.definition.javaDebug; const javaDebugPort = javaDebugEnabled ? await getPort() : null; + const gradleHome = getConfigImportGradleHome(); const runTask = this.client.runTask( this.projectFolder, this.task, args, javaDebugPort, + gradleHome, (output: Output): void => { this.handleOutput(output.getMessage().trim()); } diff --git a/proto/gradle_tasks.proto b/proto/gradle_tasks.proto index 0421c323d..ef085f41d 100644 --- a/proto/gradle_tasks.proto +++ b/proto/gradle_tasks.proto @@ -20,7 +20,10 @@ message CancelGetBuildsReply { string message = 1; } message GetTasksRequest { string project_dir = 1; } -message GetBuildRequest { string project_dir = 1; } +message GetBuildRequest { + string project_dir = 1; + string gradle_home = 2; + } message GetBuildReply { oneof kind { @@ -28,6 +31,7 @@ message GetBuildReply { Progress progress = 2; Output output = 3; Cancelled cancelled = 4; + Environment environment = 5; } } @@ -47,6 +51,7 @@ message RunTaskRequest { repeated string args = 3; bool java_debug = 4; int32 java_debug_port = 5; + string gradle_home = 6; } message RunTaskResult { @@ -105,6 +110,21 @@ message Cancelled { message Progress { string message = 1; } +message Environment { + JavaEnvironment java_environment = 1; + GradleEnvironment gradle_environment = 2; +} + +message JavaEnvironment { + string java_home = 1; + repeated string jvm_args = 2; +} + +message GradleEnvironment { + string gradle_user_home = 1; + string gradle_version = 2; +} + message Output { enum OutputType { STDERR = 0; diff --git a/tasks-server/src/main/java/com/github/badsyntax/gradletasks/GradleTasksService.java b/tasks-server/src/main/java/com/github/badsyntax/gradletasks/GradleTasksService.java index b74a33de0..9a1283c87 100644 --- a/tasks-server/src/main/java/com/github/badsyntax/gradletasks/GradleTasksService.java +++ b/tasks-server/src/main/java/com/github/badsyntax/gradletasks/GradleTasksService.java @@ -21,7 +21,7 @@ public void getBuild(final GetBuildRequest req, if (!projectDir.exists()) { throw new GradleTasksException(String.format(PROJECT_DIR_ERROR, req.getProjectDir())); } - GradleTasksUtil.getBuild(projectDir, responseObserver); + GradleTasksUtil.getBuild(projectDir, req, responseObserver); responseObserver.onCompleted(); } catch (GradleTasksException e) { logger.error(e.getMessage()); @@ -39,8 +39,7 @@ public void runTask(final RunTaskRequest req, if (!projectDir.exists()) { throw new GradleTasksException(String.format(PROJECT_DIR_ERROR, req.getProjectDir())); } - GradleTasksUtil.runTask(projectDir, req.getTask(), req.getArgsList(), req.getJavaDebug(), - req.getJavaDebugPort(), responseObserver); + GradleTasksUtil.runTask(projectDir, req, responseObserver); responseObserver.onCompleted(); } catch (GradleTasksException e) { logger.error(e.getMessage()); diff --git a/tasks-server/src/main/java/com/github/badsyntax/gradletasks/GradleTasksUtil.java b/tasks-server/src/main/java/com/github/badsyntax/gradletasks/GradleTasksUtil.java index 19a17af42..db3f49b9d 100644 --- a/tasks-server/src/main/java/com/github/badsyntax/gradletasks/GradleTasksUtil.java +++ b/tasks-server/src/main/java/com/github/badsyntax/gradletasks/GradleTasksUtil.java @@ -3,8 +3,8 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.util.HashMap; -import java.util.List; import java.util.Map; +import com.google.common.base.Strings; import org.gradle.tooling.BuildCancelledException; import org.gradle.tooling.BuildException; import org.gradle.tooling.BuildLauncher; @@ -15,17 +15,20 @@ import org.gradle.tooling.ProjectConnection; import org.gradle.tooling.UnsupportedVersionException; import org.gradle.tooling.exceptions.UnsupportedBuildArgumentException; +import org.gradle.tooling.model.build.BuildEnvironment; import io.grpc.stub.StreamObserver; public class GradleTasksUtil { - private static CancellationTokenPool cancellationTokenPool = new CancellationTokenPool(); + private static final CancellationTokenPool cancellationTokenPool = new CancellationTokenPool(); private static final Object lock = new Object(); + private static final String JAVA_TOOL_OPTIONS_ENV = "JAVA_TOOL_OPTIONS"; + private static final String GRADLE_USER_HOME_ARG = "-Dgradle.user.home=%s"; private GradleTasksUtil() { } - public static void getBuild(final File projectDir, + public static void getBuild(final File projectDir, final GetBuildRequest req, final StreamObserver responseObserver) throws GradleTasksException { CancellationTokenSource cancellationTokenSource = GradleConnector.newCancellationTokenSource(); GradleConnector gradleConnector = @@ -33,9 +36,11 @@ public static void getBuild(final File projectDir, String cancellationKey = projectDir.getAbsolutePath(); cancellationTokenPool.put(CancellationTokenPool.TYPE.GET, cancellationKey, cancellationTokenSource); - try (ProjectConnection projectConnection = gradleConnector.connect()) { + try (ProjectConnection connection = gradleConnector.connect()) { + responseObserver.onNext(buildEnvironmentReply(connection, req.getGradleHome())); + ModelBuilder rootProjectBuilder = - projectConnection.model(org.gradle.tooling.model.GradleProject.class); + connection.model(org.gradle.tooling.model.GradleProject.class); rootProjectBuilder.withCancellationToken(cancellationTokenSource.token()) .addProgressListener((ProgressEvent progressEvent) -> { synchronized (lock) { @@ -65,7 +70,9 @@ public final void onOutputChanged(ByteArrayOutputStream outputMessage) { } } }).setColorOutput(false); - rootProjectBuilder.withArguments("-Dgradle.user.home=testuserhome") + if (!Strings.isNullOrEmpty(req.getGradleHome())) { + rootProjectBuilder.withArguments(String.format(GRADLE_USER_HOME_ARG, req.getGradleHome())); + } GradleProject gradleProject = buildProject(rootProjectBuilder.get()); GradleBuild gradleBuild = GradleBuild.newBuilder().setProject(gradleProject).build(); GetBuildResult result = GetBuildResult.newBuilder().setBuild(gradleBuild).build(); @@ -86,18 +93,35 @@ public final void onOutputChanged(ByteArrayOutputStream outputMessage) { } } - public static void runTask(final File projectDir, final String task, final List args, - final Boolean javaDebug, final int javaDebugPort, + private static GetBuildReply buildEnvironmentReply(ProjectConnection connection, + String gradleHome) { + BuildEnvironment environment = connection.model(BuildEnvironment.class).get(); + org.gradle.tooling.model.build.GradleEnvironment gradleEnvironment = environment.getGradle(); + org.gradle.tooling.model.build.JavaEnvironment javaEnvironment = environment.getJava(); + return GetBuildReply.newBuilder() + .setEnvironment(Environment.newBuilder() + .setGradleEnvironment(GradleEnvironment.newBuilder() + .setGradleUserHome(Strings.isNullOrEmpty(gradleHome) + ? gradleEnvironment.getGradleUserHome().getAbsolutePath() + : gradleHome) + .setGradleVersion(gradleEnvironment.getGradleVersion())) + .setJavaEnvironment(JavaEnvironment.newBuilder() + .setJavaHome(javaEnvironment.getJavaHome().getAbsolutePath()) + .addAllJvmArgs(javaEnvironment.getJvmArguments()))) + .build(); + } + + public static void runTask(final File projectDir, final RunTaskRequest req, final StreamObserver responseObserver) throws GradleTasksException { GradleConnector gradleConnector = GradleConnector.newConnector().forProjectDirectory(projectDir); CancellationTokenSource cancellationTokenSource = GradleConnector.newCancellationTokenSource(); - String cancellationKey = projectDir.getAbsolutePath() + task; + String cancellationKey = projectDir.getAbsolutePath() + req.getTask(); cancellationTokenPool.put(CancellationTokenPool.TYPE.RUN, cancellationKey, cancellationTokenSource); - try (ProjectConnection projectConnection = gradleConnector.connect()) { + try (ProjectConnection connection = gradleConnector.connect()) { BuildLauncher build = - projectConnection.newBuild().withCancellationToken(cancellationTokenSource.token()) + connection.newBuild().withCancellationToken(cancellationTokenSource.token()) .addProgressListener((ProgressEvent progressEvent) -> { synchronized (lock) { Progress progress = @@ -109,7 +133,6 @@ public static void runTask(final File projectDir, final String task, final List< @Override public final void onOutputChanged(ByteArrayOutputStream outputMessage) { synchronized (lock) { - Output output = Output.newBuilder().setOutputType(Output.OutputType.STDOUT) .setMessage(outputMessage.toString()).build(); RunTaskReply reply = RunTaskReply.newBuilder().setOutput(output).build(); @@ -126,20 +149,23 @@ public final void onOutputChanged(ByteArrayOutputStream outputMessage) { responseObserver.onNext(reply); } } - }).setColorOutput(true).withArguments(args).forTasks(task); - if (Boolean.TRUE.equals(javaDebug)) { - build.setEnvironmentVariables(buildJavaEnvVarsWithJwdp(javaDebugPort)); + }).setColorOutput(true).withArguments(req.getArgsList()).forTasks(req.getTask()); + if (!Strings.isNullOrEmpty(req.getGradleHome())) { + build.addArguments(String.format(GRADLE_USER_HOME_ARG, req.getGradleHome())); + } + if (Boolean.TRUE.equals(req.getJavaDebug())) { + build.setEnvironmentVariables(buildJavaEnvVarsWithJwdp(req.getJavaDebugPort())); } build.run(); - RunTaskResult result = - RunTaskResult.newBuilder().setMessage("Successfully run task").setTask(task).build(); + RunTaskResult result = RunTaskResult.newBuilder().setMessage("Successfully run task") + .setTask(req.getTask()).build(); RunTaskReply reply = RunTaskReply.newBuilder().setRunTaskResult(result).build(); responseObserver.onNext(reply); } catch (BuildException | UnsupportedVersionException | UnsupportedBuildArgumentException | IllegalStateException e) { throw new GradleTasksException(e.getMessage(), e); } catch (BuildCancelledException e) { - Cancelled cancelled = Cancelled.newBuilder().setMessage(e.getMessage()).setTask(task) + Cancelled cancelled = Cancelled.newBuilder().setMessage(e.getMessage()).setTask(req.getTask()) .setProjectDir(projectDir.getPath()).build(); RunTaskReply reply = RunTaskReply.newBuilder().setCancelled(cancelled).build(); responseObserver.onNext(reply); @@ -206,9 +232,8 @@ private static GradleProject buildProject(org.gradle.tooling.model.GradleProject private static Map buildJavaEnvVarsWithJwdp(int javaDebugPort) { HashMap envVars = new HashMap<>(System.getenv()); - envVars.put("JAVA_TOOL_OPTIONS", - String.format( - "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=localhost:%d", + envVars.put(JAVA_TOOL_OPTIONS_ENV, + String.format("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=localhost:%d", javaDebugPort)); return envVars; } From b5420176202e03e636d8b800774b154b1a3387dc Mon Sep 17 00:00:00 2001 From: Richard Willis Date: Wed, 29 Apr 2020 11:03:09 +0100 Subject: [PATCH 4/9] Fix collapsing gradleView --- extension/src/gradleView.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/extension/src/gradleView.ts b/extension/src/gradleView.ts index fc6493eb6..bf6b5ad99 100644 --- a/extension/src/gradleView.ts +++ b/extension/src/gradleView.ts @@ -236,6 +236,7 @@ export class GradleTasksTreeDataProvider 'gradle:explorerCollapsed', collapsed ); + this.buildTreeItems(); this.render(); } From 9b8213156d66dc9375c2781f5d83d6a8a567fd4f Mon Sep 17 00:00:00 2001 From: Richard Willis Date: Wed, 29 Apr 2020 11:04:15 +0100 Subject: [PATCH 5/9] Better approach for custom gradle user home Now also supporting relative paths --- README.md | 13 +++-- extension/src/client.ts | 15 ++--- extension/src/config.ts | 4 +- extension/src/tasks.ts | 13 +++-- proto/gradle_tasks.proto | 4 +- .../gradletasks/GradleTasksUtil.java | 57 ++++++++++--------- 6 files changed, 58 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 60de4c0d6..41b389e9a 100644 --- a/README.md +++ b/README.md @@ -16,12 +16,12 @@ This extension provides a visual interface for your Gradle build. You can view G This extension supports whatever Gradle supports and is language/project agnostic, but it can work nicely alongside other extensions like the [Java language support extension](https://github.com/redhat-developer/vscode-java). -- 👉 [All Features](./FEATURES.md) +👉 [All Features](./FEATURES.md) ## Requirements - [Java >= 8](https://adoptopenjdk.net/) must be installed -- Local Gradle wrapper executables must exist at the root of the workspace folders (either `gradlew` or `gradlew.bat`, depending on your environment) +- Local Gradle wrapper executables must exist at the root of the workspace folders ## Settings @@ -36,15 +36,16 @@ This extension contributes the following settings: This extension supports the following settings: -- `java.home`: Absolute path to JDK home folder used to launch the gradle daemons. (Contributed by [vscode-java](https://github.com/redhat-developer/vscode-java).) -- `java.import.gradle.home`: Setting for GRADLE_HOME. (Contributed by [vscode-java](https://github.com/redhat-developer/vscode-java).) +- `java.home`: Absolute path to JDK home folder used to launch the gradle daemons (Contributed by [vscode-java](https://github.com/redhat-developer/vscode-java)) +- `java.import.gradle.user.home`: Setting for GRADLE_HOME (Contributed by [vscode-java](https://github.com/redhat-developer/vscode-java)) +- `java.import.gradle.jvmArguments`: JVM arguments to pass to Gradle (Contributed by [vscode-java](https://github.com/redhat-developer/vscode-java)) ## Supported Environment Variables Most of the standard Java & Gradle environment variables are supported: - `JAVE_HOME` (overridden by `java.home`) -- `GRADLE_USER_HOME` (overridden by `java.import.gradle.home`) +- `GRADLE_USER_HOME` (overridden by `java.import.gradle.user.home`) ### Setting Project Environment Variables @@ -58,7 +59,7 @@ You can use an environment manager like [direnv](https://direnv.net/) to set pro } ``` -## Debugging Debugging JavaExec Tasks +## Debugging JavaExec Tasks ![Debug Screencast](images/debug-screencast.gif) diff --git a/extension/src/client.ts b/extension/src/client.ts index d3a011ea8..94664cb02 100644 --- a/extension/src/client.ts +++ b/extension/src/client.ts @@ -97,18 +97,19 @@ export class GradleTasksClient implements vscode.Disposable { public async getBuild( projectFolder: string, - gradleHome: string | null + gradleUserHome: string | null ): Promise { this.statusBarItem.text = localize( + // TODO 'client.refreshingTasks', - '{0} Gradle: Refreshing Tasks', + '{0} Gradle: Building', '$(sync~spin)' ); this.statusBarItem.show(); const request = new GetBuildRequest(); request.setProjectDir(projectFolder); - if (gradleHome) { - request.setGradleHome(gradleHome); + if (gradleUserHome) { + request.setGradleUserHome(gradleUserHome); } const getBuildStream = this.grpcClient!.getBuild(request); try { @@ -158,7 +159,7 @@ export class GradleTasksClient implements vscode.Disposable { task: vscode.Task, args: string[] = [], javaDebugPort: number | null, - gradleHome: string | null, + gradleUserHome: string | null, onOutput: (output: Output) => void ): Promise { this.statusBarItem.show(); @@ -170,8 +171,8 @@ export class GradleTasksClient implements vscode.Disposable { if (javaDebugPort !== null) { request.setJavaDebugPort(javaDebugPort); } - if (gradleHome) { - request.setGradleHome(gradleHome); + if (gradleUserHome) { + request.setGradleUserHome(gradleUserHome); } const runTaskStream = this.grpcClient!.runTask(request); try { diff --git a/extension/src/config.ts b/extension/src/config.ts index 516b5c5c2..8a7ae19ad 100644 --- a/extension/src/config.ts +++ b/extension/src/config.ts @@ -24,10 +24,10 @@ export function getConfigJavaHome(): string | null { .get('home', null); } -export function getConfigImportGradleHome(): string | null { +export function getConfigImportGradleUserHome(): string | null { return vscode.workspace .getConfiguration('java') - .get('import.gradle.home', null); + .get('import.gradle.user.home', null); } export function getConfigIsDebugEnabled(): boolean { diff --git a/extension/src/tasks.ts b/extension/src/tasks.ts index b6a2e3fd2..b60ba768c 100644 --- a/extension/src/tasks.ts +++ b/extension/src/tasks.ts @@ -13,7 +13,7 @@ import { ConfigTaskPresentationOptionsPanelKind, ConfigTaskPresentationOptions, getConfigTaskPresentationOptions, - getConfigImportGradleHome, + getConfigImportGradleUserHome, } from './config'; import { logger } from './logger'; import { GradleTasksClient } from './client'; @@ -217,8 +217,11 @@ export class GradleTaskProvider implements vscode.TaskProvider { private async getGradleBuild( projectFolder: vscode.WorkspaceFolder ): Promise { - const gradleHome = getConfigImportGradleHome(); - return await this.client?.getBuild(projectFolder.uri.fsPath, gradleHome); + const gradleUserHome = getConfigImportGradleUserHome(); + return await this.client?.getBuild( + projectFolder.uri.fsPath, + gradleUserHome + ); } private getVSCodeTasksFromGradleProject( @@ -401,13 +404,13 @@ class CustomBuildTaskTerminal implements vscode.Pseudoterminal { try { const javaDebugEnabled = this.task.definition.javaDebug; const javaDebugPort = javaDebugEnabled ? await getPort() : null; - const gradleHome = getConfigImportGradleHome(); + const gradleUserHome = getConfigImportGradleUserHome(); const runTask = this.client.runTask( this.projectFolder, this.task, args, javaDebugPort, - gradleHome, + gradleUserHome, (output: Output): void => { this.handleOutput(output.getMessage().trim()); } diff --git a/proto/gradle_tasks.proto b/proto/gradle_tasks.proto index ef085f41d..fec4e179e 100644 --- a/proto/gradle_tasks.proto +++ b/proto/gradle_tasks.proto @@ -22,7 +22,7 @@ message GetTasksRequest { string project_dir = 1; } message GetBuildRequest { string project_dir = 1; - string gradle_home = 2; + string gradle_user_home = 2; } message GetBuildReply { @@ -51,7 +51,7 @@ message RunTaskRequest { repeated string args = 3; bool java_debug = 4; int32 java_debug_port = 5; - string gradle_home = 6; + string gradle_user_home = 6; } message RunTaskResult { diff --git a/tasks-server/src/main/java/com/github/badsyntax/gradletasks/GradleTasksUtil.java b/tasks-server/src/main/java/com/github/badsyntax/gradletasks/GradleTasksUtil.java index db3f49b9d..59cfb9b15 100644 --- a/tasks-server/src/main/java/com/github/badsyntax/gradletasks/GradleTasksUtil.java +++ b/tasks-server/src/main/java/com/github/badsyntax/gradletasks/GradleTasksUtil.java @@ -2,6 +2,7 @@ import java.io.ByteArrayOutputStream; import java.io.File; +import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import com.google.common.base.Strings; @@ -23,7 +24,6 @@ public class GradleTasksUtil { private static final CancellationTokenPool cancellationTokenPool = new CancellationTokenPool(); private static final Object lock = new Object(); private static final String JAVA_TOOL_OPTIONS_ENV = "JAVA_TOOL_OPTIONS"; - private static final String GRADLE_USER_HOME_ARG = "-Dgradle.user.home=%s"; private GradleTasksUtil() { } @@ -33,11 +33,15 @@ public static void getBuild(final File projectDir, final GetBuildRequest req, CancellationTokenSource cancellationTokenSource = GradleConnector.newCancellationTokenSource(); GradleConnector gradleConnector = GradleConnector.newConnector().forProjectDirectory(projectDir); + if (!Strings.isNullOrEmpty(req.getGradleUserHome())) { + gradleConnector + .useGradleUserHomeDir(buildGradleUserHomeFile(req.getGradleUserHome(), projectDir)); + } String cancellationKey = projectDir.getAbsolutePath(); cancellationTokenPool.put(CancellationTokenPool.TYPE.GET, cancellationKey, cancellationTokenSource); try (ProjectConnection connection = gradleConnector.connect()) { - responseObserver.onNext(buildEnvironmentReply(connection, req.getGradleHome())); + responseObserver.onNext(buildEnvironmentReply(connection)); ModelBuilder rootProjectBuilder = connection.model(org.gradle.tooling.model.GradleProject.class); @@ -70,9 +74,6 @@ public final void onOutputChanged(ByteArrayOutputStream outputMessage) { } } }).setColorOutput(false); - if (!Strings.isNullOrEmpty(req.getGradleHome())) { - rootProjectBuilder.withArguments(String.format(GRADLE_USER_HOME_ARG, req.getGradleHome())); - } GradleProject gradleProject = buildProject(rootProjectBuilder.get()); GradleBuild gradleBuild = GradleBuild.newBuilder().setProject(gradleProject).build(); GetBuildResult result = GetBuildResult.newBuilder().setBuild(gradleBuild).build(); @@ -93,28 +94,14 @@ public final void onOutputChanged(ByteArrayOutputStream outputMessage) { } } - private static GetBuildReply buildEnvironmentReply(ProjectConnection connection, - String gradleHome) { - BuildEnvironment environment = connection.model(BuildEnvironment.class).get(); - org.gradle.tooling.model.build.GradleEnvironment gradleEnvironment = environment.getGradle(); - org.gradle.tooling.model.build.JavaEnvironment javaEnvironment = environment.getJava(); - return GetBuildReply.newBuilder() - .setEnvironment(Environment.newBuilder() - .setGradleEnvironment(GradleEnvironment.newBuilder() - .setGradleUserHome(Strings.isNullOrEmpty(gradleHome) - ? gradleEnvironment.getGradleUserHome().getAbsolutePath() - : gradleHome) - .setGradleVersion(gradleEnvironment.getGradleVersion())) - .setJavaEnvironment(JavaEnvironment.newBuilder() - .setJavaHome(javaEnvironment.getJavaHome().getAbsolutePath()) - .addAllJvmArgs(javaEnvironment.getJvmArguments()))) - .build(); - } - public static void runTask(final File projectDir, final RunTaskRequest req, final StreamObserver responseObserver) throws GradleTasksException { GradleConnector gradleConnector = GradleConnector.newConnector().forProjectDirectory(projectDir); + if (!Strings.isNullOrEmpty(req.getGradleUserHome())) { + gradleConnector + .useGradleUserHomeDir(buildGradleUserHomeFile(req.getGradleUserHome(), projectDir)); + } CancellationTokenSource cancellationTokenSource = GradleConnector.newCancellationTokenSource(); String cancellationKey = projectDir.getAbsolutePath() + req.getTask(); cancellationTokenPool.put(CancellationTokenPool.TYPE.RUN, cancellationKey, @@ -150,9 +137,6 @@ public final void onOutputChanged(ByteArrayOutputStream outputMessage) { } } }).setColorOutput(true).withArguments(req.getArgsList()).forTasks(req.getTask()); - if (!Strings.isNullOrEmpty(req.getGradleHome())) { - build.addArguments(String.format(GRADLE_USER_HOME_ARG, req.getGradleHome())); - } if (Boolean.TRUE.equals(req.getJavaDebug())) { build.setEnvironmentVariables(buildJavaEnvVarsWithJwdp(req.getJavaDebugPort())); } @@ -237,4 +221,25 @@ private static Map buildJavaEnvVarsWithJwdp(int javaDebugPort) { javaDebugPort)); return envVars; } + + private static File buildGradleUserHomeFile(String gradleUserHome, File projectDir) { + String gradleUserHomePath = Paths.get(gradleUserHome).isAbsolute() ? gradleUserHome + : Paths.get(projectDir.getAbsolutePath(), gradleUserHome).toAbsolutePath().toString(); + return new File(gradleUserHomePath); + } + + private static GetBuildReply buildEnvironmentReply(ProjectConnection connection) { + BuildEnvironment environment = connection.model(BuildEnvironment.class).get(); + org.gradle.tooling.model.build.GradleEnvironment gradleEnvironment = environment.getGradle(); + org.gradle.tooling.model.build.JavaEnvironment javaEnvironment = environment.getJava(); + return GetBuildReply.newBuilder() + .setEnvironment(Environment.newBuilder() + .setGradleEnvironment(GradleEnvironment.newBuilder() + .setGradleUserHome(gradleEnvironment.getGradleUserHome().getAbsolutePath()) + .setGradleVersion(gradleEnvironment.getGradleVersion())) + .setJavaEnvironment(JavaEnvironment.newBuilder() + .setJavaHome(javaEnvironment.getJavaHome().getAbsolutePath()) + .addAllJvmArgs(javaEnvironment.getJvmArguments()))) + .build(); + } } From ac5cefa3bdd2cb88e1140d52434faf8958672a09 Mon Sep 17 00:00:00 2001 From: Richard Willis Date: Wed, 29 Apr 2020 18:38:27 +0100 Subject: [PATCH 6/9] Update config scopes --- extension/package.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extension/package.json b/extension/package.json index 94b3e677b..07b383e51 100644 --- a/extension/package.json +++ b/extension/package.json @@ -349,21 +349,25 @@ "gradle.enableTasksExplorer": { "type": "boolean", "default": true, + "scope": "resource", "description": "%extension.config.enableTasksExplorer.description%" }, "gradle.debug": { "type": "boolean", "default": false, + "scope": "window", "description": "%extension.config.debug.description%" }, "gradle.focusTaskInExplorer": { "type": "boolean", "default": true, + "scope": "resource", "description": "%extension.config.focusTaskInExplorer.description%" }, "gradle.javaDebug": { "type": "object", "description": "%extension.config.javaDebug.description%", + "scope": "resource", "properties": { "tasks": { "type": "array", @@ -393,6 +397,7 @@ "gradle.taskPresentationOptions": { "type": "object", "description": "%extension.config.taskPresentationOptions.description%", + "scope": "resource", "properties": { "reveal": { "type": "string", From 4b4da15d59e8048f60ce437c887b66e75a9dd67b Mon Sep 17 00:00:00 2001 From: Richard Willis Date: Wed, 29 Apr 2020 18:39:36 +0100 Subject: [PATCH 7/9] Update Java project after getting build info This patches various issues with the java language server, including: * Timeout of 120000 reached waiting for exclusive access to file (gradle-6.3-bin.zip) due to two gradle processes attempting to create a gradle dist * Refereces that might have been created as part of project build --- README.md | 2 ++ extension/src/client.ts | 2 +- extension/src/tasks.ts | 33 +++++++++++++++++++++------------ 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 41b389e9a..6be547999 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,8 @@ You can use an environment manager like [direnv](https://direnv.net/) to set pro } ``` +Note, the VS Code settings take precedence over the environment variables. + ## Debugging JavaExec Tasks ![Debug Screencast](images/debug-screencast.gif) diff --git a/extension/src/client.ts b/extension/src/client.ts index 94664cb02..c3d902dfc 100644 --- a/extension/src/client.ts +++ b/extension/src/client.ts @@ -102,7 +102,7 @@ export class GradleTasksClient implements vscode.Disposable { this.statusBarItem.text = localize( // TODO 'client.refreshingTasks', - '{0} Gradle: Building', + '{0} Gradle: Building...', '$(sync~spin)' ); this.statusBarItem.show(); diff --git a/extension/src/tasks.ts b/extension/src/tasks.ts index b60ba768c..345f3f4e9 100644 --- a/extension/src/tasks.ts +++ b/extension/src/tasks.ts @@ -123,15 +123,14 @@ export function getRestartingTask(task: vscode.Task): vscode.Task | void { return restartingTasks.get(task.definition.id); } -async function hasGradleBuildFile( - folder: vscode.WorkspaceFolder -): Promise { - const files = fg.sync('*{.gradle,.gradle.kts}', { +function getGradleBuildFile(folder: vscode.WorkspaceFolder): string { + const files = fg.sync('!(*settings){.gradle,.gradle.kts}', { onlyFiles: true, cwd: folder.uri.fsPath, deep: 1, + absolute: true, }); - return files.length > 0; + return files[0]; } function getTaskPresentationOptions(): vscode.TaskPresentationOptions { @@ -167,11 +166,15 @@ export class GradleTaskProvider implements vscode.TaskProvider { const allTasks: vscode.Task[] = []; const taskPresentationOptions = getTaskPresentationOptions(); for (const workspaceFolder of folders) { - if ( - getConfigIsAutoDetectionEnabled(workspaceFolder) && - hasGradleBuildFile(workspaceFolder) - ) { - const gradleBuild = await this.getGradleBuild(workspaceFolder); + if (getConfigIsAutoDetectionEnabled(workspaceFolder)) { + const buildFile = getGradleBuildFile(workspaceFolder); + if (!buildFile) { + continue; + } + const gradleBuild = await this.getGradleBuild( + workspaceFolder, + vscode.Uri.file(buildFile) + ); const gradleProject = gradleBuild && gradleBuild.getProject(); if (gradleProject) { allTasks.push( @@ -215,13 +218,19 @@ export class GradleTaskProvider implements vscode.TaskProvider { } private async getGradleBuild( - projectFolder: vscode.WorkspaceFolder + projectFolder: vscode.WorkspaceFolder, + buildFile: vscode.Uri ): Promise { const gradleUserHome = getConfigImportGradleUserHome(); - return await this.client?.getBuild( + const build = await this.client?.getBuild( projectFolder.uri.fsPath, gradleUserHome ); + vscode.commands.executeCommand( + 'gradle.updateJavaProjectConfiguration', + buildFile + ); + return build; } private getVSCodeTasksFromGradleProject( From cb3b1a2390e9870617710f6abe54fef9f0c75127 Mon Sep 17 00:00:00 2001 From: Richard Willis Date: Wed, 29 Apr 2020 18:44:52 +0100 Subject: [PATCH 8/9] Remove mention of java.import.gradle.jvmArguments --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 6be547999..71527c62f 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,6 @@ This extension supports the following settings: - `java.home`: Absolute path to JDK home folder used to launch the gradle daemons (Contributed by [vscode-java](https://github.com/redhat-developer/vscode-java)) - `java.import.gradle.user.home`: Setting for GRADLE_HOME (Contributed by [vscode-java](https://github.com/redhat-developer/vscode-java)) -- `java.import.gradle.jvmArguments`: JVM arguments to pass to Gradle (Contributed by [vscode-java](https://github.com/redhat-developer/vscode-java)) ## Supported Environment Variables From 92c79d47053fea26b0596245de6287e2c7caa2a8 Mon Sep 17 00:00:00 2001 From: Richard Willis Date: Wed, 29 Apr 2020 18:52:07 +0100 Subject: [PATCH 9/9] Rename config function --- extension/src/config.ts | 2 +- extension/src/tasks.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extension/src/config.ts b/extension/src/config.ts index 8a7ae19ad..164e612b1 100644 --- a/extension/src/config.ts +++ b/extension/src/config.ts @@ -24,7 +24,7 @@ export function getConfigJavaHome(): string | null { .get('home', null); } -export function getConfigImportGradleUserHome(): string | null { +export function getConfigJavaImportGradleUserHome(): string | null { return vscode.workspace .getConfiguration('java') .get('import.gradle.user.home', null); diff --git a/extension/src/tasks.ts b/extension/src/tasks.ts index 345f3f4e9..a89e5a428 100644 --- a/extension/src/tasks.ts +++ b/extension/src/tasks.ts @@ -13,7 +13,7 @@ import { ConfigTaskPresentationOptionsPanelKind, ConfigTaskPresentationOptions, getConfigTaskPresentationOptions, - getConfigImportGradleUserHome, + getConfigJavaImportGradleUserHome, } from './config'; import { logger } from './logger'; import { GradleTasksClient } from './client'; @@ -221,7 +221,7 @@ export class GradleTaskProvider implements vscode.TaskProvider { projectFolder: vscode.WorkspaceFolder, buildFile: vscode.Uri ): Promise { - const gradleUserHome = getConfigImportGradleUserHome(); + const gradleUserHome = getConfigJavaImportGradleUserHome(); const build = await this.client?.getBuild( projectFolder.uri.fsPath, gradleUserHome @@ -413,7 +413,7 @@ class CustomBuildTaskTerminal implements vscode.Pseudoterminal { try { const javaDebugEnabled = this.task.definition.javaDebug; const javaDebugPort = javaDebugEnabled ? await getPort() : null; - const gradleUserHome = getConfigImportGradleUserHome(); + const gradleUserHome = getConfigJavaImportGradleUserHome(); const runTask = this.client.runTask( this.projectFolder, this.task,