Skip to content

Optionally provide build tasks for library products #1741

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

## {{releaseVersion}} - {{releaseDate}}

### Added

- New `swift.createTasksForLibraryProducts` setting that when enabled causes the extension to automatically create and provide tasks for library products ([#1741](https://github.com/swiftlang/vscode-swift/pull/1741))

## 2.10.0 - 2025-07-28

### Added

- Added Swiftly toolchain management support `.swift-version` files, and integration with the toolchain selection UI ([#1717](https://github.com/swiftlang/vscode-swift/pull/1717)
- Added Swiftly toolchain management support `.swift-version` files, and integration with the toolchain selection UI ([#1717](https://github.com/swiftlang/vscode-swift/pull/1717))
- Added code lenses to run suites/tests, configurable with the `swift.showTestCodeLenses` setting ([#1698](https://github.com/swiftlang/vscode-swift/pull/1698))
- New `swift.excludePathsFromActivation` setting to ignore specified sub-folders from being activated as projects ([#1693](https://github.com/swiftlang/vscode-swift/pull/1693))
- New `swift.recordTestDuration` setting to disable capturing test durations, which can improve performance of heavy test runs ([#1745](https://github.com/swiftlang/vscode-swift/pull/1745))
Expand Down
4 changes: 4 additions & 0 deletions assets/test/defaultPackage/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ let package = Package(
.library(
name: "PackageLib",
targets: ["PackageLib"]),
.library(
name: "PackageLib2",
type: .dynamic,
targets: ["PackageLib"]),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand Down
4 changes: 4 additions & 0 deletions assets/test/defaultPackage/Package@swift-6.0.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ let package = Package(
.library(
name: "PackageLib",
targets: ["PackageLib"]),
.library(
name: "PackageLib2",
type: .dynamic,
targets: ["PackageLib"]),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand Down
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,12 @@
],
"scope": "application"
},
"swift.createTasksForLibraryProducts": {
"type": "boolean",
"default": false,
"markdownDescription": "When enabled, the extension will create \"swift\" build tasks for library products in the package manifest. Note that automatic library products will not be included.",
"scope": "machine-overridable"
},
"swift.showCreateSwiftProjectInWelcomePage": {
"type": "boolean",
"default": true,
Expand Down
4 changes: 4 additions & 0 deletions src/SwiftPackage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export interface Product {
type: { executable?: null; library?: string[] };
}

export function isAutomatic(product: Product): boolean {
return (product.type.library || []).includes("automatic");
}

/** Swift Package Manager target */
export interface Target {
name: string;
Expand Down
6 changes: 6 additions & 0 deletions src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,12 @@ const configuration = {
.getConfiguration("swift")
.get<ShowBuildStatusOptions>("showBuildStatus", "swiftStatus");
},
/** create build tasks for the library products of the package(s) */
get createTasksForLibraryProducts(): boolean {
return vscode.workspace
.getConfiguration("swift")
.get<boolean>("createTasksForLibraryProducts", false);
},
/** background compilation */
get backgroundCompilation(): boolean {
return vscode.workspace
Expand Down
12 changes: 11 additions & 1 deletion src/tasks/SwiftTaskProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import * as vscode from "vscode";
import { WorkspaceContext } from "../WorkspaceContext";
import { FolderContext } from "../FolderContext";
import { Product } from "../SwiftPackage";
import { isAutomatic, Product } from "../SwiftPackage";
import configuration, {
ShowBuildStatusOptions,
substituteVariablesInString,
Expand Down Expand Up @@ -425,6 +425,16 @@ export class SwiftTaskProvider implements vscode.TaskProvider {
for (const executable of executables) {
tasks.push(...createBuildTasks(executable, folderContext));
}

if (configuration.createTasksForLibraryProducts) {
const libraries = await folderContext.swiftPackage.libraryProducts;
for (const lib of libraries) {
if (isAutomatic(lib)) {
continue;
}
tasks.push(...createBuildTasks(lib, folderContext));
}
}
}
return tasks;
}
Expand Down
48 changes: 47 additions & 1 deletion test/integration-tests/tasks/SwiftTaskProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ import { executeTaskAndWaitForResult, waitForEndTaskProcess } from "../../utilit
import { Version } from "../../../src/utilities/version";
import { FolderContext } from "../../../src/FolderContext";
import { mockGlobalObject } from "../../MockUtils";
import { activateExtensionForSuite, folderInRootWorkspace } from "../utilities/testutilities";
import {
activateExtensionForSuite,
folderInRootWorkspace,
updateSettings,
} from "../utilities/testutilities";

suite("SwiftTaskProvider Test Suite", () => {
let workspaceContext: WorkspaceContext;
Expand Down Expand Up @@ -92,6 +96,13 @@ suite("SwiftTaskProvider Test Suite", () => {
});

suite("provideTasks", () => {
let resetSettings: (() => Promise<void>) | undefined;
teardown(async () => {
if (resetSettings) {
await resetSettings();
}
});

suite("includes build all task from extension", () => {
let task: vscode.Task | undefined;

Expand Down Expand Up @@ -150,6 +161,41 @@ suite("SwiftTaskProvider Test Suite", () => {
expect(task?.detail).to.include("swift build --product PackageExe");
});

test("includes library build tasks task", async () => {
const taskProvider = workspaceContext.taskProvider;
let tasks = await taskProvider.provideTasks(new vscode.CancellationTokenSource().token);
let task = tasks.find(t => t.name === "Build Debug PackageLib2 (defaultPackage)");
expect(task).to.be.undefined;
task = tasks.find(t => t.name === "Build Release PackageLib2 (defaultPackage)");
expect(task).to.be.undefined;

resetSettings = await updateSettings({
"swift.createTasksForLibraryProducts": true,
});

tasks = await taskProvider.provideTasks(new vscode.CancellationTokenSource().token);
task = tasks.find(t => t.name === "Build Debug PackageLib2 (defaultPackage)");
expect(
task,
'expected to find a task named "Build Debug PackageLib2 (defaultPackage)", instead found ' +
tasks.map(t => t.name)
).to.not.be.undefined;
expect(task?.detail).to.include("swift build --product PackageLib2");
task = tasks.find(t => t.name === "Build Release PackageLib2 (defaultPackage)");
expect(
task,
'expected to find a task named "Build Release PackageLib2 (defaultPackage)", instead found ' +
tasks.map(t => t.name)
).to.not.be.undefined;
expect(task?.detail).to.include("swift build -c release --product PackageLib2");

// Don't include automatic products
task = tasks.find(t => t.name === "Build Debug PackageLib (defaultPackage)");
expect(task).to.be.undefined;
task = tasks.find(t => t.name === "Build Release PackageLib (defaultPackage)");
expect(task).to.be.undefined;
});

test("includes product release task", async () => {
const taskProvider = workspaceContext.taskProvider;
const tasks = await taskProvider.provideTasks(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,17 @@ Add tasks for common operations with your Package.
For workspaces that contain a `Package.swift` file, the Swift extension adds the following tasks:

- **Build All**: Build all targets in the Package.
- **Build Debug <Executable>**: Each executable in a Package.swift get a task for building a debug build.
- **Build Release <Executable>**: Each executable in a Package.swift get a task for building a release build.
- **Build Debug \<Executable\>**: Each executable product in a Package.swift get a task for building a debug build.
- **Build Release \<Executable\>**: Each executable product in a Package.swift get a task for building a release build.

> 💡 Tip: Tasks use workflows common to all VS Code extensions. For more information see [the VS Code documentation for tasks](https://code.visualstudio.com/docs/editor/tasks).

These tasks are available via the commands **Terminal ▸ Run Task...** and **Terminal ▸ Run Build Task...** in the command palette.

## Create tasks for library targets

By default build tasks are only automatically created for executable products. If you set the `swift.createTasksForLibraryProducts` setting to true, then additional tasks will be created:
- **Build Debug \<Library\>**: Each library product in a Package.swift get a task for building a debug build.
- **Build Release \<Library\>**: Each library product in a Package.swift get a task for building a release build.

> ⚠️ Important: Tasks will not be created for automatic library products, as you cannot specify a `--product` option for automatic products when building. For more information see the [Swift Package Manager documentation for Product definitions](https://docs.swift.org/package-manager/PackageDescription/PackageDescription.html#product).