Skip to content

Commit b4a8b0c

Browse files
authored
refactor: Move Bazel wrapper commands into separate file (#406)
This in preparation of adding a new "Measure Coverage" command, but I don't want to make the main `extension.ts` file even longer.
1 parent dba3102 commit b4a8b0c

File tree

2 files changed

+329
-302
lines changed

2 files changed

+329
-302
lines changed
Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
// Copyright 2024 The Bazel Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import * as vscode from "vscode";
16+
17+
import { IBazelCommandAdapter } from "../bazel";
18+
import {
19+
BazelWorkspaceInfo,
20+
createBazelTask,
21+
queryQuickPickPackage,
22+
queryQuickPickTargets,
23+
} from "../bazel";
24+
import { getDefaultBazelExecutablePath } from "./configuration";
25+
26+
/**
27+
* Builds a Bazel target and streams output to the terminal.
28+
*
29+
* @param adapter An object that implements {@link IBazelCommandAdapter} from
30+
* which the command's arguments will be determined.
31+
*/
32+
async function bazelBuildTarget(adapter: IBazelCommandAdapter | undefined) {
33+
if (adapter === undefined) {
34+
// If the command adapter was unspecified, it means this command is being
35+
// invoked via the command palatte. Provide quickpick build targets for
36+
// the user to choose from.
37+
const quickPick = await vscode.window.showQuickPick(
38+
queryQuickPickTargets({ query: "kind('.* rule', ...)" }),
39+
{
40+
canPickMany: false,
41+
},
42+
);
43+
// If the result was undefined, the user cancelled the quick pick, so don't
44+
// try again.
45+
if (quickPick) {
46+
await bazelBuildTarget(quickPick);
47+
}
48+
return;
49+
}
50+
const commandOptions = adapter.getBazelCommandOptions();
51+
const task = createBazelTask("build", commandOptions);
52+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
53+
vscode.tasks.executeTask(task);
54+
}
55+
56+
/**
57+
* Builds a Bazel target and attaches the Starlark debugger.
58+
*
59+
* @param adapter An object that implements {@link IBazelCommandAdapter} from
60+
* which the command's arguments will be determined.
61+
*/
62+
async function bazelBuildTargetWithDebugging(
63+
adapter: IBazelCommandAdapter | undefined,
64+
) {
65+
if (adapter === undefined) {
66+
// If the command adapter was unspecified, it means this command is being
67+
// invoked via the command palatte. Provide quickpick build targets for
68+
// the user to choose from.
69+
const quickPick = await vscode.window.showQuickPick(
70+
queryQuickPickTargets({ query: "kind('.* rule', ...)" }),
71+
{
72+
canPickMany: false,
73+
},
74+
);
75+
// If the result was undefined, the user cancelled the quick pick, so don't
76+
// try again.
77+
if (quickPick) {
78+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
79+
bazelBuildTargetWithDebugging(quickPick);
80+
}
81+
return;
82+
}
83+
const bazelConfigCmdLine =
84+
vscode.workspace.getConfiguration("bazel.commandLine");
85+
const startupOptions = bazelConfigCmdLine.get<string[]>("startupOptions");
86+
const commandArgs = bazelConfigCmdLine.get<string[]>("commandArgs");
87+
88+
const commandOptions = adapter.getBazelCommandOptions();
89+
90+
const fullArgs = commandArgs
91+
.concat(commandOptions.targets)
92+
.concat(commandOptions.options);
93+
94+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
95+
vscode.debug.startDebugging(undefined, {
96+
args: fullArgs,
97+
bazelCommand: "build",
98+
bazelExecutablePath: getDefaultBazelExecutablePath(),
99+
bazelStartupOptions: startupOptions,
100+
cwd: commandOptions.workspaceInfo.bazelWorkspacePath,
101+
name: "On-demand Bazel Build Debug",
102+
request: "launch",
103+
type: "bazel-launch-build",
104+
});
105+
}
106+
107+
/**
108+
* Builds a Bazel package and streams output to the terminal.
109+
*
110+
* @param adapter An object that implements {@link IBazelCommandAdapter} from
111+
* which the command's arguments will be determined.
112+
*/
113+
async function bazelbuildAll(adapter: IBazelCommandAdapter | undefined) {
114+
await buildPackage(":all", adapter);
115+
}
116+
117+
/**
118+
* Builds a Bazel package recursively and streams output to the terminal.
119+
*
120+
* @param adapter An object that implements {@link IBazelCommandAdapter} from
121+
* which the command's arguments will be determined.
122+
*/
123+
async function bazelbuildAllRecursive(
124+
adapter: IBazelCommandAdapter | undefined,
125+
) {
126+
await buildPackage("/...", adapter);
127+
}
128+
129+
async function buildPackage(
130+
suffix: string,
131+
adapter: IBazelCommandAdapter | undefined,
132+
) {
133+
if (adapter === undefined) {
134+
// If the command adapter was unspecified, it means this command is being
135+
// invoked via the command palatte. Provide quickpick build targets for
136+
// the user to choose from.
137+
const quickPick = await vscode.window.showQuickPick(
138+
queryQuickPickPackage({}),
139+
{
140+
canPickMany: false,
141+
},
142+
);
143+
// If the result was undefined, the user cancelled the quick pick, so don't
144+
// try again.
145+
if (quickPick) {
146+
await buildPackage(suffix, quickPick);
147+
}
148+
return;
149+
}
150+
const commandOptions = adapter.getBazelCommandOptions();
151+
const allCommandOptions = {
152+
options: commandOptions.options,
153+
targets: commandOptions.targets.map((s) => s + suffix),
154+
workspaceInfo: commandOptions.workspaceInfo,
155+
};
156+
const task = createBazelTask("build", allCommandOptions);
157+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
158+
vscode.tasks.executeTask(task);
159+
}
160+
161+
/**
162+
* Runs a Bazel target and streams output to the terminal.
163+
*
164+
* @param adapter An object that implements {@link IBazelCommandAdapter} from
165+
* which the command's arguments will be determined.
166+
*/
167+
async function bazelRunTarget(adapter: IBazelCommandAdapter | undefined) {
168+
if (adapter === undefined) {
169+
// If the command adapter was unspecified, it means this command is being
170+
// invoked via the command palatte. Provide quickpick test targets for
171+
// the user to choose from.
172+
const quickPick = await vscode.window.showQuickPick(
173+
queryQuickPickTargets({ query: "kind('.* rule', ...)" }),
174+
{
175+
canPickMany: false,
176+
},
177+
);
178+
// If the result was undefined, the user cancelled the quick pick, so don't
179+
// try again.
180+
if (quickPick) {
181+
await bazelRunTarget(quickPick);
182+
}
183+
return;
184+
}
185+
const commandOptions = adapter.getBazelCommandOptions();
186+
const task = createBazelTask("run", commandOptions);
187+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
188+
vscode.tasks.executeTask(task);
189+
}
190+
191+
/**
192+
* Tests a Bazel target and streams output to the terminal.
193+
*
194+
* @param adapter An object that implements {@link IBazelCommandAdapter} from
195+
* which the command's arguments will be determined.
196+
*/
197+
async function bazelTestTarget(adapter: IBazelCommandAdapter | undefined) {
198+
if (adapter === undefined) {
199+
// If the command adapter was unspecified, it means this command is being
200+
// invoked via the command palatte. Provide quickpick test targets for
201+
// the user to choose from.
202+
const quickPick = await vscode.window.showQuickPick(
203+
queryQuickPickTargets({ query: "kind('.*_test rule', ...)" }),
204+
{
205+
canPickMany: false,
206+
},
207+
);
208+
// If the result was undefined, the user cancelled the quick pick, so don't
209+
// try again.
210+
if (quickPick) {
211+
await bazelTestTarget(quickPick);
212+
}
213+
return;
214+
}
215+
const commandOptions = adapter.getBazelCommandOptions();
216+
const task = createBazelTask("test", commandOptions);
217+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
218+
vscode.tasks.executeTask(task);
219+
}
220+
221+
/**
222+
* Tests a Bazel package and streams output to the terminal.
223+
*
224+
* @param adapter An object that implements {@link IBazelCommandAdapter} from
225+
* which the command's arguments will be determined.
226+
*/
227+
async function bazelTestAll(adapter: IBazelCommandAdapter | undefined) {
228+
await testPackage(":all", adapter);
229+
}
230+
231+
/**
232+
* Tests a Bazel package recursively and streams output to the terminal.
233+
*
234+
* @param adapter An object that implements {@link IBazelCommandAdapter} from
235+
* which the command's arguments will be determined.
236+
*/
237+
async function bazelTestAllRecursive(
238+
adapter: IBazelCommandAdapter | undefined,
239+
) {
240+
await testPackage("/...", adapter);
241+
}
242+
243+
async function testPackage(
244+
suffix: string,
245+
adapter: IBazelCommandAdapter | undefined,
246+
) {
247+
if (adapter === undefined) {
248+
// If the command adapter was unspecified, it means this command is being
249+
// invoked via the command palatte. Provide quickpick build targets for
250+
// the user to choose from.
251+
const quickPick = await vscode.window.showQuickPick(
252+
queryQuickPickPackage({}),
253+
{
254+
canPickMany: false,
255+
},
256+
);
257+
// If the result was undefined, the user cancelled the quick pick, so don't
258+
// try again.
259+
if (quickPick) {
260+
await testPackage(suffix, quickPick);
261+
}
262+
return;
263+
}
264+
const commandOptions = adapter.getBazelCommandOptions();
265+
const allCommandOptions = {
266+
options: commandOptions.options,
267+
targets: commandOptions.targets.map((s) => s + suffix),
268+
workspaceInfo: commandOptions.workspaceInfo,
269+
};
270+
const task = createBazelTask("test", allCommandOptions);
271+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
272+
vscode.tasks.executeTask(task);
273+
}
274+
275+
/**
276+
* Cleans a Bazel workspace.
277+
*
278+
* If there is only a single workspace open, it will be cleaned immediately. If
279+
* there are multiple workspace folders open, a quick-pick window will be opened
280+
* asking the user to choose one.
281+
*/
282+
async function bazelClean() {
283+
const workspaceInfo = await BazelWorkspaceInfo.fromWorkspaceFolders();
284+
if (!workspaceInfo) {
285+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
286+
vscode.window.showInformationMessage(
287+
"Please open a Bazel workspace folder to use this command.",
288+
);
289+
290+
return;
291+
}
292+
const task = createBazelTask("clean", {
293+
options: [],
294+
targets: [],
295+
workspaceInfo,
296+
});
297+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
298+
vscode.tasks.executeTask(task);
299+
}
300+
301+
/**
302+
* Activate all user-facing commands which simply wrap Bazel commands
303+
* such as `build`, `clean`, etc.
304+
*/
305+
export function activateWrapperCommands(): vscode.Disposable[] {
306+
return [
307+
vscode.commands.registerCommand("bazel.buildTarget", bazelBuildTarget),
308+
vscode.commands.registerCommand(
309+
"bazel.buildTargetWithDebugging",
310+
bazelBuildTargetWithDebugging,
311+
),
312+
vscode.commands.registerCommand("bazel.buildAll", bazelbuildAll),
313+
vscode.commands.registerCommand(
314+
"bazel.buildAllRecursive",
315+
bazelbuildAllRecursive,
316+
),
317+
vscode.commands.registerCommand("bazel.runTarget", bazelRunTarget),
318+
vscode.commands.registerCommand("bazel.testTarget", bazelTestTarget),
319+
vscode.commands.registerCommand("bazel.testAll", bazelTestAll),
320+
vscode.commands.registerCommand(
321+
"bazel.testAllRecursive",
322+
bazelTestAllRecursive,
323+
),
324+
vscode.commands.registerCommand("bazel.clean", bazelClean),
325+
];
326+
}

0 commit comments

Comments
 (0)