diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index 023347095..1b17545be 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -26,7 +26,7 @@ The extension models the project hierarchical structure using the vscode treeVie
Gradle tasks can be run through either the [treeView](https://code.visualstudio.com/api/extension-guides/tree-view) or the Command Palette.
-The tasks use the gRPC client to call the `runTask` server method. Similar to getting project data, Gradle progress and output (`STDERR` & `STDOUT`) is streamed to the client. Tasks are run in a custom vscode terminal.
+Tasks are run via the `runBuild` gRPC method. Similar to getting project data, Gradle progress and output (`STDERR` & `STDOUT`) is streamed to the client. Tasks are run in a custom vscode terminal. The `runBuild` gRPC method accepts a list of arguments which are passed to the `BuildLauncher`.
## The Build System
diff --git a/README.md b/README.md
index 68bb04595..55d31836c 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,11 @@ When you expand a project, tasks are listed in a tree, grouped by the task group
Run tasks
-Tasks can be run via the `Gradle Tasks`, `Pinned Tasks` or `Recent Tasks` treeviews, or as vscode tasks via `Command Palette => Run Task`.
+Tasks can be run via:
+
+- `Gradle Tasks`, `Pinned Tasks` or `Recent Tasks` treeviews
+- `Run Task` command
+- `Run a Gradle Build` command
A running task will be shown with an animated "spinner" icon in the treeviews, along with `Cancel Task` & `Restart Task` buttons. The `Cancel Task` button will gracefully cancel the task. The `Restart Task` button will first cancel the task, then restart it.
@@ -45,6 +49,10 @@ Send a SIGINT signal (ctrl/cmd + c) in the terminal to gracefully cancel it.
+Tasks run via the `Run a Gradle Build` command are not reflected in any of the treeviews. Use this command to specify your own Gradle build arguments, for example to run multiple tasks or to exclude tasks.
+
+
+
Debug JavaExec tasks
@@ -135,7 +143,7 @@ This extension contributes the following settings:
- `gradle.autoDetect`: Automatically detect Gradle tasks
- `gradle.focusTaskInExplorer`: Focus the task in the explorer when running a task
-- `gradle.nestedProjects`: Support nested projects (boolean or an array of directories)
+- `gradle.nestedProjects`: Process nested projects (boolean or an array of directories)
- `gradle.javaDebug`: Debug JavaExec tasks (see below for usage)
- `gradle.debug`: Show extra debug info in the output panel
- `gradle.disableConfirmations`: Disable the warning confirm messages when performing batch actions (eg clear tasks, stop daemons etc)
diff --git a/build.gradle b/build.gradle
index f2edc0b1c..b4078e7d3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -28,6 +28,7 @@ allprojects {
subprojects {
apply plugin: 'java'
apply plugin: 'com.google.protobuf'
+ apply plugin: 'com.diffplug.gradle.spotless'
repositories {
maven {
diff --git a/extension/package.json b/extension/package.json
index a07bcc001..ca9d3d836 100644
--- a/extension/package.json
+++ b/extension/package.json
@@ -93,6 +93,14 @@
"dark": "resources/dark/run.svg"
}
},
+ {
+ "command": "gradle.runBuild",
+ "title": "Run a Gradle Build",
+ "icon": {
+ "light": "resources/light/console.svg",
+ "dark": "resources/dark/console.svg"
+ }
+ },
{
"command": "gradle.pinTask",
"title": "Pin Task"
@@ -186,7 +194,7 @@
"title": "Open Task Build File"
},
{
- "command": "gradle.cancelTask",
+ "command": "gradle.cancelBuild",
"title": "Cancel Task"
},
{
@@ -221,10 +229,6 @@
"dark": "resources/dark/list-tree.svg"
}
},
- {
- "command": "gradle.killGradleProcess",
- "title": "Kill Gradle task process"
- },
{
"command": "gradle.showProcessMessage",
"title": "Show Gradle process information message box"
@@ -341,7 +345,7 @@
"when": "false"
},
{
- "command": "gradle.cancelTask",
+ "command": "gradle.cancelBuild",
"when": "false"
},
{
@@ -402,6 +406,10 @@
},
{
"command": "gradle.openSettings",
+ "when": "view == gradleTasksView"
+ },
+ {
+ "command": "gradle.runBuild",
"when": "view == gradleTasksView",
"group": "navigation@0"
},
diff --git a/extension/src/api/Api.ts b/extension/src/api/Api.ts
index 846c73114..488b1b53d 100644
--- a/extension/src/api/Api.ts
+++ b/extension/src/api/Api.ts
@@ -4,6 +4,7 @@ import { logger } from '../logger';
import { GradleTasksTreeDataProvider } from '../views';
import { GradleTaskDefinition } from '../tasks';
import { GradleClient } from '../client';
+import { getRunTaskCommandCancellationKey } from '../client/CancellationKeys';
export interface RunTaskOpts {
projectFolder: string;
@@ -30,12 +31,20 @@ export class Api {
public async runTask(opts: RunTaskOpts): Promise {
const task = await this.findTask(opts.projectFolder, opts.taskName);
- return this.client.runTask(
+ const buildArgs = [task.definition.script]
+ .concat(opts.args)
+ .filter(Boolean);
+ const cancellationKey = getRunTaskCommandCancellationKey(
opts.projectFolder,
- task,
- opts.args,
+ opts.taskName
+ );
+ return this.client.runBuild(
+ opts.projectFolder,
+ cancellationKey,
+ buildArgs,
opts.input,
0,
+ task,
opts.onOutput,
opts.showOutputColors
);
@@ -43,7 +52,11 @@ export class Api {
public async cancelRunTask(opts: CancelTaskOpts): Promise {
const task = await this.findTask(opts.projectFolder, opts.taskName);
- return this.client.cancelRunTask(task);
+ const cancellationKey = getRunTaskCommandCancellationKey(
+ opts.projectFolder,
+ opts.taskName
+ );
+ return this.client.cancelBuild(cancellationKey, task);
}
private async findTask(
diff --git a/extension/src/client/CancellationKeys.ts b/extension/src/client/CancellationKeys.ts
new file mode 100644
index 000000000..77dfcd89c
--- /dev/null
+++ b/extension/src/client/CancellationKeys.ts
@@ -0,0 +1,17 @@
+export function getBuildCancellationKey(rootProjectFolder: string): string {
+ return 'getBuild' + rootProjectFolder;
+}
+
+export function getRunBuildCancellationKey(
+ rootProjectFolder: string,
+ args: ReadonlyArray
+): string {
+ return 'runBuild' + rootProjectFolder + args.join('');
+}
+
+export function getRunTaskCommandCancellationKey(
+ rootProjectFolder: string,
+ taskName: string
+): string {
+ return 'runTask' + rootProjectFolder + taskName;
+}
diff --git a/extension/src/client/GradleClient.ts b/extension/src/client/GradleClient.ts
index 77e73e003..15a241ba1 100644
--- a/extension/src/client/GradleClient.ts
+++ b/extension/src/client/GradleClient.ts
@@ -3,19 +3,11 @@ import * as grpc from '@grpc/grpc-js';
import { connectivityState as ConnectivityState } from '@grpc/grpc-js';
import {
- RunTaskRequest,
- RunTaskReply,
Output,
GetBuildRequest,
GetBuildReply,
- CancelGetBuildsRequest,
- CancelGetBuildsReply,
- CancelRunTaskRequest,
- CancelRunTaskReply,
- CancelRunTasksReply,
Cancelled,
GradleBuild,
- CancelRunTasksRequest,
Environment,
GradleConfig,
GetDaemonsStatusReply,
@@ -24,6 +16,10 @@ import {
StopDaemonsRequest,
StopDaemonRequest,
StopDaemonReply,
+ RunBuildRequest,
+ RunBuildReply,
+ CancelBuildRequest,
+ CancelBuildReply,
} from '../proto/gradle_pb';
import { GradleClient as GrpcClient } from '../proto/gradle_grpc_pb';
@@ -32,14 +28,14 @@ import { EventWaiter } from '../events';
import { GradleServer } from '../server';
import { ProgressHandler } from '../progress';
import { getGradleConfig } from '../config';
-import { GradleTaskDefinition } from '../tasks';
import { removeCancellingTask, restartQueuedTask } from '../tasks/taskUtil';
import {
COMMAND_REFRESH_DAEMON_STATUS,
COMMAND_SHOW_LOGS,
- COMMAND_CANCEL_TASK,
+ COMMAND_CANCEL_BUILD,
} from '../commands';
import { RootProject } from '../rootProject/RootProject';
+import { getBuildCancellationKey } from './CancellationKeys';
function logBuildEnvironment(environment: Environment): void {
const javaEnv = environment.getJavaEnvironment()!;
@@ -150,14 +146,18 @@ export class GradleClient implements vscode.Disposable {
progress,
'Configure project'
);
+ const cancellationKey = getBuildCancellationKey(
+ rootProject.getProjectUri().fsPath
+ );
- token.onCancellationRequested(() => this.cancelGetBuilds());
+ token.onCancellationRequested(() => this.cancelBuild(cancellationKey));
const stdOutLoggerStream = new LoggerStream(logger, LogVerbosity.INFO);
const stdErrLoggerStream = new LoggerStream(logger, LogVerbosity.ERROR);
const request = new GetBuildRequest();
request.setProjectDir(rootProject.getProjectUri().fsPath);
+ request.setCancellationKey(cancellationKey);
request.setGradleConfig(gradleConfig);
request.setShowOutputColors(showOutputColors);
const getBuildStream = this.grpcClient!.getBuild(request);
@@ -223,12 +223,13 @@ export class GradleClient implements vscode.Disposable {
);
}
- public async runTask(
+ public async runBuild(
projectFolder: string,
- task: vscode.Task,
- args: ReadonlyArray = [],
+ cancellationKey: string,
+ args: ReadonlyArray,
input = '',
javaDebugPort = 0,
+ task?: vscode.Task,
onOutput?: (output: Output) => void,
showOutputColors = true
): Promise {
@@ -245,7 +246,11 @@ export class GradleClient implements vscode.Disposable {
token: vscode.CancellationToken
) => {
token.onCancellationRequested(() =>
- vscode.commands.executeCommand(COMMAND_CANCEL_TASK, task)
+ vscode.commands.executeCommand(
+ COMMAND_CANCEL_BUILD,
+ cancellationKey,
+ task
+ )
);
const progressHandler = new ProgressHandler(progress);
@@ -255,35 +260,35 @@ export class GradleClient implements vscode.Disposable {
});
const gradleConfig = getGradleConfig();
- const request = new RunTaskRequest();
+ const request = new RunBuildRequest();
request.setProjectDir(projectFolder);
- request.setTask(task.definition.script);
+ request.setCancellationKey(cancellationKey);
request.setArgsList(args as string[]);
request.setGradleConfig(gradleConfig);
- request.setJavaDebug(task.definition.javaDebug);
request.setShowOutputColors(showOutputColors);
request.setJavaDebugPort(javaDebugPort);
request.setInput(input);
- const runTaskStream = this.grpcClient!.runTask(request);
+ const runBuildStream = this.grpcClient!.runBuild(request);
try {
await new Promise((resolve, reject) => {
- runTaskStream
- .on('data', (runTaskReply: RunTaskReply) => {
- switch (runTaskReply.getKindCase()) {
- case RunTaskReply.KindCase.PROGRESS:
+ runBuildStream
+ .on('data', (runBuildReply: RunBuildReply) => {
+ switch (runBuildReply.getKindCase()) {
+ case RunBuildReply.KindCase.PROGRESS:
progressHandler.report(
- runTaskReply.getProgress()!.getMessage().trim()
+ runBuildReply.getProgress()!.getMessage().trim()
);
break;
- case RunTaskReply.KindCase.OUTPUT:
+ case RunBuildReply.KindCase.OUTPUT:
if (onOutput) {
- onOutput(runTaskReply.getOutput()!);
+ onOutput(runBuildReply.getOutput()!);
}
break;
- case RunTaskReply.KindCase.CANCELLED:
- this.handleRunTaskCancelled(
- task,
- runTaskReply.getCancelled()!
+ case RunBuildReply.KindCase.CANCELLED:
+ this.handleRunBuildCancelled(
+ args,
+ runBuildReply.getCancelled()!,
+ task
);
break;
}
@@ -291,109 +296,54 @@ export class GradleClient implements vscode.Disposable {
.on('error', reject)
.on('end', resolve);
});
- logger.info('Completed task:', task.definition.script);
+ logger.info('Completed build:', args.join(' '));
} catch (err) {
- logger.error('Error running task:', err.details || err.message);
+ logger.error(
+ 'Error running build:',
+ `${args.join(' ')}:`,
+ err.details || err.message
+ );
throw err;
} finally {
vscode.commands.executeCommand(COMMAND_REFRESH_DAEMON_STATUS);
- restartQueuedTask(task);
+ if (task) {
+ restartQueuedTask(task);
+ }
}
}
);
}
- public async cancelRunTask(task: vscode.Task): Promise {
+ public async cancelBuild(
+ cancellationKey: string,
+ task?: vscode.Task
+ ): Promise {
await this.waitForConnect();
this.statusBarItem.hide();
- const definition = task.definition as GradleTaskDefinition;
- const request = new CancelRunTaskRequest();
- request.setProjectDir(definition.projectFolder);
- request.setTask(definition.script);
+ const request = new CancelBuildRequest();
+ request.setCancellationKey(cancellationKey);
try {
- const reply: CancelRunTaskReply = await new Promise((resolve, reject) => {
- this.grpcClient!.cancelRunTask(
+ const reply: CancelBuildReply = await new Promise((resolve, reject) => {
+ this.grpcClient!.cancelBuild(
request,
(
err: grpc.ServiceError | null,
- cancelRunTaskReply: CancelRunTaskReply | undefined
+ cancelRunBuildReply: CancelBuildReply | undefined
) => {
if (err) {
reject(err);
} else {
- resolve(cancelRunTaskReply);
+ resolve(cancelRunBuildReply);
}
}
);
});
- logger.debug(reply.getMessage());
- if (!reply.getTaskRunning()) {
+ logger.info('Cancel build:', reply.getMessage());
+ if (!reply.getBuildRunning() && task) {
removeCancellingTask(task);
}
} catch (err) {
- logger.error(
- 'Error cancelling running task:',
- err.details || err.message
- );
- } finally {
- vscode.commands.executeCommand(COMMAND_REFRESH_DAEMON_STATUS);
- }
- }
-
- public async cancelRunTasks(): Promise {
- await this.waitForConnect();
- const request = new CancelRunTasksRequest();
- try {
- const reply: CancelRunTasksReply = await new Promise(
- (resolve, reject) => {
- this.grpcClient!.cancelRunTasks(
- request,
- (
- err: grpc.ServiceError | null,
- cancelRunTasksReply: CancelRunTasksReply | undefined
- ) => {
- if (err) {
- reject(err);
- } else {
- resolve(cancelRunTasksReply);
- }
- }
- );
- }
- );
- logger.debug(reply.getMessage());
- } catch (err) {
- logger.error(
- 'Error cancelling running tasks:',
- err.details || err.message
- );
- }
- }
-
- public async cancelGetBuilds(): Promise {
- await this.waitForConnect();
- const request = new CancelGetBuildsRequest();
- try {
- const reply: CancelGetBuildsReply = await new Promise(
- (resolve, reject) => {
- this.grpcClient!.cancelGetBuilds(
- request,
- (
- err: grpc.ServiceError | null,
- cancelGetBuildsReply: CancelGetBuildsReply | undefined
- ) => {
- if (err) {
- reject(err);
- } else {
- resolve(cancelGetBuildsReply);
- }
- }
- );
- }
- );
- logger.debug(reply.getMessage());
- } catch (err) {
- logger.error('Error cancelling get builds:', err.details || err.message);
+ logger.error('Error cancelling build:', err.details || err.message);
}
}
@@ -488,14 +438,17 @@ export class GradleClient implements vscode.Disposable {
}
}
- private handleRunTaskCancelled = (
- task: vscode.Task,
- cancelled: Cancelled
+ private handleRunBuildCancelled = (
+ args: ReadonlyArray,
+ cancelled: Cancelled,
+ task?: vscode.Task
): void => {
logger.info(
- `Task cancelled: ${task.definition.script}: ${cancelled.getMessage()}`
+ `Build cancelled: ${args.join(' ')}: ${cancelled.getMessage()}`
);
- removeCancellingTask(task);
+ if (task) {
+ removeCancellingTask(task);
+ }
};
private handleGetBuildCancelled = (cancelled: Cancelled): void => {
diff --git a/extension/src/commands/cancelBuildCommand.ts b/extension/src/commands/cancelBuildCommand.ts
new file mode 100644
index 000000000..115c7084b
--- /dev/null
+++ b/extension/src/commands/cancelBuildCommand.ts
@@ -0,0 +1,15 @@
+import * as vscode from 'vscode';
+import { cancelBuild } from '../tasks/taskUtil';
+import { logger } from '../logger';
+export const COMMAND_CANCEL_BUILD = 'gradle.cancelBuild';
+
+export async function cancelBuildCommand(
+ cancellationKey: string,
+ task?: vscode.Task
+): Promise {
+ try {
+ await cancelBuild(cancellationKey, task);
+ } catch (e) {
+ logger.error('Error cancelling task:', e.message);
+ }
+}
diff --git a/extension/src/commands/cancelTaskCommand.ts b/extension/src/commands/cancelTaskCommand.ts
deleted file mode 100644
index 7a43ca67f..000000000
--- a/extension/src/commands/cancelTaskCommand.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import * as vscode from 'vscode';
-import { cancelTask } from '../tasks/taskUtil';
-import { logger } from '../logger';
-export const COMMAND_CANCEL_TASK = 'gradle.cancelTask';
-
-export async function cancelTaskCommand(task: vscode.Task): Promise {
- try {
- await cancelTask(task);
- } catch (e) {
- logger.error('Error cancelling task:', e.message);
- }
-}
diff --git a/extension/src/commands/cancelTreeItemTaskCommand.ts b/extension/src/commands/cancelTreeItemTaskCommand.ts
index c7d6ce462..161a3f0ba 100644
--- a/extension/src/commands/cancelTreeItemTaskCommand.ts
+++ b/extension/src/commands/cancelTreeItemTaskCommand.ts
@@ -1,12 +1,29 @@
import * as vscode from 'vscode';
import { GradleTaskTreeItem } from '../views';
-import { COMMAND_CANCEL_TASK } from '.';
+import { COMMAND_CANCEL_BUILD } from '.';
+import { getRunTaskCommandCancellationKey } from '../client/CancellationKeys';
+import { GradleTaskDefinition } from '../tasks';
+import { getRunningGradleTask } from '../tasks/taskUtil';
export const COMMAND_CANCEL_TREE_ITEM_TASK = 'gradle.cancelTreeItemTask';
export async function cancelTreeItemTaskCommand(
treeItem: GradleTaskTreeItem
): Promise {
if (treeItem && treeItem.task) {
- return vscode.commands.executeCommand(COMMAND_CANCEL_TASK, treeItem.task);
+ // We get the running task as we could be cancelling a task that is running with args
+ const runningTask = getRunningGradleTask(treeItem.task);
+ if (!runningTask) {
+ return;
+ }
+ const definition = runningTask.definition as GradleTaskDefinition;
+ const cancellationKey = getRunTaskCommandCancellationKey(
+ definition.projectFolder,
+ runningTask.name
+ );
+ return vscode.commands.executeCommand(
+ COMMAND_CANCEL_BUILD,
+ cancellationKey,
+ runningTask
+ );
}
}
diff --git a/extension/src/commands/index.ts b/extension/src/commands/index.ts
index 98c2fe4b2..5e0056112 100644
--- a/extension/src/commands/index.ts
+++ b/extension/src/commands/index.ts
@@ -1,4 +1,4 @@
-export * from './cancelTaskCommand';
+export * from './cancelBuildCommand';
export * from './cancelTreeItemTaskCommand';
export * from './cancellingTreeItemTaskCommand';
export * from './clearAllPinnedTasksCommand';
@@ -30,3 +30,4 @@ export * from './showTaskTerminalCommand';
export * from './showTasksCommand';
export * from './stopDaemonCommand';
export * from './stopDaemonsCommand';
+export * from './runBuildCommand';
diff --git a/extension/src/commands/refreshCommand.ts b/extension/src/commands/refreshCommand.ts
index e8680b52c..ac64d49c8 100644
--- a/extension/src/commands/refreshCommand.ts
+++ b/extension/src/commands/refreshCommand.ts
@@ -4,6 +4,8 @@ export const COMMAND_REFRESH = 'gradle.refresh';
export async function refreshCommand(): Promise {
const extension = Extension.getInstance();
extension.getGradleTaskProvider().clearTasksCache();
+ // Explicitly load tasks as the views might not be visible
+ extension.getGradleTaskProvider().loadTasks();
extension.getGradleTasksTreeDataProvider().refresh();
extension.getPinnedTasksTreeDataProvider().refresh();
extension.getRecentTasksTreeDataProvider().refresh();
diff --git a/extension/src/commands/register.ts b/extension/src/commands/register.ts
index 3cc7f08c2..ea2431c52 100644
--- a/extension/src/commands/register.ts
+++ b/extension/src/commands/register.ts
@@ -14,8 +14,8 @@ import {
debugTaskWithArgsCommand,
COMMAND_RENDER_TASK,
renderTaskCommand,
- COMMAND_CANCEL_TASK,
- cancelTaskCommand,
+ COMMAND_CANCEL_BUILD,
+ cancelBuildCommand,
COMMAND_CANCEL_TREE_ITEM_TASK,
cancelTreeItemTaskCommand,
COMMAND_REFRESH,
@@ -60,6 +60,8 @@ import {
clearAllPinnedTasksCommand,
COMMAND_REMOVE_RECENT_TASK,
removeRecentTaskCommand,
+ COMMAND_RUN_BUILD,
+ runBuildCommand,
} from '.';
export function registerCommands(context: vscode.ExtensionContext): void {
@@ -77,7 +79,7 @@ export function registerCommands(context: vscode.ExtensionContext): void {
debugTaskWithArgsCommand
),
vscode.commands.registerCommand(COMMAND_RENDER_TASK, renderTaskCommand),
- vscode.commands.registerCommand(COMMAND_CANCEL_TASK, cancelTaskCommand),
+ vscode.commands.registerCommand(COMMAND_CANCEL_BUILD, cancelBuildCommand),
vscode.commands.registerCommand(
COMMAND_CANCEL_TREE_ITEM_TASK,
cancelTreeItemTaskCommand
@@ -135,6 +137,7 @@ export function registerCommands(context: vscode.ExtensionContext): void {
vscode.commands.registerCommand(
COMMAND_REMOVE_RECENT_TASK,
removeRecentTaskCommand
- )
+ ),
+ vscode.commands.registerCommand(COMMAND_RUN_BUILD, runBuildCommand)
);
}
diff --git a/extension/src/commands/runBuildCommand.ts b/extension/src/commands/runBuildCommand.ts
new file mode 100644
index 000000000..756e25ca5
--- /dev/null
+++ b/extension/src/commands/runBuildCommand.ts
@@ -0,0 +1,43 @@
+import * as vscode from 'vscode';
+import { getGradleCommand, getRootProjectFolder } from '../input';
+import { GradleRunnerTerminal } from '../terminal';
+import { getRunBuildCancellationKey } from '../client/CancellationKeys';
+export const COMMAND_RUN_BUILD = 'gradle.runBuild';
+
+export async function runBuildCommand(): Promise {
+ const rootProject = await getRootProjectFolder();
+ if (!rootProject) {
+ return;
+ }
+ const gradleCommand = await getGradleCommand();
+ if (!gradleCommand) {
+ return;
+ }
+ const args: string[] = gradleCommand.split(' ').filter(Boolean);
+ const cancellationKey = getRunBuildCancellationKey(
+ rootProject.getProjectUri().fsPath,
+ args
+ );
+ const terminal = new GradleRunnerTerminal(rootProject, args, cancellationKey);
+ const task = new vscode.Task(
+ {
+ type: 'gradle',
+ },
+ rootProject.getWorkspaceFolder(),
+ gradleCommand,
+ 'gradle',
+ new vscode.CustomExecution(
+ async (): Promise => terminal
+ ),
+ ['$gradle']
+ );
+ task.presentationOptions = {
+ showReuseMessage: false,
+ clear: true,
+ echo: true,
+ focus: true,
+ panel: vscode.TaskPanelKind.Shared,
+ reveal: vscode.TaskRevealKind.Always,
+ };
+ vscode.tasks.executeTask(task);
+}
diff --git a/extension/src/extension/Extension.ts b/extension/src/extension/Extension.ts
index a0c1bec68..380271be0 100644
--- a/extension/src/extension/Extension.ts
+++ b/extension/src/extension/Extension.ts
@@ -90,7 +90,8 @@ export class Extension {
this.icons = new Icons(context);
this.gradleTasksTreeDataProvider = new GradleTasksTreeDataProvider(
- this.context
+ this.context,
+ this.rootProjectsStore
);
this.gradleTasksTreeView = vscode.window.createTreeView(GRADLE_TASKS_VIEW, {
treeDataProvider: this.gradleTasksTreeDataProvider,
@@ -175,15 +176,7 @@ export class Extension {
}
private loadTasks(): void {
- this.client.onDidConnect(async () => {
- this.gradleTaskProvider.clearTasksCache();
- // Explicitly load tasks as the views might not be visible on editor start
- this.gradleTaskProvider.loadTasks();
- // If the server crashes and is restarted, we need to review the views
- this.gradleTasksTreeDataProvider.refresh();
- this.pinnedTasksTreeDataProvider.refresh();
- this.recentTasksTreeDataProvider.refresh();
- });
+ this.client.onDidConnect(() => this.refresh());
}
private handleTaskEvents(): void {
@@ -203,26 +196,21 @@ export class Extension {
private handleWatchEvents(): void {
this.buildFileWatcher.onDidChange((uri: vscode.Uri) => {
logger.debug('Build file changed:', uri.fsPath);
- this.reloadTasks();
+ this.refresh();
});
this.gradleWrapperWatcher.onDidChange((uri: vscode.Uri) => {
logger.debug('Gradle wrapper properties changed:', uri.fsPath);
this.client.close();
const disposable = this.client.onDidConnect(() => {
disposable.dispose();
- this.reloadTasks();
+ this.refresh();
});
this.server.restart();
});
}
- private reloadTasks(): void {
- if (this.gradleTasksTreeView.visible) {
- vscode.commands.executeCommand(COMMAND_REFRESH);
- } else {
- this.getGradleTaskProvider().clearTasksCache();
- this.getGradleTaskProvider().loadTasks();
- }
+ private refresh(): void {
+ vscode.commands.executeCommand(COMMAND_REFRESH);
}
private handleEditorEvents(): void {
@@ -235,15 +223,12 @@ export class Extension {
) {
this.server.restart();
}
- if (event.affectsConfiguration('gradle.nestedProjects')) {
- this.rootProjectsStore.clear();
- this.reloadTasks();
- }
if (
event.affectsConfiguration('gradle.javaDebug') ||
event.affectsConfiguration('gradle.nestedProjects')
) {
- vscode.commands.executeCommand(COMMAND_REFRESH);
+ this.rootProjectsStore.clear();
+ this.refresh();
}
if (event.affectsConfiguration('gradle.debug')) {
const debug = getConfigIsDebugEnabled();
@@ -256,9 +241,7 @@ export class Extension {
vscode.window.onDidCloseTerminal((terminal: vscode.Terminal) => {
this.taskTerminalsStore.removeTerminal(terminal);
}),
- vscode.workspace.onDidChangeWorkspaceFolders(() => {
- vscode.commands.executeCommand(COMMAND_REFRESH);
- })
+ vscode.workspace.onDidChangeWorkspaceFolders(() => this.refresh())
);
}
diff --git a/extension/src/input/index.ts b/extension/src/input/index.ts
index bfb68cab0..86acff5f3 100644
--- a/extension/src/input/index.ts
+++ b/extension/src/input/index.ts
@@ -1,6 +1,14 @@
import * as vscode from 'vscode';
import { TaskArgs } from '../stores/types';
import { getDisableConfirmations } from '../config';
+import { Extension } from '../extension';
+import { RootProject } from '../rootProject/RootProject';
+
+function returnTrimmedInput(value: string | undefined): string | undefined {
+ if (value !== undefined) {
+ return value.trim();
+ }
+}
export function getTaskArgs(): Thenable {
return vscode.window
@@ -8,11 +16,43 @@ export function getTaskArgs(): Thenable {
placeHolder: 'For example: --info',
ignoreFocusOut: true,
})
- .then((value: string | undefined) => {
- if (value !== undefined) {
- return value.trim();
- }
- });
+ .then(returnTrimmedInput);
+}
+
+export function getGradleCommand(): Thenable {
+ return vscode.window
+ .showInputBox({
+ prompt: [
+ 'Enter a command for gradle to run.',
+ 'This can include built-in Gradle commands or tasks.',
+ 'Not all Gradle command line options are supported.',
+ ].join(' '),
+ placeHolder: 'For example: build --info',
+ ignoreFocusOut: true,
+ })
+ .then(returnTrimmedInput);
+}
+
+export async function getRootProjectFolder(): Promise {
+ const rootProjects = await Extension.getInstance()
+ .getRootProjectsStore()
+ .buildAndGetProjectRoots();
+ if (rootProjects.length === 1) {
+ return Promise.resolve(rootProjects[0]);
+ }
+ const rootProjectPaths = rootProjects.map(
+ (rootProject) => rootProject.getProjectUri().fsPath
+ );
+ const selectedRootProjectPath = await vscode.window.showQuickPick(
+ rootProjectPaths,
+ {
+ canPickMany: false,
+ placeHolder: 'Select the root project',
+ }
+ );
+ if (selectedRootProjectPath) {
+ return rootProjects[rootProjectPaths.indexOf(selectedRootProjectPath)];
+ }
}
export async function confirmModal(message: string): Promise {
diff --git a/extension/src/rootProject/RootProject.ts b/extension/src/rootProject/RootProject.ts
index 7153f28c3..568f02654 100644
--- a/extension/src/rootProject/RootProject.ts
+++ b/extension/src/rootProject/RootProject.ts
@@ -1,11 +1,13 @@
import * as vscode from 'vscode';
import { Environment } from '../proto/gradle_pb';
+import { JavaDebug } from '../config';
export class RootProject {
private environment?: Environment;
constructor(
private readonly workspaceFolder: vscode.WorkspaceFolder,
- private readonly projectUri: vscode.Uri
+ private readonly projectUri: vscode.Uri,
+ private readonly javaDebug: JavaDebug
) {}
public setEnvironment(environment: Environment): void {
@@ -16,6 +18,10 @@ export class RootProject {
return this.environment;
}
+ public getJavaDebug(): JavaDebug {
+ return this.javaDebug;
+ }
+
public getWorkspaceFolder(): vscode.WorkspaceFolder {
return this.workspaceFolder;
}
diff --git a/extension/src/stores/RootProjectsStore.ts b/extension/src/stores/RootProjectsStore.ts
index 0e2ba55ff..852339e08 100644
--- a/extension/src/stores/RootProjectsStore.ts
+++ b/extension/src/stores/RootProjectsStore.ts
@@ -1,6 +1,6 @@
import * as vscode from 'vscode';
import * as path from 'path';
-import { getNestedProjectsConfig } from '../config';
+import { getNestedProjectsConfig, getConfigJavaDebug } from '../config';
import { StoreMap } from '.';
import { isGradleRootProject } from '../util';
import { RootProject } from '../rootProject/RootProject';
@@ -19,10 +19,9 @@ async function getNestedRootProjectFolders(): Promise {
}
function buildRootFolder(folderUri: vscode.Uri): RootProject {
- return new RootProject(
- vscode.workspace.getWorkspaceFolder(folderUri)!,
- folderUri
- );
+ const workspaceFolder = vscode.workspace.getWorkspaceFolder(folderUri)!;
+ const javaDebug = getConfigJavaDebug(workspaceFolder);
+ return new RootProject(workspaceFolder, folderUri, javaDebug);
}
export class RootProjectsStore extends StoreMap {
diff --git a/extension/src/stores/StoreMap.ts b/extension/src/stores/StoreMap.ts
index 814951ba8..a4b50b153 100644
--- a/extension/src/stores/StoreMap.ts
+++ b/extension/src/stores/StoreMap.ts
@@ -11,6 +11,10 @@ export abstract class StoreMap extends EventedStore {
return this.data;
}
+ public get(key: K): V | undefined {
+ return this.getData().get(key);
+ }
+
public clear(fireOnDidChange = true): void {
this.data.clear();
if (fireOnDidChange) {
diff --git a/extension/src/tasks/GradleTaskProvider.ts b/extension/src/tasks/GradleTaskProvider.ts
index 4039432a8..48e95754b 100644
--- a/extension/src/tasks/GradleTaskProvider.ts
+++ b/extension/src/tasks/GradleTaskProvider.ts
@@ -6,6 +6,7 @@ import { createTaskFromDefinition, loadTasksForProjectRoots } from './taskUtil';
import { TaskId } from '../stores/types';
import { RootProjectsStore } from '../stores';
import { RootProject } from '../rootProject/RootProject';
+import { getConfigJavaDebug } from '../config';
export class GradleTaskProvider
implements vscode.TaskProvider, vscode.Disposable {
@@ -52,9 +53,11 @@ export class GradleTaskProvider
);
return undefined;
}
+ const javaDebug = getConfigJavaDebug(workspaceFolder);
const rootProject = new RootProject(
workspaceFolder,
- vscode.Uri.file(gradleTaskDefinition.projectFolder)
+ vscode.Uri.file(gradleTaskDefinition.projectFolder),
+ javaDebug
);
return createTaskFromDefinition(gradleTaskDefinition, rootProject);
}
diff --git a/extension/src/tasks/taskUtil.ts b/extension/src/tasks/taskUtil.ts
index bb6e95fb4..b13be4068 100644
--- a/extension/src/tasks/taskUtil.ts
+++ b/extension/src/tasks/taskUtil.ts
@@ -3,7 +3,7 @@ import { GradleTask, GradleProject, GradleBuild } from '../proto/gradle_pb';
import { TaskArgs } from '../stores/types';
import { Extension } from '../extension';
import { GradleTaskDefinition } from '.';
-import { CustomBuildTaskTerminal } from '../terminal';
+import { GradleRunnerTerminal } from '../terminal';
import { logger } from '../logger';
import { getGradleConfig, getConfigIsAutoDetectionEnabled } from '../config';
import {
@@ -17,6 +17,7 @@ import {
import { getTaskArgs } from '../input';
import { COMMAND_RENDER_TASK } from '../commands';
import { RootProject } from '../rootProject/RootProject';
+import { getRunTaskCommandCancellationKey } from '../client/CancellationKeys';
const cancellingTasks: Map = new Map();
const restartingTasks: Map = new Map();
@@ -50,16 +51,25 @@ export function getRunningGradleTasks(): vscode.Task[] {
.map(({ task }) => task);
}
+export function getRunningGradleTask(task: vscode.Task): vscode.Task | void {
+ return getRunningGradleTasks().find((runningTask) =>
+ isTask(runningTask, task)
+ );
+}
+
export function isTaskRunning(task: vscode.Task, args?: TaskArgs): boolean {
return getTaskExecution(task, args) !== undefined;
}
-export async function cancelTask(task: vscode.Task): Promise {
- if (isTaskRunning(task)) {
+export async function cancelBuild(
+ cancellationKey: string,
+ task?: vscode.Task
+): Promise {
+ if (task && isTaskRunning(task)) {
cancellingTasks.set(task.definition.id, task);
await vscode.commands.executeCommand(COMMAND_RENDER_TASK, task);
- Extension.getInstance().getClient().cancelRunTask(task);
}
+ Extension.getInstance().getClient().cancelBuild(cancellationKey, task);
}
export function isTaskCancelling(task: vscode.Task, args?: TaskArgs): boolean {
@@ -102,19 +112,23 @@ export async function restartQueuedTask(task: vscode.Task): Promise {
}
}
-export async function removeCancellingTask(task: vscode.Task): Promise {
+export function removeCancellingTask(task: vscode.Task): void {
const cancellingTask = getCancellingTask(task);
if (cancellingTask) {
cancellingTasks.delete(cancellingTask.definition.id);
- await vscode.commands.executeCommand(COMMAND_RENDER_TASK, task);
}
}
export function queueRestartTask(task: vscode.Task): void {
if (isTaskRunning(task)) {
- restartingTasks.set(task.definition.id, task);
+ const definition = task.definition as GradleTaskDefinition;
+ restartingTasks.set(definition.id, task);
+ const cancellationKey = getRunTaskCommandCancellationKey(
+ definition.projectFolder,
+ task.name
+ );
// Once the task is cancelled it will restart via onDidEndTask
- cancelTask(task);
+ cancelBuild(cancellationKey, task);
}
}
@@ -136,14 +150,19 @@ export function createTaskFromDefinition(
rootProject: RootProject
): vscode.Task {
const taskTerminalsStore = Extension.getInstance().getTaskTerminalsStore();
- const terminal = new CustomBuildTaskTerminal(
- rootProject.getWorkspaceFolder(),
- rootProject.getProjectUri()
+ const args = [definition.script].concat(
+ definition.args.split(' ').filter(Boolean)
+ );
+ const taskName = buildTaskName(definition);
+ const cancellationKey = getRunTaskCommandCancellationKey(
+ rootProject.getProjectUri().fsPath,
+ definition.script
);
+ const terminal = new GradleRunnerTerminal(rootProject, args, cancellationKey);
const task = new vscode.Task(
definition,
rootProject.getWorkspaceFolder(),
- buildTaskName(definition),
+ taskName,
'gradle',
new vscode.CustomExecution(
async (): Promise => {
@@ -321,9 +340,8 @@ export function cloneTask(
args,
javaDebug,
};
- const rootProject = new RootProject(
- task.scope as vscode.WorkspaceFolder,
- vscode.Uri.file(definition.projectFolder)
- );
- return createTaskFromDefinition(definition, rootProject);
+ const rootProject = Extension.getInstance()
+ .getRootProjectsStore()
+ .get(definition.projectFolder);
+ return createTaskFromDefinition(definition, rootProject!);
}
diff --git a/extension/src/terminal/CustomBuildTaskTerminal.ts b/extension/src/terminal/GradleRunnerTerminal.ts
similarity index 78%
rename from extension/src/terminal/CustomBuildTaskTerminal.ts
rename to extension/src/terminal/GradleRunnerTerminal.ts
index ccbce1c87..142b0d27c 100644
--- a/extension/src/terminal/CustomBuildTaskTerminal.ts
+++ b/extension/src/terminal/GradleRunnerTerminal.ts
@@ -1,45 +1,47 @@
import * as vscode from 'vscode';
import * as util from 'util';
-import getPort from 'get-port';
-import { isTaskRunning } from '../tasks/taskUtil';
-import { waitOnTcp, isTest } from '../util';
+import { isTest, waitOnTcp } from '../util';
import { logger, LoggerStream, LogVerbosity } from '../logger';
-import { Extension } from '../extension';
import { Output } from '../proto/gradle_pb';
-import { COMMAND_CANCEL_TASK } from '../commands';
import { ServiceError } from '@grpc/grpc-js';
+import { RootProject } from '../rootProject/RootProject';
+import { isTaskRunning } from '../tasks/taskUtil';
+import getPort from 'get-port';
+import { Extension } from '../extension';
+import { COMMAND_CANCEL_BUILD } from '../commands';
const NL = '\n';
const CR = '\r';
const nlRegExp = new RegExp(`${NL}([^${CR}])`, 'g');
-export class CustomBuildTaskTerminal implements vscode.Pseudoterminal {
+export class GradleRunnerTerminal {
private readonly writeEmitter = new vscode.EventEmitter();
- public readonly onDidWrite: vscode.Event = this.writeEmitter.event;
+ private stdOutLoggerStream: LoggerStream;
private readonly closeEmitter = new vscode.EventEmitter();
private task?: vscode.Task;
+ public readonly onDidWrite: vscode.Event = this.writeEmitter.event;
public readonly onDidClose?: vscode.Event = this.closeEmitter.event;
- private stdOutLoggerStream: LoggerStream;
constructor(
- private readonly workspaceFolder: vscode.WorkspaceFolder,
- private readonly projectFolder: vscode.Uri
+ private readonly rootProject: RootProject,
+ private readonly args: string[],
+ private readonly cancellationKey: string
) {
// TODO: this is only needed for the tests. Find a better way to test task output in the tests.
this.stdOutLoggerStream = new LoggerStream(logger, LogVerbosity.INFO);
}
- public setTask(task: vscode.Task): void {
- this.task = task;
+ public open(): void {
+ this.runBuild();
}
- public open(): void {
- this.doBuild();
+ public setTask(task: vscode.Task): void {
+ this.task = task;
}
public close(): void {
if (this.task && isTaskRunning(this.task)) {
- vscode.commands.executeCommand(COMMAND_CANCEL_TASK, this.task);
+ this.cancelCommand();
}
}
@@ -47,7 +49,7 @@ export class CustomBuildTaskTerminal implements vscode.Pseudoterminal {
try {
await waitOnTcp('localhost', javaDebugPort);
const startedDebugging = await vscode.debug.startDebugging(
- this.workspaceFolder,
+ this.rootProject.getWorkspaceFolder(),
{
type: 'java',
name: 'Debug (Attach) via Gradle Tasks',
@@ -65,21 +67,19 @@ export class CustomBuildTaskTerminal implements vscode.Pseudoterminal {
}
}
- private async doBuild(): Promise {
- const args: string[] = this.task!.definition.args.split(' ').filter(
- Boolean
- );
+ private async runBuild(): Promise {
+ const javaDebugEnabled = this.task ? this.task.definition.javaDebug : false;
try {
- const javaDebugEnabled = this.task!.definition.javaDebug;
const javaDebugPort = javaDebugEnabled ? await getPort() : 0;
const runTask = Extension.getInstance()
.getClient()
- .runTask(
- this.projectFolder.fsPath,
- this.task!,
- args,
+ .runBuild(
+ this.rootProject.getProjectUri().fsPath,
+ this.cancellationKey,
+ this.args,
'',
javaDebugPort,
+ this.task,
this.handleOutput,
true
);
@@ -94,6 +94,14 @@ export class CustomBuildTaskTerminal implements vscode.Pseudoterminal {
}
}
+ private cancelCommand(): void {
+ vscode.commands.executeCommand(
+ COMMAND_CANCEL_BUILD,
+ this.cancellationKey,
+ this.task
+ );
+ }
+
private handleOutput = (output: Output): void => {
const messageBytes = output.getOutputBytes_asU8();
if (messageBytes.length) {
@@ -111,7 +119,7 @@ export class CustomBuildTaskTerminal implements vscode.Pseudoterminal {
public handleInput(data: string): void {
// sigint eg cmd/ctrl+C
if (data === '\x03') {
- vscode.commands.executeCommand(COMMAND_CANCEL_TASK, this.task);
+ this.cancelCommand();
}
}
diff --git a/extension/src/terminal/index.ts b/extension/src/terminal/index.ts
index 38c469f60..79e11b45d 100644
--- a/extension/src/terminal/index.ts
+++ b/extension/src/terminal/index.ts
@@ -1 +1 @@
-export * from './CustomBuildTaskTerminal';
+export * from './GradleRunnerTerminal';
diff --git a/extension/src/test/integration/gradle/extension.test.ts b/extension/src/test/integration/gradle/extension.test.ts
index ac95277ca..c715f5b2f 100644
--- a/extension/src/test/integration/gradle/extension.test.ts
+++ b/extension/src/test/integration/gradle/extension.test.ts
@@ -84,7 +84,7 @@ describe(getSuiteName('Extension'), () => {
});
assert.ok(loggerAppendSpy.calledWith(sinon.match('Hello, World!')));
assert.ok(
- loggerAppendLineSpy.calledWith(sinon.match('Completed task: hello'))
+ loggerAppendLineSpy.calledWith(sinon.match('Completed build: hello'))
);
});
diff --git a/extension/src/test/unit/gradleDaemons.test.ts b/extension/src/test/unit/gradleDaemons.test.ts
index 15c39cfa0..f41242123 100644
--- a/extension/src/test/unit/gradleDaemons.test.ts
+++ b/extension/src/test/unit/gradleDaemons.test.ts
@@ -1,7 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
-// TODO: add tests for filtering out duplicate versions
-
import * as assert from 'assert';
import * as vscode from 'vscode';
import * as sinon from 'sinon';
diff --git a/extension/src/test/unit/gradleTasks.test.ts b/extension/src/test/unit/gradleTasks.test.ts
index 53f0094f3..a52ff9ee3 100644
--- a/extension/src/test/unit/gradleTasks.test.ts
+++ b/extension/src/test/unit/gradleTasks.test.ts
@@ -41,11 +41,12 @@ import {
COMMAND_SHOW_LOGS,
explorerFlatCommand,
explorerTreeCommand,
- cancelTaskCommand,
COMMAND_RENDER_TASK,
+ cancelBuildCommand,
} from '../../commands';
import { removeCancellingTask } from '../../tasks/taskUtil';
import { RootProjectsStore } from '../../stores';
+import { getRunTaskCommandCancellationKey } from '../../client/CancellationKeys';
const mockContext = buildMockContext();
const mockExtension = buildMockExtension();
@@ -101,10 +102,11 @@ mockGradleBuildWithoutTasks.setProject(mockGradleProjectWithoutTasks);
describe(getSuiteName('Gradle tasks'), () => {
beforeEach(() => {
const icons = new Icons(mockContext);
+ const rootProjectsStore = new RootProjectsStore();
const gradleTasksTreeDataProvider = new GradleTasksTreeDataProvider(
- mockContext
+ mockContext,
+ rootProjectsStore
);
- const rootProjectsStore = new RootProjectsStore();
const gradleTaskProvider = new GradleTaskProvider(rootProjectsStore);
const mockClient = buildMockClient();
mockExtension.getRootProjectsStore.returns(rootProjectsStore);
@@ -124,13 +126,10 @@ describe(getSuiteName('Gradle tasks'), () => {
});
describe('Without a multi-root workspace', () => {
- beforeEach(() => {
- sinon
- .stub(vscode.workspace, 'workspaceFolders')
- .value([mockWorkspaceFolder1]);
- });
-
describe('With no gradle tasks', () => {
+ beforeEach(() => {
+ stubWorkspaceFolders([mockWorkspaceFolder1]);
+ });
it('should build a "No Tasks" tree item when no tasks are found', async () => {
mockExtension
.getClient()
@@ -382,7 +381,11 @@ describe(getSuiteName('Gradle tasks'), () => {
vscode.commands,
'executeCommand'
);
- cancelTaskCommand(task);
+ const cancellationKey = getRunTaskCommandCancellationKey(
+ mockTaskDefinition1ForFolder1.projectFolder,
+ task.name
+ );
+ cancelBuildCommand(cancellationKey, task);
assert.ok(
executeCommandStub.calledWith(COMMAND_RENDER_TASK, task),
'Task was not rendered'
diff --git a/extension/src/test/unit/pinnedTasks.test.ts b/extension/src/test/unit/pinnedTasks.test.ts
index 0e361e3d6..6809ac89b 100644
--- a/extension/src/test/unit/pinnedTasks.test.ts
+++ b/extension/src/test/unit/pinnedTasks.test.ts
@@ -78,10 +78,11 @@ mockGradleBuild.setProject(mockGradleProject);
describe(getSuiteName('Pinned tasks'), () => {
beforeEach(() => {
const icons = new Icons(mockContext);
+ const rootProjectsStore = new RootProjectsStore();
const gradleTasksTreeDataProvider = new GradleTasksTreeDataProvider(
- mockContext
+ mockContext,
+ rootProjectsStore
);
- const rootProjectsStore = new RootProjectsStore();
const gradleTaskProvider = new GradleTaskProvider(rootProjectsStore);
const pinnedTasksStore = new PinnedTasksStore(mockContext);
const pinnedTasksTreeDataProvider = new PinnedTasksTreeDataProvider(
@@ -101,6 +102,7 @@ describe(getSuiteName('Pinned tasks'), () => {
gradleTasksTreeDataProvider
);
mockExtension.getIcons.returns(icons);
+ mockExtension.getRootProjectsStore.returns(rootProjectsStore);
sinon.stub(Extension, 'getInstance').returns(mockExtension);
logger.reset();
logger.setLoggingChannel(buildMockOutputChannel());
diff --git a/extension/src/test/unit/recentTasks.test.ts b/extension/src/test/unit/recentTasks.test.ts
index 8c8b4618c..81abec1b0 100644
--- a/extension/src/test/unit/recentTasks.test.ts
+++ b/extension/src/test/unit/recentTasks.test.ts
@@ -99,6 +99,7 @@ describe(getSuiteName('Recent tasks'), () => {
mockExtension.getGradleTaskProvider.returns(gradleTaskProvider);
mockExtension.getTaskTerminalsStore.returns(taskTerminalsStore);
mockExtension.getIcons.returns(icons);
+ mockExtension.getRootProjectsStore.returns(rootProjectsStore);
sinon.stub(Extension, 'getInstance').returns(mockExtension);
logger.reset();
diff --git a/extension/src/views/gradleTasks/GradleTasksTreeDataProvider.ts b/extension/src/views/gradleTasks/GradleTasksTreeDataProvider.ts
index 7c069ba75..f65512b9b 100644
--- a/extension/src/views/gradleTasks/GradleTasksTreeDataProvider.ts
+++ b/extension/src/views/gradleTasks/GradleTasksTreeDataProvider.ts
@@ -9,28 +9,30 @@ import {
TreeItemWithTasksOrGroups,
} from '..';
-import { JavaDebug, getConfigJavaDebug } from '../../config';
import { GradleTaskDefinition } from '../../tasks';
import { isWorkspaceFolder } from '../../util';
import { Extension } from '../../extension';
import { isGradleTask } from '../../tasks/taskUtil';
+import { RootProjectsStore } from '../../stores';
-// eslint-disable-next-line sonarjs/no-unused-collection
-export const gradleTaskTreeItemMap: Map = new Map();
-export const gradleProjectTreeItemMap: Map<
- string,
- RootProjectTreeItem
-> = new Map();
-export const projectTreeItemMap: Map = new Map();
-export const groupTreeItemMap: Map = new Map();
-export const gradleProjectJavaDebugMap: Map = new Map();
+const gradleTaskTreeItemMap: Map = new Map();
+const gradleProjectTreeItemMap: Map = new Map();
+const projectTreeItemMap: Map = new Map();
+const groupTreeItemMap: Map = new Map();
+
+export function getGradleTaskTreeItemMap(): Map {
+ return gradleTaskTreeItemMap;
+}
+
+export function getProjectTreeItemMap(): Map {
+ return projectTreeItemMap;
+}
function resetCachedTreeItems(): void {
gradleTaskTreeItemMap.clear();
gradleProjectTreeItemMap.clear();
projectTreeItemMap.clear();
groupTreeItemMap.clear();
- gradleProjectJavaDebugMap.clear();
}
export class GradleTasksTreeDataProvider
@@ -41,7 +43,10 @@ export class GradleTasksTreeDataProvider
public readonly onDidChangeTreeData: vscode.Event = this
._onDidChangeTreeData.event;
- constructor(private readonly context: vscode.ExtensionContext) {
+ constructor(
+ private readonly context: vscode.ExtensionContext,
+ private readonly rootProjectStore: RootProjectsStore
+ ) {
const collapsed = this.context.workspaceState.get(
'gradleTasksCollapsed',
false
@@ -125,13 +130,17 @@ export class GradleTasksTreeDataProvider
tasks.forEach((task) => {
const definition = task.definition as GradleTaskDefinition;
if (isWorkspaceFolder(task.scope) && isGradleTask(task)) {
+ const rootProject = this.rootProjectStore.get(definition.projectFolder);
+ if (!rootProject) {
+ return;
+ }
gradleProjectTreeItem = gradleProjectTreeItemMap.get(
definition.projectFolder
);
if (!gradleProjectTreeItem) {
gradleProjectTreeItem = new RootProjectTreeItem(
path.basename(definition.projectFolder),
- vscode.Uri.file(definition.projectFolder)
+ rootProject.getProjectUri()
);
gradleProjectTreeItemMap.set(
definition.projectFolder,
@@ -139,19 +148,6 @@ export class GradleTasksTreeDataProvider
);
}
- if (!gradleProjectJavaDebugMap.has(definition.projectFolder)) {
- const workspaceFolder = vscode.workspace.getWorkspaceFolder(
- vscode.Uri.file(definition.projectFolder)
- );
- if (!workspaceFolder) {
- return;
- }
- gradleProjectJavaDebugMap.set(
- definition.projectFolder,
- getConfigJavaDebug(workspaceFolder)
- );
- }
-
let projectTreeItem = projectTreeItemMap.get(definition.buildFile);
if (!projectTreeItem) {
projectTreeItem = new ProjectTreeItem(
@@ -189,7 +185,7 @@ export class GradleTasksTreeDataProvider
taskName,
definition.description || taskName,
'',
- gradleProjectJavaDebugMap.get(definition.projectFolder)
+ rootProject.getJavaDebug()
);
taskTreeItem.setContext();
diff --git a/extension/src/views/pinnedTasks/PinnedTasksTreeDataProvider.ts b/extension/src/views/pinnedTasks/PinnedTasksTreeDataProvider.ts
index e39340e6a..0db160efe 100644
--- a/extension/src/views/pinnedTasks/PinnedTasksTreeDataProvider.ts
+++ b/extension/src/views/pinnedTasks/PinnedTasksTreeDataProvider.ts
@@ -5,43 +5,49 @@ import {
PinnedTaskTreeItem,
NoPinnedTasksTreeItem,
} from '.';
-import { GradleTaskTreeItem, gradleProjectJavaDebugMap } from '..';
+import { GradleTaskTreeItem } from '..';
import { GradleTaskDefinition } from '../../tasks';
import { isWorkspaceFolder } from '../../util';
import { PinnedTasksStore, RootProjectsStore } from '../../stores';
import { Extension } from '../../extension';
import { TaskId, TaskArgs } from '../../stores/types';
import { cloneTask, isGradleTask } from '../../tasks/taskUtil';
+import { RootProject } from '../../rootProject/RootProject';
const pinnedTasksGradleProjectTreeItemMap: Map<
string,
PinnedTasksRootProjectTreeItem
> = new Map();
-// eslint-disable-next-line sonarjs/no-unused-collection
-export const pinnedTasksTreeItemMap: Map<
- string,
- PinnedTaskTreeItem
-> = new Map();
+const pinnedTasksTreeItemMap: Map = new Map();
+
+export function getPinnedTasksTreeItemMap(): Map {
+ return pinnedTasksTreeItemMap;
+}
function buildTaskTreeItem(
gradleProjectTreeItem: PinnedTasksRootProjectTreeItem,
- task: vscode.Task
+ task: vscode.Task,
+ rootProject: RootProject
): GradleTaskTreeItem {
const definition = task.definition as GradleTaskDefinition;
+ const taskName = task.name;
const pinnedTaskTreeItem = new PinnedTaskTreeItem(
gradleProjectTreeItem,
task,
- task.name,
- definition.description || task.name,
- '',
- gradleProjectJavaDebugMap.get(definition.projectFolder)
+ taskName,
+ definition.description || taskName, // tooltip
+ '', // description
+ rootProject.getJavaDebug()
);
pinnedTaskTreeItem.setContext();
return pinnedTaskTreeItem;
}
-function buildGradleProjectTreeItem(task: vscode.Task): void {
+function buildGradleProjectTreeItem(
+ task: vscode.Task,
+ rootProject: RootProject
+): void {
const definition = task.definition as GradleTaskDefinition;
if (isWorkspaceFolder(task.scope) && isGradleTask(task)) {
let gradleProjectTreeItem = pinnedTasksGradleProjectTreeItemMap.get(
@@ -57,7 +63,11 @@ function buildGradleProjectTreeItem(task: vscode.Task): void {
);
}
- const pinnedTaskTreeItem = buildTaskTreeItem(gradleProjectTreeItem, task);
+ const pinnedTaskTreeItem = buildTaskTreeItem(
+ gradleProjectTreeItem,
+ task,
+ rootProject
+ );
pinnedTasksTreeItemMap.set(
definition.id + definition.args,
pinnedTaskTreeItem
@@ -136,11 +146,16 @@ export class PinnedTasksTreeDataProvider
if (!task) {
return;
}
+ const definition = task.definition as GradleTaskDefinition;
+ const rootProject = this.rootProjectsStore.get(definition.projectFolder);
+ if (!rootProject) {
+ return;
+ }
const taskArgs = pinnedTasks.get(taskId) || '';
if (taskArgs) {
Array.from(taskArgs.values()).forEach((args: TaskArgs) => {
const pinnedTask = cloneTask(task, args);
- buildGradleProjectTreeItem(pinnedTask);
+ buildGradleProjectTreeItem(pinnedTask, rootProject);
});
}
});
diff --git a/extension/src/views/recentTasks/RecentTaskTreeItem.ts b/extension/src/views/recentTasks/RecentTaskTreeItem.ts
index 319e5672e..a31929dfa 100644
--- a/extension/src/views/recentTasks/RecentTaskTreeItem.ts
+++ b/extension/src/views/recentTasks/RecentTaskTreeItem.ts
@@ -23,6 +23,7 @@ export class RecentTaskTreeItem extends GradleTaskTreeItem {
javaDebug: JavaDebug = { tasks: [] },
private readonly taskTerminalsStore: TaskTerminalsStore
) {
+ // On construction, don't set a description, this will be set in setContext
super(parentTreeItem, task, label, description || label, '', javaDebug);
}
diff --git a/extension/src/views/recentTasks/RecentTasksTreeDataProvider.ts b/extension/src/views/recentTasks/RecentTasksTreeDataProvider.ts
index c458ee6b9..5fabe6874 100644
--- a/extension/src/views/recentTasks/RecentTasksTreeDataProvider.ts
+++ b/extension/src/views/recentTasks/RecentTasksTreeDataProvider.ts
@@ -11,35 +11,38 @@ import {
RootProjectsStore,
} from '../../stores';
import { GradleTaskDefinition } from '../../tasks';
-import { gradleProjectJavaDebugMap, GradleTaskTreeItem } from '..';
+import { GradleTaskTreeItem } from '..';
import { isWorkspaceFolder } from '../../util';
import { Extension } from '../../extension';
import { TaskId, TaskArgs } from '../../stores/types';
import { cloneTask, isGradleTask } from '../../tasks/taskUtil';
+import { RootProject } from '../../rootProject/RootProject';
const recentTasksGradleProjectTreeItemMap: Map<
string,
RecentTasksRootProjectTreeItem
> = new Map();
-// eslint-disable-next-line sonarjs/no-unused-collection
-export const recentTasksTreeItemMap: Map<
- string,
- RecentTaskTreeItem
-> = new Map();
+const recentTasksTreeItemMap: Map = new Map();
+
+export function getRecentTaskTreeItemMap(): Map {
+ return recentTasksTreeItemMap;
+}
function buildTaskTreeItem(
gradleProjectTreeItem: RecentTasksRootProjectTreeItem,
task: vscode.Task,
+ rootProject: RootProject,
taskTerminalsStore: TaskTerminalsStore
): RecentTaskTreeItem {
const definition = task.definition as GradleTaskDefinition;
+ const taskName = task.name;
const recentTaskTreeItem = new RecentTaskTreeItem(
gradleProjectTreeItem,
task,
- task.name,
- definition.description,
- gradleProjectJavaDebugMap.get(definition.projectFolder),
+ taskName,
+ definition.description || taskName, // used for tooltip
+ rootProject.getJavaDebug(),
taskTerminalsStore
);
recentTaskTreeItem.setContext();
@@ -48,6 +51,7 @@ function buildTaskTreeItem(
function buildGradleProjectTreeItem(
task: vscode.Task,
+ rootProject: RootProject,
taskTerminalsStore: TaskTerminalsStore
): void {
const definition = task.definition as GradleTaskDefinition;
@@ -68,6 +72,7 @@ function buildGradleProjectTreeItem(
const recentTaskTreeItem = buildTaskTreeItem(
gradleProjectTreeItem,
task,
+ rootProject,
taskTerminalsStore
);
recentTasksTreeItemMap.set(
@@ -169,12 +174,19 @@ export class RecentTasksTreeDataProvider
return;
}
const definition = task.definition as GradleTaskDefinition;
+ const rootProject = this.rootProjectsStore.get(definition.projectFolder);
+ if (!rootProject) {
+ return;
+ }
const taskArgs = recentTasks.get(taskId) || '';
-
if (taskArgs) {
Array.from(taskArgs.values()).forEach((args: TaskArgs) => {
const recentTask = cloneTask(task, args, definition.javaDebug);
- buildGradleProjectTreeItem(recentTask, this.taskTerminalsStore);
+ buildGradleProjectTreeItem(
+ recentTask,
+ rootProject,
+ this.taskTerminalsStore
+ );
});
}
});
diff --git a/extension/src/views/viewUtil.ts b/extension/src/views/viewUtil.ts
index 0e69a4c34..5ffd31fc4 100644
--- a/extension/src/views/viewUtil.ts
+++ b/extension/src/views/viewUtil.ts
@@ -6,18 +6,18 @@ import {
TREE_ITEM_STATE_TASK_DEBUG_IDLE,
TREE_ITEM_STATE_TASK_IDLE,
} from './constants';
-import {
- gradleTaskTreeItemMap,
- pinnedTasksTreeItemMap,
- recentTasksTreeItemMap,
- projectTreeItemMap,
-} from '.';
import { GradleTaskDefinition } from '../tasks';
import { logger } from '../logger';
import { JavaDebug } from '../config';
import { TaskArgs } from '../stores/types';
import { isTaskCancelling, isTaskRunning } from '../tasks/taskUtil';
-import { GradleTaskTreeItem } from './gradleTasks';
+import {
+ GradleTaskTreeItem,
+ getGradleTaskTreeItemMap,
+ getProjectTreeItemMap,
+ getPinnedTasksTreeItemMap,
+ getRecentTaskTreeItemMap,
+} from '.';
import { Extension } from '../extension';
export function treeItemSortCompareFunc(
@@ -48,17 +48,17 @@ export function getTreeItemForTask(
task: vscode.Task
): GradleTaskTreeItem | null {
const definition = task.definition as GradleTaskDefinition;
- const gradleTaskTreeItem = gradleTaskTreeItemMap.get(definition.id);
+ const gradleTaskTreeItem = getGradleTaskTreeItemMap().get(definition.id);
if (gradleTaskTreeItem && gradleTaskTreeItem.task === task) {
return gradleTaskTreeItem;
}
- const pinnedTaskTreeItem = pinnedTasksTreeItemMap.get(
+ const pinnedTaskTreeItem = getPinnedTasksTreeItemMap().get(
definition.id + definition.args
);
if (pinnedTaskTreeItem && pinnedTaskTreeItem.task === task) {
return pinnedTaskTreeItem;
}
- const recentTaskTreeItem = recentTasksTreeItemMap.get(
+ const recentTaskTreeItem = getRecentTaskTreeItemMap().get(
definition.id + definition.args
);
if (recentTaskTreeItem && recentTaskTreeItem.task === task) {
@@ -69,20 +69,20 @@ export function getTreeItemForTask(
export function updateGradleTreeItemStateForTask(task: vscode.Task): void {
const definition = task.definition as GradleTaskDefinition;
- const gradleTaskTreeItem = gradleTaskTreeItemMap.get(definition.id);
+ const gradleTaskTreeItem = getGradleTaskTreeItemMap().get(definition.id);
const extension = Extension.getInstance();
if (gradleTaskTreeItem) {
gradleTaskTreeItem.setContext();
extension.getGradleTasksTreeDataProvider().refresh(gradleTaskTreeItem);
}
- const pinTaskTreeItem = pinnedTasksTreeItemMap.get(
+ const pinTaskTreeItem = getPinnedTasksTreeItemMap().get(
definition.id + definition.args
);
if (pinTaskTreeItem) {
pinTaskTreeItem.setContext();
extension.getPinnedTasksTreeDataProvider().refresh(pinTaskTreeItem);
}
- const recentTaskTreeItem = recentTasksTreeItemMap.get(
+ const recentTaskTreeItem = getRecentTaskTreeItemMap().get(
definition.id + definition.args
);
if (recentTaskTreeItem) {
@@ -98,7 +98,7 @@ export async function focusTaskInGradleTasksTree(
const definition = task.definition as GradleTaskDefinition;
const treeItem = getTreeItemForTask(task); // null if running task from command palette
if (treeItem === null || treeItem.constructor === GradleTaskTreeItem) {
- const gradleTaskTreeItem = gradleTaskTreeItemMap.get(definition.id);
+ const gradleTaskTreeItem = getGradleTaskTreeItemMap().get(definition.id);
if (gradleTaskTreeItem) {
await Extension.getInstance()
.getGradleTasksTreeView()
@@ -119,7 +119,7 @@ export async function focusProjectInGradleTasksTree(
await vscode.commands.executeCommand(
`workbench.view.extension.${GRADLE_CONTAINER_VIEW}`
);
- const treeItem = projectTreeItemMap.get(uri.fsPath);
+ const treeItem = getProjectTreeItemMap().get(uri.fsPath);
if (treeItem) {
await Extension.getInstance().getGradleTasksTreeView().reveal(treeItem, {
focus: true,
diff --git a/gradle-server/build.gradle b/gradle-server/build.gradle
index 63ab24b5d..8d45ba0da 100644
--- a/gradle-server/build.gradle
+++ b/gradle-server/build.gradle
@@ -2,7 +2,6 @@ plugins {
id 'java'
id 'application'
id 'com.github.johnrengelman.shadow' version '6.0.0'
- id 'com.diffplug.gradle.spotless'
}
description = 'vscode-gradle :: gradle-server'
@@ -123,9 +122,5 @@ task serverStartScripts(type: CreateStartScripts) {
)
}
-task(format) {
- dependsOn(spotlessApply)
-}
-
-compileJava.dependsOn 'generateProto', spotlessCheck
+compileJava.dependsOn 'generateProto', 'spotlessCheck'
assemble.dependsOn serverStartScripts
diff --git a/gradle-server/src/main/java/com/github/badsyntax/gradle/GradleBuildCancellation.java b/gradle-server/src/main/java/com/github/badsyntax/gradle/GradleBuildCancellation.java
new file mode 100644
index 000000000..766a635fa
--- /dev/null
+++ b/gradle-server/src/main/java/com/github/badsyntax/gradle/GradleBuildCancellation.java
@@ -0,0 +1,38 @@
+package com.github.badsyntax.gradle;
+
+import com.github.badsyntax.gradle.exceptions.GradleCancellationException;
+import com.google.common.base.Strings;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import org.gradle.tooling.CancellationToken;
+import org.gradle.tooling.CancellationTokenSource;
+import org.gradle.tooling.GradleConnector;
+
+public class GradleBuildCancellation {
+ private static final ConcurrentMap tokens =
+ new ConcurrentHashMap<>();
+
+ private GradleBuildCancellation() {}
+
+ public static CancellationToken buildToken(String cancellationKey) {
+ CancellationTokenSource cancellationTokenSource = GradleConnector.newCancellationTokenSource();
+ tokens.put(cancellationKey, cancellationTokenSource);
+ return cancellationTokenSource.token();
+ }
+
+ public static void clearToken(String cancellationKey) {
+ tokens.remove(cancellationKey);
+ }
+
+ public static void cancelBuild(String cancellationKey) throws GradleCancellationException {
+ if (Strings.isNullOrEmpty(cancellationKey)) {
+ throw new GradleCancellationException("No cancellation key specified");
+ }
+ CancellationTokenSource cancellationTokenSource = tokens.get(cancellationKey);
+ if (cancellationTokenSource == null) {
+ throw new GradleCancellationException("Build is not running for key: " + cancellationKey);
+ } else {
+ cancellationTokenSource.cancel();
+ }
+ }
+}
diff --git a/gradle-server/src/main/java/com/github/badsyntax/gradle/GradleBuildRunner.java b/gradle-server/src/main/java/com/github/badsyntax/gradle/GradleBuildRunner.java
new file mode 100644
index 000000000..3b27be966
--- /dev/null
+++ b/gradle-server/src/main/java/com/github/badsyntax/gradle/GradleBuildRunner.java
@@ -0,0 +1,127 @@
+package com.github.badsyntax.gradle;
+
+import com.github.badsyntax.gradle.exceptions.GradleBuildRunnerException;
+import com.github.badsyntax.gradle.exceptions.GradleConnectionException;
+import com.google.common.base.Strings;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.gradle.tooling.BuildLauncher;
+import org.gradle.tooling.CancellationToken;
+import org.gradle.tooling.GradleConnector;
+import org.gradle.tooling.ProjectConnection;
+import org.gradle.tooling.events.OperationType;
+import org.gradle.tooling.events.ProgressListener;
+
+public class GradleBuildRunner {
+ private static final String JAVA_TOOL_OPTIONS_ENV = "JAVA_TOOL_OPTIONS";
+
+ private String projectDir;
+ private List args;
+ private GradleConfig gradleConfig;
+ private String cancellationKey;
+ private Boolean colorOutput;
+ private int javaDebugPort;
+ private OutputStream standardOutputStream;
+ private OutputStream standardErrorStream;
+ private InputStream standardInputStream;
+ private ProgressListener progressListener;
+
+ public GradleBuildRunner(
+ String projectDir,
+ List args,
+ GradleConfig gradleConfig,
+ String cancellationKey,
+ Boolean colorOutput,
+ int javaDebugPort) {
+ this.projectDir = projectDir;
+ this.args = args;
+ this.gradleConfig = gradleConfig;
+ this.cancellationKey = cancellationKey;
+ this.colorOutput = colorOutput;
+ this.javaDebugPort = javaDebugPort;
+ }
+
+ public GradleBuildRunner(
+ String projectDir, List args, GradleConfig gradleConfig, String cancellationKey) {
+ this(projectDir, args, gradleConfig, cancellationKey, true, 0);
+ }
+
+ public GradleBuildRunner setStandardOutputStream(OutputStream standardOutputStream) {
+ this.standardOutputStream = standardOutputStream;
+ return this;
+ }
+
+ public GradleBuildRunner setStandardInputStream(InputStream standardInputStream) {
+ this.standardInputStream = standardInputStream;
+ return this;
+ }
+
+ public GradleBuildRunner setStandardErrorStream(OutputStream standardErrorStream) {
+ this.standardErrorStream = standardErrorStream;
+ return this;
+ }
+
+ public GradleBuildRunner setProgressListener(ProgressListener progressListener) {
+ this.progressListener = progressListener;
+ return this;
+ }
+
+ public void run() throws GradleConnectionException, IOException, GradleBuildRunnerException {
+ GradleConnector gradleConnector = GradleProjectConnector.build(projectDir, gradleConfig);
+ try (ProjectConnection connection = gradleConnector.connect()) {
+ runBuild(connection);
+ } finally {
+ GradleBuildCancellation.clearToken(cancellationKey);
+ }
+ }
+
+ private void runBuild(ProjectConnection connection)
+ throws GradleBuildRunnerException, IOException {
+ Set progressEvents = new HashSet<>();
+ progressEvents.add(OperationType.PROJECT_CONFIGURATION);
+ progressEvents.add(OperationType.TASK);
+ progressEvents.add(OperationType.TRANSFORM);
+
+ CancellationToken cancellationToken = GradleBuildCancellation.buildToken(cancellationKey);
+
+ BuildLauncher build =
+ connection
+ .newBuild()
+ .withCancellationToken(cancellationToken)
+ .addProgressListener(progressListener, progressEvents)
+ .setStandardOutput(standardOutputStream)
+ .setStandardError(standardErrorStream)
+ .setColorOutput(colorOutput)
+ .withArguments(args);
+
+ if (this.standardInputStream != null) {
+ build.setStandardInput(standardInputStream);
+ }
+
+ if (javaDebugPort != 0) {
+ build.setEnvironmentVariables(buildJavaEnvVarsWithJwdp(javaDebugPort));
+ }
+
+ if (!Strings.isNullOrEmpty(gradleConfig.getJvmArguments())) {
+ build.setJvmArguments(gradleConfig.getJvmArguments());
+ }
+
+ build.run();
+ }
+
+ private static Map buildJavaEnvVarsWithJwdp(int javaDebugPort) {
+ HashMap envVars = new HashMap<>(System.getenv());
+ envVars.put(
+ JAVA_TOOL_OPTIONS_ENV,
+ String.format(
+ "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=localhost:%d",
+ javaDebugPort));
+ return envVars;
+ }
+}
diff --git a/gradle-server/src/main/java/com/github/badsyntax/gradle/GradleService.java b/gradle-server/src/main/java/com/github/badsyntax/gradle/GradleService.java
index 3b3f7094a..650f76484 100644
--- a/gradle-server/src/main/java/com/github/badsyntax/gradle/GradleService.java
+++ b/gradle-server/src/main/java/com/github/badsyntax/gradle/GradleService.java
@@ -1,10 +1,9 @@
package com.github.badsyntax.gradle;
-import com.github.badsyntax.gradle.cancellation.CancellationHandler;
-import com.github.badsyntax.gradle.handlers.CancelTaskHandler;
+import com.github.badsyntax.gradle.handlers.CancelBuildHandler;
import com.github.badsyntax.gradle.handlers.GetBuildHandler;
import com.github.badsyntax.gradle.handlers.GetDaemonsStatusHandler;
-import com.github.badsyntax.gradle.handlers.RunTaskHandler;
+import com.github.badsyntax.gradle.handlers.RunBuildHandler;
import com.github.badsyntax.gradle.handlers.StopDaemonHandler;
import com.github.badsyntax.gradle.handlers.StopDaemonsHandler;
import io.grpc.stub.StreamObserver;
@@ -18,34 +17,16 @@ public void getBuild(GetBuildRequest req, StreamObserver response
}
@Override
- public void runTask(RunTaskRequest req, StreamObserver responseObserver) {
- RunTaskHandler runTaskHandler = new RunTaskHandler(req, responseObserver);
- runTaskHandler.run();
+ public void runBuild(RunBuildRequest req, StreamObserver responseObserver) {
+ RunBuildHandler runBuildHandler = new RunBuildHandler(req, responseObserver);
+ runBuildHandler.run();
}
@Override
- public void cancelGetBuilds(
- CancelGetBuildsRequest req, StreamObserver responseObserver) {
- CancellationHandler.cancelAllRunningBuilds();
- responseObserver.onNext(
- CancelGetBuildsReply.newBuilder().setMessage("Cancel build projects requested").build());
- responseObserver.onCompleted();
- }
-
- @Override
- public void cancelRunTask(
- CancelRunTaskRequest req, StreamObserver responseObserver) {
- CancelTaskHandler cancelTaskHandler = new CancelTaskHandler(req, responseObserver);
- cancelTaskHandler.run();
- }
-
- @Override
- public void cancelRunTasks(
- CancelRunTasksRequest req, StreamObserver responseObserver) {
- CancellationHandler.cancelAllRunningTasks();
- responseObserver.onNext(
- CancelRunTasksReply.newBuilder().setMessage("Cancel running tasks requested").build());
- responseObserver.onCompleted();
+ public void cancelBuild(
+ CancelBuildRequest req, StreamObserver responseObserver) {
+ CancelBuildHandler cancelRunBuildHandler = new CancelBuildHandler(req, responseObserver);
+ cancelRunBuildHandler.run();
}
@Override
diff --git a/gradle-server/src/main/java/com/github/badsyntax/gradle/cancellation/CancellationHandler.java b/gradle-server/src/main/java/com/github/badsyntax/gradle/cancellation/CancellationHandler.java
deleted file mode 100644
index 0c0c10c89..000000000
--- a/gradle-server/src/main/java/com/github/badsyntax/gradle/cancellation/CancellationHandler.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.github.badsyntax.gradle.cancellation;
-
-import com.github.badsyntax.gradle.exceptions.GradleCancellationException;
-import org.gradle.tooling.CancellationToken;
-import org.gradle.tooling.CancellationTokenSource;
-import org.gradle.tooling.GradleConnector;
-
-public class CancellationHandler {
- private static final CancellationTokenPool cancellationTokenPool = new CancellationTokenPool();
-
- private CancellationHandler() {}
-
- public static CancellationToken getCancellationToken(
- CancellationTokenPool.TYPE cancellationType, String cancellationKey) {
- CancellationTokenSource cancellationTokenSource = GradleConnector.newCancellationTokenSource();
- cancellationTokenPool.put(cancellationType, cancellationKey, cancellationTokenSource);
- return cancellationTokenSource.token();
- }
-
- public static CancellationToken getBuildCancellationToken(String cancellationKey) {
- return getCancellationToken(CancellationTokenPool.TYPE.GET, cancellationKey);
- }
-
- public static CancellationToken getRunTaskCancellationToken(String cancellationKey) {
- return getCancellationToken(CancellationTokenPool.TYPE.RUN, cancellationKey);
- }
-
- public static void clearToken(
- CancellationTokenPool.TYPE cancellationType, String cancellationKey) {
- cancellationTokenPool.remove(cancellationType, cancellationKey);
- }
-
- public static void clearBuildToken(String cancellationKey) {
- clearToken(CancellationTokenPool.TYPE.GET, cancellationKey);
- }
-
- public static void clearRunTaskToken(String cancellationKey) {
- clearToken(CancellationTokenPool.TYPE.RUN, cancellationKey);
- }
-
- public static void cancelRunTask(String cancellationKey) throws GradleCancellationException {
- CancellationTokenSource cancellationTokenSource =
- cancellationTokenPool.get(CancellationTokenPool.TYPE.RUN, cancellationKey);
- if (cancellationTokenSource == null) {
- throw new GradleCancellationException("Task is not running");
- } else {
- cancellationTokenSource.cancel();
- }
- }
-
- public static void cancelAllRunningBuilds() {
- cancellationTokenPool.cancel(CancellationTokenPool.TYPE.GET);
- }
-
- public static void cancelAllRunningTasks() {
- cancellationTokenPool.cancel(CancellationTokenPool.TYPE.RUN);
- }
-}
diff --git a/gradle-server/src/main/java/com/github/badsyntax/gradle/cancellation/CancellationTokenPool.java b/gradle-server/src/main/java/com/github/badsyntax/gradle/cancellation/CancellationTokenPool.java
deleted file mode 100644
index fc41f9397..000000000
--- a/gradle-server/src/main/java/com/github/badsyntax/gradle/cancellation/CancellationTokenPool.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.github.badsyntax.gradle.cancellation;
-
-import java.util.EnumMap;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import org.gradle.tooling.CancellationTokenSource;
-
-public class CancellationTokenPool {
-
- public enum TYPE {
- RUN,
- GET
- }
-
- private final ConcurrentMap runTaskTokens =
- new ConcurrentHashMap<>();
- private final ConcurrentMap getTasksTokens =
- new ConcurrentHashMap<>();
- private final EnumMap> pool =
- new EnumMap<>(TYPE.class);
-
- public CancellationTokenPool() {
- pool.put(TYPE.RUN, runTaskTokens);
- pool.put(TYPE.GET, getTasksTokens);
- }
-
- public CancellationTokenSource get(TYPE type, String key) {
- return pool.get(type).get(key);
- }
-
- public void put(TYPE type, String key, CancellationTokenSource tokenSource) {
- pool.get(type).put(key, tokenSource);
- }
-
- public void remove(TYPE type, String key) {
- pool.get(type).remove(key);
- }
-
- public Map getPoolType(TYPE type) {
- return pool.get(type);
- }
-
- public Map> getPool() {
- return pool;
- }
-
- public void cancelAll() {
- pool.keySet().stream()
- .forEach(
- typeKey ->
- pool.get(typeKey).keySet().stream()
- .forEach(poolKey -> pool.get(typeKey).get(poolKey).cancel()));
- }
-
- public void cancel(CancellationTokenPool.TYPE type) {
- Map poolOfType = getPoolType(type);
- poolOfType.keySet().stream().forEach(key -> poolOfType.get(key).cancel());
- }
-}
diff --git a/gradle-server/src/main/java/com/github/badsyntax/gradle/exceptions/GradleBuildRunnerException.java b/gradle-server/src/main/java/com/github/badsyntax/gradle/exceptions/GradleBuildRunnerException.java
new file mode 100644
index 000000000..1172f132f
--- /dev/null
+++ b/gradle-server/src/main/java/com/github/badsyntax/gradle/exceptions/GradleBuildRunnerException.java
@@ -0,0 +1,13 @@
+package com.github.badsyntax.gradle.exceptions;
+
+public class GradleBuildRunnerException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public GradleBuildRunnerException(String message) {
+ super(message);
+ }
+
+ public GradleBuildRunnerException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/gradle-server/src/main/java/com/github/badsyntax/gradle/exceptions/GradleTaskRunnerException.java b/gradle-server/src/main/java/com/github/badsyntax/gradle/exceptions/GradleTaskRunnerException.java
deleted file mode 100644
index be03dee0f..000000000
--- a/gradle-server/src/main/java/com/github/badsyntax/gradle/exceptions/GradleTaskRunnerException.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.github.badsyntax.gradle.exceptions;
-
-public class GradleTaskRunnerException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public GradleTaskRunnerException(String message) {
- super(message);
- }
-
- public GradleTaskRunnerException(String message, Throwable cause) {
- super(message, cause);
- }
-}
diff --git a/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/CancelBuildHandler.java b/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/CancelBuildHandler.java
new file mode 100644
index 000000000..2089a35a8
--- /dev/null
+++ b/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/CancelBuildHandler.java
@@ -0,0 +1,47 @@
+package com.github.badsyntax.gradle.handlers;
+
+import com.github.badsyntax.gradle.CancelBuildReply;
+import com.github.badsyntax.gradle.CancelBuildRequest;
+import com.github.badsyntax.gradle.GradleBuildCancellation;
+import com.github.badsyntax.gradle.exceptions.GradleCancellationException;
+import io.grpc.stub.StreamObserver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CancelBuildHandler {
+ private static final Logger logger = LoggerFactory.getLogger(CancelBuildHandler.class.getName());
+
+ private CancelBuildRequest req;
+ private StreamObserver responseObserver;
+
+ public CancelBuildHandler(
+ CancelBuildRequest req, StreamObserver responseObserver) {
+ this.req = req;
+ this.responseObserver = responseObserver;
+ }
+
+ public void run() {
+ try {
+ GradleBuildCancellation.cancelBuild(req.getCancellationKey());
+ replyWithCancelledSuccess();
+ } catch (GradleCancellationException e) {
+ logger.error(e.getMessage());
+ replyWithCancelError(e);
+ } finally {
+ responseObserver.onCompleted();
+ }
+ }
+
+ private void replyWithCancelledSuccess() {
+ responseObserver.onNext(
+ CancelBuildReply.newBuilder()
+ .setMessage("Cancel run build requested")
+ .setBuildRunning(true)
+ .build());
+ }
+
+ private void replyWithCancelError(Exception e) {
+ responseObserver.onNext(
+ CancelBuildReply.newBuilder().setMessage(e.getMessage()).setBuildRunning(false).build());
+ }
+}
diff --git a/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/CancelTaskHandler.java b/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/CancelTaskHandler.java
deleted file mode 100644
index eb54e300d..000000000
--- a/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/CancelTaskHandler.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.github.badsyntax.gradle.handlers;
-
-import com.github.badsyntax.gradle.CancelRunTaskReply;
-import com.github.badsyntax.gradle.CancelRunTaskRequest;
-import com.github.badsyntax.gradle.cancellation.CancellationHandler;
-import com.github.badsyntax.gradle.exceptions.GradleCancellationException;
-import io.grpc.stub.StreamObserver;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class CancelTaskHandler {
- private static final Logger logger = LoggerFactory.getLogger(CancelTaskHandler.class.getName());
-
- private CancelRunTaskRequest req;
- private StreamObserver responseObserver;
-
- public CancelTaskHandler(
- CancelRunTaskRequest req, StreamObserver responseObserver) {
- this.req = req;
- this.responseObserver = responseObserver;
- }
-
- public void run() {
- try {
- CancellationHandler.cancelRunTask(
- RunTaskHandler.getCancellationKey(req.getProjectDir(), req.getTask()));
- replyWithCancelledSuccess();
- } catch (GradleCancellationException e) {
- logger.error(e.getMessage());
- replyWithCancelError(e);
- } finally {
- responseObserver.onCompleted();
- }
- }
-
- private void replyWithCancelledSuccess() {
- responseObserver.onNext(
- CancelRunTaskReply.newBuilder()
- .setMessage("Cancel run task requested")
- .setTaskRunning(true)
- .build());
- }
-
- private void replyWithCancelError(Exception e) {
- responseObserver.onNext(
- CancelRunTaskReply.newBuilder().setMessage(e.getMessage()).setTaskRunning(false).build());
- }
-}
diff --git a/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/GetBuildHandler.java b/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/GetBuildHandler.java
index 756cd3a1f..14f7d06a1 100644
--- a/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/GetBuildHandler.java
+++ b/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/GetBuildHandler.java
@@ -8,6 +8,7 @@
import com.github.badsyntax.gradle.GetBuildRequest;
import com.github.badsyntax.gradle.GetBuildResult;
import com.github.badsyntax.gradle.GradleBuild;
+import com.github.badsyntax.gradle.GradleBuildCancellation;
import com.github.badsyntax.gradle.GradleEnvironment;
import com.github.badsyntax.gradle.GradleProject;
import com.github.badsyntax.gradle.GradleProjectConnector;
@@ -15,7 +16,6 @@
import com.github.badsyntax.gradle.JavaEnvironment;
import com.github.badsyntax.gradle.Output;
import com.github.badsyntax.gradle.Progress;
-import com.github.badsyntax.gradle.cancellation.CancellationHandler;
import com.github.badsyntax.gradle.exceptions.GradleConnectionException;
import com.google.common.base.Strings;
import com.google.protobuf.ByteString;
@@ -25,6 +25,7 @@
import java.util.Set;
import org.gradle.internal.service.ServiceCreationException;
import org.gradle.tooling.BuildCancelledException;
+import org.gradle.tooling.CancellationToken;
import org.gradle.tooling.GradleConnector;
import org.gradle.tooling.ModelBuilder;
import org.gradle.tooling.ProjectConnection;
@@ -46,14 +47,6 @@ public GetBuildHandler(GetBuildRequest req, StreamObserver respon
this.responseObserver = responseObserver;
}
- public static String getCancellationKey(String projectDir) {
- return projectDir;
- }
-
- public String getCancellationKey() {
- return GetBuildHandler.getCancellationKey(req.getProjectDir());
- }
-
public void run() {
GradleConnector gradleConnector;
try {
@@ -77,7 +70,7 @@ public void run() {
logger.error(e.getMessage());
replyWithError(e);
} finally {
- CancellationHandler.clearBuildToken(getCancellationKey());
+ GradleBuildCancellation.clearToken(req.getCancellationKey());
}
}
@@ -97,8 +90,12 @@ private org.gradle.tooling.model.GradleProject buildGradleProject(ProjectConnect
replyWithProgress(event);
}
};
+
+ CancellationToken cancellationToken =
+ GradleBuildCancellation.buildToken(req.getCancellationKey());
+
projectBuilder
- .withCancellationToken(CancellationHandler.getBuildCancellationToken(getCancellationKey()))
+ .withCancellationToken(cancellationToken)
.addProgressListener(progressListener, progressEvents)
.setStandardOutput(
new ByteBufferOutputStream() {
@@ -173,7 +170,7 @@ private Environment buildEnvironment(ProjectConnection connection) {
.build();
}
- public void replyWithProject(GradleProject gradleProject) {
+ private void replyWithProject(GradleProject gradleProject) {
responseObserver.onNext(
GetBuildReply.newBuilder()
.setGetBuildResult(
@@ -183,7 +180,7 @@ public void replyWithProject(GradleProject gradleProject) {
responseObserver.onCompleted();
}
- public void replyWithCancelled(BuildCancelledException e) {
+ private void replyWithCancelled(BuildCancelledException e) {
responseObserver.onNext(
GetBuildReply.newBuilder()
.setCancelled(
@@ -194,11 +191,11 @@ public void replyWithCancelled(BuildCancelledException e) {
responseObserver.onCompleted();
}
- public void replyWithError(Exception e) {
+ private void replyWithError(Exception e) {
responseObserver.onError(ErrorMessageBuilder.build(e));
}
- public void replyWithBuildEnvironment(Environment environment) {
+ private void replyWithBuildEnvironment(Environment environment) {
responseObserver.onNext(GetBuildReply.newBuilder().setEnvironment(environment).build());
}
diff --git a/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/RunBuildHandler.java b/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/RunBuildHandler.java
new file mode 100644
index 000000000..f37ce799c
--- /dev/null
+++ b/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/RunBuildHandler.java
@@ -0,0 +1,145 @@
+package com.github.badsyntax.gradle.handlers;
+
+import com.github.badsyntax.gradle.ByteBufferOutputStream;
+import com.github.badsyntax.gradle.Cancelled;
+import com.github.badsyntax.gradle.ErrorMessageBuilder;
+import com.github.badsyntax.gradle.GradleBuildRunner;
+import com.github.badsyntax.gradle.Output;
+import com.github.badsyntax.gradle.Progress;
+import com.github.badsyntax.gradle.RunBuildReply;
+import com.github.badsyntax.gradle.RunBuildRequest;
+import com.github.badsyntax.gradle.RunBuildResult;
+import com.github.badsyntax.gradle.exceptions.GradleBuildRunnerException;
+import com.github.badsyntax.gradle.exceptions.GradleConnectionException;
+import com.google.common.base.Strings;
+import com.google.protobuf.ByteString;
+import io.grpc.stub.StreamObserver;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import org.gradle.tooling.BuildCancelledException;
+import org.gradle.tooling.BuildException;
+import org.gradle.tooling.UnsupportedVersionException;
+import org.gradle.tooling.events.ProgressEvent;
+import org.gradle.tooling.exceptions.UnsupportedBuildArgumentException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RunBuildHandler {
+ private static final Logger logger = LoggerFactory.getLogger(RunBuildHandler.class.getName());
+
+ private RunBuildRequest req;
+ private StreamObserver responseObserver;
+
+ public RunBuildHandler(RunBuildRequest req, StreamObserver responseObserver) {
+ this.req = req;
+ this.responseObserver = responseObserver;
+ }
+
+ public void run() {
+ GradleBuildRunner gradleRunner =
+ new GradleBuildRunner(
+ req.getProjectDir(),
+ req.getArgsList(),
+ req.getGradleConfig(),
+ req.getCancellationKey(),
+ req.getShowOutputColors(),
+ req.getJavaDebugPort());
+ gradleRunner
+ .setProgressListener(
+ (ProgressEvent event) -> {
+ synchronized (RunBuildHandler.class) {
+ replyWithProgress(event);
+ }
+ })
+ .setStandardOutputStream(
+ new ByteBufferOutputStream() {
+ @Override
+ public void onFlush(byte[] bytes) {
+ synchronized (RunBuildHandler.class) {
+ replyWithStandardOutput(bytes);
+ }
+ }
+ })
+ .setStandardErrorStream(
+ new ByteBufferOutputStream() {
+ @Override
+ public void onFlush(byte[] bytes) {
+ synchronized (RunBuildHandler.class) {
+ replyWithStandardError(bytes);
+ }
+ }
+ });
+
+ if (!Strings.isNullOrEmpty(req.getInput())) {
+ gradleRunner.setStandardInputStream(new ByteArrayInputStream(req.getInput().getBytes()));
+ }
+
+ try {
+ gradleRunner.run();
+ replyWithSuccess();
+ responseObserver.onCompleted();
+ } catch (BuildCancelledException e) {
+ replyWithCancelled(e);
+ responseObserver.onCompleted();
+ } catch (GradleConnectionException
+ | BuildException
+ | UnsupportedVersionException
+ | UnsupportedBuildArgumentException
+ | IllegalStateException
+ | IOException
+ | GradleBuildRunnerException e) {
+ logger.error(e.getMessage());
+ replyWithError(e);
+ }
+ }
+
+ public void replyWithCancelled(BuildCancelledException e) {
+ responseObserver.onNext(
+ RunBuildReply.newBuilder()
+ .setCancelled(
+ Cancelled.newBuilder()
+ .setMessage(e.getMessage())
+ .setProjectDir(req.getProjectDir()))
+ .build());
+ }
+
+ public void replyWithError(Exception e) {
+ responseObserver.onError(ErrorMessageBuilder.build(e));
+ }
+
+ public void replyWithSuccess() {
+ responseObserver.onNext(
+ RunBuildReply.newBuilder()
+ .setRunBuildResult(RunBuildResult.newBuilder().setMessage("Successfully run build"))
+ .build());
+ }
+
+ private void replyWithProgress(ProgressEvent progressEvent) {
+ responseObserver.onNext(
+ RunBuildReply.newBuilder()
+ .setProgress(Progress.newBuilder().setMessage(progressEvent.getDisplayName()))
+ .build());
+ }
+
+ private void replyWithStandardOutput(byte[] bytes) {
+ ByteString byteString = ByteString.copyFrom(bytes);
+ responseObserver.onNext(
+ RunBuildReply.newBuilder()
+ .setOutput(
+ Output.newBuilder()
+ .setOutputType(Output.OutputType.STDOUT)
+ .setOutputBytes(byteString))
+ .build());
+ }
+
+ private void replyWithStandardError(byte[] bytes) {
+ ByteString byteString = ByteString.copyFrom(bytes);
+ responseObserver.onNext(
+ RunBuildReply.newBuilder()
+ .setOutput(
+ Output.newBuilder()
+ .setOutputType(Output.OutputType.STDERR)
+ .setOutputBytes(byteString))
+ .build());
+ }
+}
diff --git a/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/RunTaskHandler.java b/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/RunTaskHandler.java
deleted file mode 100644
index 00e7af507..000000000
--- a/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/RunTaskHandler.java
+++ /dev/null
@@ -1,216 +0,0 @@
-package com.github.badsyntax.gradle.handlers;
-
-import com.github.badsyntax.gradle.ByteBufferOutputStream;
-import com.github.badsyntax.gradle.Cancelled;
-import com.github.badsyntax.gradle.ErrorMessageBuilder;
-import com.github.badsyntax.gradle.GradleProjectConnector;
-import com.github.badsyntax.gradle.Output;
-import com.github.badsyntax.gradle.Progress;
-import com.github.badsyntax.gradle.RunTaskReply;
-import com.github.badsyntax.gradle.RunTaskRequest;
-import com.github.badsyntax.gradle.RunTaskResult;
-import com.github.badsyntax.gradle.cancellation.CancellationHandler;
-import com.github.badsyntax.gradle.exceptions.GradleConnectionException;
-import com.github.badsyntax.gradle.exceptions.GradleTaskRunnerException;
-import com.google.common.base.Strings;
-import com.google.protobuf.ByteString;
-import io.grpc.stub.StreamObserver;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import org.gradle.tooling.BuildCancelledException;
-import org.gradle.tooling.BuildException;
-import org.gradle.tooling.BuildLauncher;
-import org.gradle.tooling.GradleConnector;
-import org.gradle.tooling.ProjectConnection;
-import org.gradle.tooling.UnsupportedVersionException;
-import org.gradle.tooling.events.OperationType;
-import org.gradle.tooling.events.ProgressEvent;
-import org.gradle.tooling.events.ProgressListener;
-import org.gradle.tooling.exceptions.UnsupportedBuildArgumentException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class RunTaskHandler {
- private static final Logger logger = LoggerFactory.getLogger(RunTaskHandler.class.getName());
- private static final String JAVA_TOOL_OPTIONS_ENV = "JAVA_TOOL_OPTIONS";
-
- private RunTaskRequest req;
- private StreamObserver responseObserver;
-
- public RunTaskHandler(RunTaskRequest req, StreamObserver responseObserver) {
- this.req = req;
- this.responseObserver = responseObserver;
- }
-
- public static String getCancellationKey(String projectDir, String task) {
- return projectDir + task;
- }
-
- public String getCancellationKey() {
- return RunTaskHandler.getCancellationKey(req.getProjectDir(), req.getTask());
- }
-
- public void run() {
- GradleConnector gradleConnector;
- try {
- gradleConnector = GradleProjectConnector.build(req.getProjectDir(), req.getGradleConfig());
- } catch (GradleConnectionException e) {
- logger.error(e.getMessage());
- responseObserver.onError(ErrorMessageBuilder.build(e));
- return;
- }
-
- try (ProjectConnection connection = gradleConnector.connect()) {
- runTask(connection);
- replyWithSuccess();
- responseObserver.onCompleted();
- } catch (BuildCancelledException e) {
- replyWithCancelled(e);
- responseObserver.onCompleted();
- } catch (BuildException
- | UnsupportedVersionException
- | UnsupportedBuildArgumentException
- | IllegalStateException
- | IOException
- | GradleTaskRunnerException e) {
- logger.error(e.getMessage());
- replyWithError(e);
- } finally {
- CancellationHandler.clearRunTaskToken(getCancellationKey());
- }
- }
-
- public void runTask(ProjectConnection connection) throws GradleTaskRunnerException, IOException {
- Set progressEvents = new HashSet<>();
- progressEvents.add(OperationType.PROJECT_CONFIGURATION);
- progressEvents.add(OperationType.TASK);
- progressEvents.add(OperationType.TRANSFORM);
-
- ProgressListener progressListener =
- (ProgressEvent event) -> {
- synchronized (RunTaskHandler.class) {
- replyWithProgress(event);
- }
- };
-
- // Specifying the tasks to run via build arguments provides support for task *and* build
- // arguments.
- // Using BuildLauncher.forTasks() prevents us from specifying task args.
- ArrayList argsList = new ArrayList(req.getArgsList());
- argsList.add(0, req.getTask());
-
- BuildLauncher build =
- connection
- .newBuild()
- .withCancellationToken(
- CancellationHandler.getRunTaskCancellationToken(getCancellationKey()))
- .addProgressListener(progressListener, progressEvents)
- .setStandardOutput(
- new ByteBufferOutputStream() {
- @Override
- public void onFlush(byte[] bytes) {
- synchronized (RunTaskHandler.class) {
- replyWithStandardOutput(bytes);
- }
- }
- })
- .setStandardError(
- new ByteBufferOutputStream() {
- @Override
- public void onFlush(byte[] bytes) {
- synchronized (RunTaskHandler.class) {
- replyWithStandardError(bytes);
- }
- }
- })
- .setColorOutput(req.getShowOutputColors())
- .withArguments(argsList);
-
- if (!Strings.isNullOrEmpty(req.getInput())) {
- InputStream inputStream = new ByteArrayInputStream(req.getInput().getBytes());
- build.setStandardInput(inputStream);
- }
-
- if (Boolean.TRUE.equals(req.getJavaDebug())) {
- if (req.getJavaDebugPort() == 0) {
- throw new GradleTaskRunnerException("Java debug port is not set");
- }
- build.setEnvironmentVariables(buildJavaEnvVarsWithJwdp(req.getJavaDebugPort()));
- }
-
- if (!Strings.isNullOrEmpty(req.getGradleConfig().getJvmArguments())) {
- build.setJvmArguments(req.getGradleConfig().getJvmArguments());
- }
-
- build.run();
- }
-
- private static Map buildJavaEnvVarsWithJwdp(int javaDebugPort) {
- HashMap envVars = new HashMap<>(System.getenv());
- envVars.put(
- JAVA_TOOL_OPTIONS_ENV,
- String.format(
- "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=localhost:%d",
- javaDebugPort));
- return envVars;
- }
-
- public void replyWithCancelled(BuildCancelledException e) {
- responseObserver.onNext(
- RunTaskReply.newBuilder()
- .setCancelled(
- Cancelled.newBuilder()
- .setMessage(e.getMessage())
- .setProjectDir(req.getProjectDir()))
- .build());
- }
-
- public void replyWithError(Exception e) {
- responseObserver.onError(ErrorMessageBuilder.build(e));
- }
-
- public void replyWithSuccess() {
- responseObserver.onNext(
- RunTaskReply.newBuilder()
- .setRunTaskResult(
- RunTaskResult.newBuilder()
- .setMessage("Successfully run task")
- .setTask(req.getTask()))
- .build());
- }
-
- private void replyWithProgress(ProgressEvent progressEvent) {
- responseObserver.onNext(
- RunTaskReply.newBuilder()
- .setProgress(Progress.newBuilder().setMessage(progressEvent.getDisplayName()))
- .build());
- }
-
- private void replyWithStandardOutput(byte[] bytes) {
- ByteString byteString = ByteString.copyFrom(bytes);
- responseObserver.onNext(
- RunTaskReply.newBuilder()
- .setOutput(
- Output.newBuilder()
- .setOutputType(Output.OutputType.STDOUT)
- .setOutputBytes(byteString))
- .build());
- }
-
- private void replyWithStandardError(byte[] bytes) {
- ByteString byteString = ByteString.copyFrom(bytes);
- responseObserver.onNext(
- RunTaskReply.newBuilder()
- .setOutput(
- Output.newBuilder()
- .setOutputType(Output.OutputType.STDERR)
- .setOutputBytes(byteString))
- .build());
- }
-}
diff --git a/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/StopDaemonHandler.java b/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/StopDaemonHandler.java
index 422803b2f..9350cf948 100644
--- a/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/StopDaemonHandler.java
+++ b/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/StopDaemonHandler.java
@@ -31,11 +31,11 @@ public void run() {
}
}
- public void replyWithError(Exception e) {
+ private void replyWithError(Exception e) {
responseObserver.onError(ErrorMessageBuilder.build(e));
}
- public void replyWithSuccess(String message) {
+ private void replyWithSuccess(String message) {
responseObserver.onNext(StopDaemonReply.newBuilder().setMessage(message).build());
responseObserver.onCompleted();
}
diff --git a/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/StopDaemonsHandler.java b/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/StopDaemonsHandler.java
index 91c7a6eb8..0dc8f896c 100644
--- a/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/StopDaemonsHandler.java
+++ b/gradle-server/src/main/java/com/github/badsyntax/gradle/handlers/StopDaemonsHandler.java
@@ -35,11 +35,11 @@ public void run() {
}
}
- public void replyWithError(Exception e) {
+ private void replyWithError(Exception e) {
responseObserver.onError(ErrorMessageBuilder.build(e));
}
- public void replyWithSuccess(String message) {
+ private void replyWithSuccess(String message) {
responseObserver.onNext(StopDaemonsReply.newBuilder().setMessage(message).build());
}
}
diff --git a/gradle-server/src/test/java/com/github/badsyntax/gradle/GradleServerTest.java b/gradle-server/src/test/java/com/github/badsyntax/gradle/GradleServerTest.java
index cbebafa50..296f765fd 100644
--- a/gradle-server/src/test/java/com/github/badsyntax/gradle/GradleServerTest.java
+++ b/gradle-server/src/test/java/com/github/badsyntax/gradle/GradleServerTest.java
@@ -291,29 +291,32 @@ public void getBuild_shouldStreamCorrectProgressEvents() throws IOException {
}
@Test
- public void runTask_shouldSetProjectDirectory() throws IOException {
- StreamObserver mockResponseObserver =
- (StreamObserver) mock(StreamObserver.class);
+ public void runBuild_shouldSetProjectDirectory() throws IOException {
+ StreamObserver mockResponseObserver =
+ (StreamObserver) mock(StreamObserver.class);
- RunTaskRequest req =
- RunTaskRequest.newBuilder()
+ ArrayList buildArgs = new ArrayList<>();
+ buildArgs.add("tasks");
+
+ RunBuildRequest req =
+ RunBuildRequest.newBuilder()
.setProjectDir(mockProjectDir.getAbsolutePath().toString())
- .setTask("tasks")
+ .addAllArgs(buildArgs)
.setGradleConfig(GradleConfig.newBuilder().setWrapperEnabled(true))
.build();
- stub.runTask(req, mockResponseObserver);
+ stub.runBuild(req, mockResponseObserver);
verify(mockResponseObserver, never()).onError(any());
verify(mockConnector).forProjectDirectory(mockProjectDir);
}
@Test
- public void runTask_shouldUseGradleUserHome() throws IOException {
- StreamObserver mockResponseObserver =
- (StreamObserver) mock(StreamObserver.class);
+ public void runBuild_shouldUseGradleUserHome() throws IOException {
+ StreamObserver mockResponseObserver =
+ (StreamObserver) mock(StreamObserver.class);
- RunTaskRequest req =
- RunTaskRequest.newBuilder()
+ RunBuildRequest req =
+ RunBuildRequest.newBuilder()
.setProjectDir(mockProjectDir.getAbsolutePath().toString())
.setGradleConfig(
GradleConfig.newBuilder()
@@ -321,91 +324,72 @@ public void runTask_shouldUseGradleUserHome() throws IOException {
.setWrapperEnabled(true))
.build();
- stub.runTask(req, mockResponseObserver);
+ stub.runBuild(req, mockResponseObserver);
verify(mockResponseObserver, never()).onError(any());
verify(mockConnector).useGradleUserHomeDir(mockGradleUserHome);
}
@Test
- public void runTask_shouldThrowIfWrapperNotEnabledAndNoVersionSpecified() throws IOException {
- StreamObserver mockResponseObserver =
- (StreamObserver) mock(StreamObserver.class);
+ public void runBuild_shouldThrowIfWrapperNotEnabledAndNoVersionSpecified() throws IOException {
+ StreamObserver mockResponseObserver =
+ (StreamObserver) mock(StreamObserver.class);
- RunTaskRequest req =
- RunTaskRequest.newBuilder()
+ RunBuildRequest req =
+ RunBuildRequest.newBuilder()
.setProjectDir(mockProjectDir.getAbsolutePath().toString())
.setGradleConfig(GradleConfig.newBuilder().setWrapperEnabled(false))
.build();
ArgumentCaptor onError = ArgumentCaptor.forClass(Throwable.class);
- stub.runTask(req, mockResponseObserver);
+ stub.runBuild(req, mockResponseObserver);
verify(mockResponseObserver).onError(onError.capture());
assertEquals("INTERNAL: Gradle version is required", onError.getValue().getMessage());
}
@Test
- public void runTask_shouldSetGradleVersionWrapperNotEnabledVersionSpecified() throws Exception {
- StreamObserver mockResponseObserver =
- (StreamObserver) mock(StreamObserver.class);
+ public void runBuild_shouldSetGradleVersionWrapperNotEnabledVersionSpecified() throws Exception {
+ StreamObserver mockResponseObserver =
+ (StreamObserver) mock(StreamObserver.class);
- RunTaskRequest req =
- RunTaskRequest.newBuilder()
+ RunBuildRequest req =
+ RunBuildRequest.newBuilder()
.setProjectDir(mockProjectDir.getAbsolutePath().toString())
.setGradleConfig(GradleConfig.newBuilder().setWrapperEnabled(false).setVersion("6.3"))
.build();
- stub.runTask(req, mockResponseObserver);
+ stub.runBuild(req, mockResponseObserver);
mockResponseObserver.onCompleted();
verify(mockResponseObserver, never()).onError(any());
verify(mockConnector).useGradleVersion("6.3");
}
@Test
- public void runTask_shouldUseJvmArgs() throws IOException {
- StreamObserver mockResponseObserver =
- (StreamObserver) mock(StreamObserver.class);
+ public void runBuild_shouldUseJvmArgs() throws IOException {
+ StreamObserver mockResponseObserver =
+ (StreamObserver) mock(StreamObserver.class);
String jvmArgs = "-Xmx64m -Xms64m";
- RunTaskRequest req =
- RunTaskRequest.newBuilder()
+ RunBuildRequest req =
+ RunBuildRequest.newBuilder()
.setProjectDir(mockProjectDir.getAbsolutePath().toString())
.setGradleConfig(
GradleConfig.newBuilder().setJvmArguments(jvmArgs).setWrapperEnabled(true))
.build();
- stub.runTask(req, mockResponseObserver);
+ stub.runBuild(req, mockResponseObserver);
verify(mockResponseObserver, never()).onError(any());
verify(mockBuildLauncher).setJvmArguments(jvmArgs);
}
@Test
- public void runTask_shouldThrowIfDebugAndNotPortSpecified() throws IOException {
- StreamObserver mockResponseObserver =
- (StreamObserver) mock(StreamObserver.class);
-
- RunTaskRequest req =
- RunTaskRequest.newBuilder()
- .setProjectDir(mockProjectDir.getAbsolutePath().toString())
- .setJavaDebug(true)
- .setGradleConfig(GradleConfig.newBuilder().setWrapperEnabled(true))
- .build();
-
- ArgumentCaptor onError = ArgumentCaptor.forClass(Throwable.class);
- stub.runTask(req, mockResponseObserver);
- verify(mockResponseObserver).onError(onError.capture());
- assertEquals("INTERNAL: Java debug port is not set", onError.getValue().getMessage());
- }
-
- @Test
- public void runTask_shouldSetJwdpEnvironmentVarIfDebug() throws IOException {
- StreamObserver mockResponseObserver =
- (StreamObserver) mock(StreamObserver.class);
+ public void runBuild_shouldSetJwdpEnvironmentVarIfDebug() throws IOException {
+ StreamObserver mockResponseObserver =
+ (StreamObserver) mock(StreamObserver.class);
- RunTaskRequest req =
- RunTaskRequest.newBuilder()
+ RunBuildRequest req =
+ RunBuildRequest.newBuilder()
.setProjectDir(mockProjectDir.getAbsolutePath().toString())
- .setJavaDebug(true)
.setJavaDebugPort(1111)
.setGradleConfig(GradleConfig.newBuilder().setWrapperEnabled(true))
.build();
@@ -413,7 +397,7 @@ public void runTask_shouldSetJwdpEnvironmentVarIfDebug() throws IOException {
ArgumentCaptor> setEnvironmentVariables =
ArgumentCaptor.forClass(HashMap.class);
- stub.runTask(req, mockResponseObserver);
+ stub.runBuild(req, mockResponseObserver);
verify(mockResponseObserver, never()).onError(any());
verify(mockBuildLauncher).setEnvironmentVariables(setEnvironmentVariables.capture());
assertEquals(
@@ -422,12 +406,12 @@ public void runTask_shouldSetJwdpEnvironmentVarIfDebug() throws IOException {
}
@Test
- public void runTask_shouldSetStandardInput() throws IOException {
- StreamObserver mockResponseObserver =
- (StreamObserver) mock(StreamObserver.class);
+ public void runBuild_shouldSetStandardInput() throws IOException {
+ StreamObserver mockResponseObserver =
+ (StreamObserver) mock(StreamObserver.class);
- RunTaskRequest req =
- RunTaskRequest.newBuilder()
+ RunBuildRequest req =
+ RunBuildRequest.newBuilder()
.setProjectDir(mockProjectDir.getAbsolutePath().toString())
.setGradleConfig(GradleConfig.newBuilder().setWrapperEnabled(true))
.setInput("An input string")
@@ -435,7 +419,7 @@ public void runTask_shouldSetStandardInput() throws IOException {
ArgumentCaptor inputStream = ArgumentCaptor.forClass(InputStream.class);
- stub.runTask(req, mockResponseObserver);
+ stub.runBuild(req, mockResponseObserver);
verify(mockResponseObserver, never()).onError(any());
verify(mockBuildLauncher).setStandardInput(inputStream.capture());
InputStreamReader isReader = new InputStreamReader(inputStream.getValue());
@@ -449,40 +433,40 @@ public void runTask_shouldSetStandardInput() throws IOException {
}
@Test
- public void runTask_shouldSetColorOutput() throws IOException {
- StreamObserver mockResponseObserver =
- (StreamObserver) mock(StreamObserver.class);
+ public void runBuild_shouldSetColorOutput() throws IOException {
+ StreamObserver mockResponseObserver =
+ (StreamObserver) mock(StreamObserver.class);
- RunTaskRequest req1 =
- RunTaskRequest.newBuilder()
+ RunBuildRequest req1 =
+ RunBuildRequest.newBuilder()
.setProjectDir(mockProjectDir.getAbsolutePath().toString())
.setGradleConfig(GradleConfig.newBuilder().setWrapperEnabled(true))
.setShowOutputColors(false)
.build();
- stub.runTask(req1, mockResponseObserver);
+ stub.runBuild(req1, mockResponseObserver);
verify(mockResponseObserver, never()).onError(any());
verify(mockBuildLauncher).setColorOutput(false);
- RunTaskRequest req2 =
- RunTaskRequest.newBuilder()
+ RunBuildRequest req2 =
+ RunBuildRequest.newBuilder()
.setProjectDir(mockProjectDir.getAbsolutePath().toString())
.setGradleConfig(GradleConfig.newBuilder().setWrapperEnabled(true))
.setShowOutputColors(true)
.build();
- stub.runTask(req2, mockResponseObserver);
+ stub.runBuild(req2, mockResponseObserver);
verify(mockResponseObserver, never()).onError(any());
verify(mockBuildLauncher).setColorOutput(true);
}
@Test
- public void runTask_shouldStreamCorrectProgressEvents() throws IOException {
- StreamObserver mockResponseObserver =
- (StreamObserver) mock(StreamObserver.class);
+ public void runBuild_shouldStreamCorrectProgressEvents() throws IOException {
+ StreamObserver mockResponseObserver =
+ (StreamObserver) mock(StreamObserver.class);
- RunTaskRequest req =
- RunTaskRequest.newBuilder()
+ RunBuildRequest req =
+ RunBuildRequest.newBuilder()
.setProjectDir(mockProjectDir.getAbsolutePath().toString())
.setGradleConfig(GradleConfig.newBuilder().setWrapperEnabled(true))
.setShowOutputColors(true)
@@ -490,7 +474,7 @@ public void runTask_shouldStreamCorrectProgressEvents() throws IOException {
ArgumentCaptor> onAddProgressListener = ArgumentCaptor.forClass(Set.class);
- stub.runTask(req, mockResponseObserver);
+ stub.runBuild(req, mockResponseObserver);
verify(mockResponseObserver, never()).onError(any());
verify(mockBuildLauncher)
.addProgressListener(
diff --git a/images/run-build.png b/images/run-build.png
new file mode 100644
index 000000000..a50492ac8
Binary files /dev/null and b/images/run-build.png differ
diff --git a/npm-package/index.ts b/npm-package/index.ts
index c51d57697..ed3d0c65c 100644
--- a/npm-package/index.ts
+++ b/npm-package/index.ts
@@ -1,15 +1,15 @@
import {
Output,
- RunTaskRequest,
- CancelRunTaskRequest,
+ RunBuildRequest,
+ CancelBuildRequest,
} from './lib/proto/gradle_pb';
import type { Api, RunTaskOpts, CancelTaskOpts } from './lib/api/Api';
export {
Output,
- RunTaskRequest,
+ RunBuildRequest,
RunTaskOpts,
CancelTaskOpts,
- CancelRunTaskRequest,
+ CancelBuildRequest,
Api as ExtensionApi,
};
diff --git a/proto/gradle.proto b/proto/gradle.proto
index 2170995da..26ea1d578 100644
--- a/proto/gradle.proto
+++ b/proto/gradle.proto
@@ -8,25 +8,18 @@ package gradle;
service Gradle {
rpc GetBuild(GetBuildRequest) returns (stream GetBuildReply) {}
+ rpc RunBuild(RunBuildRequest) returns (stream RunBuildReply) {}
+ rpc CancelBuild(CancelBuildRequest) returns (CancelBuildReply) {}
rpc GetDaemonsStatus(GetDaemonsStatusRequest) returns (GetDaemonsStatusReply) {}
rpc StopDaemons(StopDaemonsRequest) returns (StopDaemonsReply) {}
rpc StopDaemon(StopDaemonRequest) returns (StopDaemonReply) {}
- rpc RunTask(RunTaskRequest) returns (stream RunTaskReply) {}
- rpc CancelGetBuilds(CancelGetBuildsRequest) returns (CancelGetBuildsReply) {}
- rpc CancelRunTask(CancelRunTaskRequest) returns (CancelRunTaskReply) {}
- rpc CancelRunTasks(CancelRunTasksRequest) returns (CancelRunTasksReply) {}
}
-message CancelGetBuildsRequest {}
-
-message CancelGetBuildsReply { string message = 1; }
-
-message GetTasksRequest { string project_dir = 1; }
-
message GetBuildRequest {
string project_dir = 1;
- GradleConfig gradle_config = 2;
- bool show_output_colors = 8;
+ string cancellation_key = 2;
+ GradleConfig gradle_config = 3;
+ bool show_output_colors = 4;
}
message GetBuildReply {
@@ -39,50 +32,42 @@ message GetBuildReply {
}
}
-message GetTasksResult {
- string message = 1;
- repeated GradleTask tasks = 2;
-}
-
message GetBuildResult {
string message = 1;
GradleBuild build = 2;
}
-message RunTaskRequest {
- enum OutputStream {
- BYTES = 0;
- STRING = 1;
- }
+message RunBuildRequest {
string project_dir = 1;
- string task = 2;
+ string cancellation_key = 2;
repeated string args = 3;
- bool java_debug = 4;
- int32 java_debug_port = 5;
- GradleConfig gradle_config = 6;
- string input = 7;
- bool show_output_colors = 8;
+ int32 java_debug_port = 4;
+ GradleConfig gradle_config = 5;
+ string input = 6;
+ bool show_output_colors = 7;
}
-message RunTaskResult {
+message RunBuildResult {
string message = 1;
- string task = 2;
}
-message CancelRunTaskRequest {
- string project_dir = 1;
- string task = 2;
+message RunBuildReply {
+ oneof kind {
+ RunBuildResult run_build_result = 1;
+ Progress progress = 2;
+ Output output = 3;
+ Cancelled cancelled = 4;
+ }
}
-message CancelRunTaskReply {
- string message = 1;
- string task = 2;
- bool task_running = 3;
+message CancelBuildRequest {
+ string cancellation_key = 1;
}
-message CancelRunTasksRequest {}
-
-message CancelRunTasksReply { string message = 1; }
+message CancelBuildReply {
+ string message = 1;
+ bool build_running = 2;
+}
message GetDaemonsStatusRequest {
string project_dir = 1;
@@ -121,15 +106,6 @@ message DaemonInfo {
string info = 3;
}
-message RunTaskReply {
- oneof kind {
- RunTaskResult run_task_result = 1;
- Progress progress = 2;
- Output output = 3;
- Cancelled cancelled = 4;
- }
-}
-
message GradleConfig {
string user_home = 2;
string jvm_arguments = 3;
@@ -158,7 +134,6 @@ message GradleTask {
message Cancelled {
string message = 1;
string project_dir = 2;
- string task = 3;
}
message Progress { string message = 1; }