Skip to content

Commit 9a10103

Browse files
Add command to generate launch configurations (#1577)
1 parent 6126fe7 commit 9a10103

File tree

9 files changed

+478
-92
lines changed

9 files changed

+478
-92
lines changed

package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@
100100
}
101101
],
102102
"commands": [
103+
{
104+
"command": "swift.generateLaunchConfigurations",
105+
"title": "Generate Launch Configurations",
106+
"category": "Swift"
107+
},
103108
{
104109
"command": "swift.previewDocumentation",
105110
"title": "Preview Documentation",
@@ -886,6 +891,10 @@
886891
}
887892
],
888893
"commandPalette": [
894+
{
895+
"command": "swift.generateLaunchConfigurations",
896+
"when": "swift.hasExecutableProduct"
897+
},
889898
{
890899
"command": "swift.previewDocumentation",
891900
"when": "swift.supportsDocumentationLivePreview"

src/WorkspaceContext.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export class WorkspaceContext implements vscode.Disposable {
101101
.then(async selected => {
102102
if (selected === "Update") {
103103
this.folders.forEach(ctx =>
104-
makeDebugConfigurations(ctx, undefined, true)
104+
makeDebugConfigurations(ctx, { yes: true })
105105
);
106106
}
107107
});
@@ -120,7 +120,7 @@ export class WorkspaceContext implements vscode.Disposable {
120120
.then(selected => {
121121
if (selected === "Update") {
122122
this.folders.forEach(ctx =>
123-
makeDebugConfigurations(ctx, undefined, true)
123+
makeDebugConfigurations(ctx, { yes: true })
124124
);
125125
}
126126
});
@@ -228,15 +228,18 @@ export class WorkspaceContext implements vscode.Disposable {
228228
updateContextKeys(folderContext: FolderContext | null) {
229229
if (!folderContext) {
230230
contextKeys.hasPackage = false;
231+
contextKeys.hasExecutableProduct = false;
231232
contextKeys.packageHasDependencies = false;
232233
return;
233234
}
234235

235236
Promise.all([
236237
folderContext.swiftPackage.foundPackage,
238+
folderContext.swiftPackage.executableProducts,
237239
folderContext.swiftPackage.dependencies,
238-
]).then(([foundPackage, dependencies]) => {
240+
]).then(([foundPackage, executableProducts, dependencies]) => {
239241
contextKeys.hasPackage = foundPackage;
242+
contextKeys.hasExecutableProduct = executableProducts.length > 0;
240243
contextKeys.packageHasDependencies = dependencies.length > 0;
241244
});
242245
}

src/commands.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { TestKind } from "./TestExplorer/TestKind";
4747
import { pickProcess } from "./commands/pickProcess";
4848
import { openDocumentation } from "./commands/openDocumentation";
4949
import restartLSPServer from "./commands/restartLSPServer";
50+
import { generateLaunchConfigurations } from "./commands/generateLaunchConfigurations";
5051

5152
/**
5253
* References:
@@ -105,6 +106,9 @@ export enum Commands {
105106
*/
106107
export function register(ctx: WorkspaceContext): vscode.Disposable[] {
107108
return [
109+
vscode.commands.registerCommand("swift.generateLaunchConfigurations", () =>
110+
generateLaunchConfigurations(ctx)
111+
),
108112
vscode.commands.registerCommand("swift.newFile", uri => newSwiftFile(uri)),
109113
vscode.commands.registerCommand(Commands.RESOLVE_DEPENDENCIES, () =>
110114
resolveDependencies(ctx)
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the VS Code Swift open source project
4+
//
5+
// Copyright (c) 2025 the VS Code Swift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of VS Code Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import { makeDebugConfigurations } from "../debugger/launch";
16+
import { FolderContext } from "../FolderContext";
17+
import { WorkspaceContext } from "../WorkspaceContext";
18+
import * as vscode from "vscode";
19+
20+
export async function generateLaunchConfigurations(ctx: WorkspaceContext): Promise<boolean> {
21+
if (ctx.folders.length === 0) {
22+
return false;
23+
}
24+
25+
if (ctx.folders.length === 1) {
26+
return await makeDebugConfigurations(ctx.folders[0], { force: true, yes: true });
27+
}
28+
29+
const quickPickItems: SelectFolderQuickPick[] = ctx.folders.map(folder => ({
30+
type: "folder",
31+
folder,
32+
label: folder.name,
33+
detail: folder.workspaceFolder.uri.fsPath,
34+
}));
35+
quickPickItems.push({ type: "all", label: "Generate For All Folders" });
36+
const selection = await vscode.window.showQuickPick(quickPickItems, {
37+
matchOnDetail: true,
38+
placeHolder: "Select a folder to generate launch configurations for",
39+
});
40+
41+
if (!selection) {
42+
return false;
43+
}
44+
45+
const foldersToUpdate: FolderContext[] = [];
46+
if (selection.type === "all") {
47+
foldersToUpdate.push(...ctx.folders);
48+
} else {
49+
foldersToUpdate.push(selection.folder);
50+
}
51+
52+
return (
53+
await Promise.all(
54+
foldersToUpdate.map(folder =>
55+
makeDebugConfigurations(folder, { force: true, yes: true })
56+
)
57+
)
58+
).reduceRight((prev, curr) => prev || curr);
59+
}
60+
61+
type SelectFolderQuickPick = AllQuickPickItem | FolderQuickPickItem;
62+
63+
interface AllQuickPickItem extends vscode.QuickPickItem {
64+
type: "all";
65+
}
66+
67+
interface FolderQuickPickItem extends vscode.QuickPickItem {
68+
type: "folder";
69+
folder: FolderContext;
70+
}

src/contextKeys.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ interface ContextKeys {
3434
*/
3535
hasPackage: boolean;
3636

37+
/**
38+
* Whether the workspace folder contains a Swift package with at least one executable product.
39+
*/
40+
hasExecutableProduct: boolean;
41+
3742
/**
3843
* Whether the Swift package has any dependencies to display in the Package Dependencies view.
3944
*/
@@ -94,6 +99,7 @@ interface ContextKeys {
9499
function createContextKeys(): ContextKeys {
95100
let isActivated: boolean = false;
96101
let hasPackage: boolean = false;
102+
let hasExecutableProduct: boolean = false;
97103
let flatDependenciesList: boolean = false;
98104
let packageHasDependencies: boolean = false;
99105
let packageHasPlugins: boolean = false;
@@ -134,6 +140,15 @@ function createContextKeys(): ContextKeys {
134140
vscode.commands.executeCommand("setContext", "swift.hasPackage", value);
135141
},
136142

143+
get hasExecutableProduct() {
144+
return hasExecutableProduct;
145+
},
146+
147+
set hasExecutableProduct(value: boolean) {
148+
hasExecutableProduct = value;
149+
vscode.commands.executeCommand("setContext", "swift.hasExecutableTarget", value);
150+
},
151+
137152
get packageHasDependencies() {
138153
return packageHasDependencies;
139154
},

src/debugger/debugAdapterFactory.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import { registerLoggingDebugAdapterTracker } from "./logTracker";
2020
import { SwiftToolchain } from "../toolchain/toolchain";
2121
import { SwiftOutputChannel } from "../ui/SwiftOutputChannel";
2222
import { fileExists } from "../utilities/filesystem";
23-
import { getLLDBLibPath } from "./lldb";
24-
import { getErrorDescription } from "../utilities/utilities";
23+
import { CI_DISABLE_ASLR, getLLDBLibPath } from "./lldb";
24+
import { getErrorDescription, swiftRuntimeEnv } from "../utilities/utilities";
2525
import configuration from "../configuration";
2626

2727
/**
@@ -137,6 +137,13 @@ export class LLDBDebugConfigurationProvider implements vscode.DebugConfiguration
137137
launchConfig.pid = pid;
138138
}
139139

140+
// Merge in the Swift runtime environment variables
141+
const runtimeEnv = swiftRuntimeEnv(true);
142+
if (runtimeEnv) {
143+
const existingEnv = launchConfig.env ?? {};
144+
launchConfig.env = { ...runtimeEnv, existingEnv };
145+
}
146+
140147
// Delegate to the appropriate debug adapter extension
141148
launchConfig.type = DebugAdapter.getLaunchConfigType(toolchain.swiftVersion);
142149
if (launchConfig.type === LaunchConfigType.CODE_LLDB) {
@@ -164,7 +171,10 @@ export class LLDBDebugConfigurationProvider implements vscode.DebugConfiguration
164171
launchConfig.debugAdapterExecutable = lldbDapPath;
165172
}
166173

167-
return launchConfig;
174+
return {
175+
...launchConfig,
176+
...CI_DISABLE_ASLR,
177+
};
168178
}
169179

170180
private async promptToInstallCodeLLDB(): Promise<boolean> {

0 commit comments

Comments
 (0)