diff --git a/package.json b/package.json index 8194847ac..ce64887dd 100644 --- a/package.json +++ b/package.json @@ -271,7 +271,15 @@ "items": { "type": "string" }, - "markdownDescription": "Additional arguments to pass to `swift` commands such as `swift build`, `swift package`, `swift test`, etc... Keys and values should be provided as individual entries in the list. If you have created a copy of the build task in `tasks.json` then these build arguments will not be propagated to that task." + "markdownDescription": "Additional arguments to pass to `swift build` and `swift test`. Keys and values should be provided as individual entries in the list. If you have created a copy of the build task in `tasks.json` then these build arguments will not be propagated to that task." + }, + "swift.packageArguments": { + "type": "array", + "default": [], + "items": { + "type": "string" + }, + "markdownDescription": "Additional arguments to pass to swift commands that do package resolution, such as `swift package resolve`, `swift package update`, `swift build` and `swift test`. Keys and values should be provided as individual entries in the list." }, "swift.additionalTestArguments": { "type": "array", diff --git a/src/configuration.ts b/src/configuration.ts index 77c35d3bc..d282378d1 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -239,6 +239,10 @@ const configuration = { get buildArguments(): string[] { return vscode.workspace.getConfiguration("swift").get("buildArguments", []); }, + /** swift package arguments */ + get packageArguments(): string[] { + return vscode.workspace.getConfiguration("swift").get("packageArguments", []); + }, /** thread/address sanitizer */ get sanitizer(): string { return vscode.workspace.getConfiguration("swift").get("sanitizer", "off"); diff --git a/src/tasks/SwiftTaskProvider.ts b/src/tasks/SwiftTaskProvider.ts index 1fb2b4b35..fbf5ba946 100644 --- a/src/tasks/SwiftTaskProvider.ts +++ b/src/tasks/SwiftTaskProvider.ts @@ -103,15 +103,20 @@ function getBuildRevealOption(): vscode.TaskRevealKind { const buildAllTaskCache = (() => { const cache = new Map(); - const key = (name: string, folderContext: FolderContext) => { - return `${name}:${folderContext.folder}:${buildOptions(folderContext.workspaceContext.toolchain).join(",")}`; + const key = (name: string, folderContext: FolderContext, task: SwiftTask) => { + return `${name}:${folderContext.folder}:${buildOptions(folderContext.workspaceContext.toolchain).join(",")}:${task.definition.args.join(",")}`; }; + return { - get(name: string, folderContext: FolderContext): SwiftTask | undefined { - return cache.get(key(name, folderContext)); + get(name: string, folderContext: FolderContext, task: SwiftTask): SwiftTask { + const cached = cache.get(key(name, folderContext, task)); + if (!cached) { + this.set(name, folderContext, task); + } + return cached ?? task; }, set(name: string, folderContext: FolderContext, task: SwiftTask) { - cache.set(key(name, folderContext), task); + cache.set(key(name, folderContext, task), task); }, }; })(); @@ -135,15 +140,6 @@ export function createBuildAllTask( ): SwiftTask { const args = BuildConfigurationFactory.buildAll(folderContext, false, release).args; const buildTaskName = buildAllTaskName(folderContext, release); - - // Create one Build All task per folder context, since this can be called multiple - // times and we want the same instance each time. Otherwise, VS Code may try and execute - // one instance while our extension code tries to listen to events on an instance created earlier/later. - const existingTask = buildAllTaskCache.get(buildTaskName, folderContext); - if (existingTask) { - return existingTask; - } - const task = createSwiftTask( args, buildTaskName, @@ -158,8 +154,11 @@ export function createBuildAllTask( }, folderContext.workspaceContext.toolchain ); - buildAllTaskCache.set(buildTaskName, folderContext, task); - return task; + + // Ensures there is one Build All task per folder context, since this can be called multiple + // times and we want the same instance each time. Otherwise, VS Code may try and execute + // one instance while our extension code tries to listen to events on an instance created earlier/later. + return buildAllTaskCache.get(buildTaskName, folderContext, task); } /** @@ -224,54 +223,40 @@ function createBuildTasks(product: Product, folderContext: FolderContext): vscod } const buildDebugName = `Build Debug ${product.name}${buildTaskNameSuffix}`; - let buildDebug = buildAllTaskCache.get(buildDebugName, folderContext); - if (!buildDebug) { - buildDebug = createSwiftTask( - ["build", "--product", product.name, ...buildOptions(toolchain)], - buildDebugName, - { - group: vscode.TaskGroup.Build, - cwd: folderContext.folder, - scope: folderContext.workspaceFolder, - presentationOptions: { - reveal: getBuildRevealOption(), - }, - disableTaskQueue: true, - dontTriggerTestDiscovery: true, + const buildDebugTask = createSwiftTask( + ["build", "--product", product.name, ...buildOptions(toolchain)], + buildDebugName, + { + group: vscode.TaskGroup.Build, + cwd: folderContext.folder, + scope: folderContext.workspaceFolder, + presentationOptions: { + reveal: getBuildRevealOption(), }, - folderContext.workspaceContext.toolchain - ); - buildAllTaskCache.set(buildDebugName, folderContext, buildDebug); - } + disableTaskQueue: true, + dontTriggerTestDiscovery: true, + }, + folderContext.workspaceContext.toolchain + ); + const buildDebug = buildAllTaskCache.get(buildDebugName, folderContext, buildDebugTask); const buildReleaseName = `Build Release ${product.name}${buildTaskNameSuffix}`; - let buildRelease = buildAllTaskCache.get(buildReleaseName, folderContext); - if (!buildRelease) { - buildRelease = createSwiftTask( - [ - "build", - "-c", - "release", - "--product", - product.name, - ...buildOptions(toolchain, false), - ], - `Build Release ${product.name}${buildTaskNameSuffix}`, - { - group: vscode.TaskGroup.Build, - cwd: folderContext.folder, - scope: folderContext.workspaceFolder, - presentationOptions: { - reveal: getBuildRevealOption(), - }, - disableTaskQueue: true, - dontTriggerTestDiscovery: true, + const buildReleaseTask = createSwiftTask( + ["build", "-c", "release", "--product", product.name, ...buildOptions(toolchain, false)], + `Build Release ${product.name}${buildTaskNameSuffix}`, + { + group: vscode.TaskGroup.Build, + cwd: folderContext.folder, + scope: folderContext.workspaceFolder, + presentationOptions: { + reveal: getBuildRevealOption(), }, - folderContext.workspaceContext.toolchain - ); - buildAllTaskCache.set(buildReleaseName, folderContext, buildRelease); - } - + disableTaskQueue: true, + dontTriggerTestDiscovery: true, + }, + folderContext.workspaceContext.toolchain + ); + const buildRelease = buildAllTaskCache.get(buildReleaseName, folderContext, buildReleaseTask); return [buildDebug, buildRelease]; } @@ -286,7 +271,7 @@ export function createSwiftTask( cmdEnv: { [key: string]: string } = {} ): SwiftTask { const swift = toolchain.getToolchainExecutable("swift"); - args = toolchain.buildFlags.withSwiftSDKFlags(args); + args = toolchain.buildFlags.withSwiftPackageFlags(toolchain.buildFlags.withSwiftSDKFlags(args)); // Add relative path current working directory const cwd = config.cwd.fsPath; diff --git a/src/toolchain/BuildFlags.ts b/src/toolchain/BuildFlags.ts index 182d72a98..24dffcd74 100644 --- a/src/toolchain/BuildFlags.ts +++ b/src/toolchain/BuildFlags.ts @@ -69,6 +69,20 @@ export class BuildFlags { } } + withSwiftPackageFlags(args: string[]): string[] { + switch (args[0]) { + case "package": + if (args[1] === "resolve" || args[1] === "update") { + return [...args, ...configuration.packageArguments]; + } + return args; + case "build": + return [...args, ...configuration.packageArguments]; + default: + return args; + } + } + /** * Get SDK flags for SwiftPM */ diff --git a/test/integration-tests/WorkspaceContext.test.ts b/test/integration-tests/WorkspaceContext.test.ts index d7f2030aa..afd14f630 100644 --- a/test/integration-tests/WorkspaceContext.test.ts +++ b/test/integration-tests/WorkspaceContext.test.ts @@ -96,6 +96,7 @@ suite("WorkspaceContext Test Suite", () => { suiteTeardown(async () => { await swiftConfig.update("buildArguments", undefined); + await swiftConfig.update("packageArguments", undefined); await swiftConfig.update("path", undefined); await swiftConfig.update("diagnosticsStyle", undefined); }); @@ -163,6 +164,19 @@ suite("WorkspaceContext Test Suite", () => { await swiftConfig.update("buildArguments", []); }); + test("Package Arguments Settings", async () => { + const folder = workspaceContext.folders.find( + f => f.folder.fsPath === packageFolder.fsPath + ); + assert(folder); + await swiftConfig.update("diagnosticsStyle", undefined); + await swiftConfig.update("packageArguments", ["--replace-scm-with-registry"]); + const buildAllTask = createBuildAllTask(folder); + const execution = buildAllTask.execution as SwiftExecution; + assertContainsArg(execution, "--replace-scm-with-registry"); + await swiftConfig.update("packageArguments", []); + }); + test("Swift Path", async () => { /* Temporarily disabled (need swift path to update immediately for this to work) const folder = workspaceContext.folders.find( diff --git a/test/unit-tests/tasks/SwiftTaskProvider.test.ts b/test/unit-tests/tasks/SwiftTaskProvider.test.ts index 6198048ee..f5a4198a3 100644 --- a/test/unit-tests/tasks/SwiftTaskProvider.test.ts +++ b/test/unit-tests/tasks/SwiftTaskProvider.test.ts @@ -50,6 +50,7 @@ suite("SwiftTaskProvider Unit Test Suite", () => { setup(async () => { buildFlags = mockObject({ withSwiftSDKFlags: mockFn(s => s.returns([])), + withSwiftPackageFlags: mockFn(s => s.returns(s.args)), }); toolchain = mockObject({ swiftVersion: new Version(6, 0, 0), @@ -186,6 +187,9 @@ suite("SwiftTaskProvider Unit Test Suite", () => { buildFlags.withSwiftSDKFlags .withArgs(match(["build"])) .returns(["build", "--sdk", "/path/to/sdk"]); + buildFlags.withSwiftPackageFlags + .withArgs(match(["build", "--sdk", "/path/to/sdk"])) + .returns(["build", "--sdk", "/path/to/sdk", "--replace-scm-with-registry"]); const task = createSwiftTask( ["build"], "build", @@ -193,7 +197,12 @@ suite("SwiftTaskProvider Unit Test Suite", () => { instance(toolchain) ); const execution = task.execution as SwiftExecution; - assert.deepEqual(execution.args, ["build", "--sdk", "/path/to/sdk"]); + assert.deepEqual(execution.args, [ + "build", + "--sdk", + "/path/to/sdk", + "--replace-scm-with-registry", + ]); }); test("include environment", () => {