From 222be9920f588be4f1399c905e170c6be6ca8206 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 25 Feb 2026 01:01:37 +0000
Subject: [PATCH 1/5] feat: add CMake: Install Component command (#4281)
Add a new 'CMake: Install Component' command that uses 'cmake --install
--component ' to install specific CMake install components.
- Add parseInstallComponentsFromContent() to parse cmake_install.cmake
- Add getInstallComponents() method on CMakeProject
- Add showComponentSelector() with QuickPick and InputBox fallback
- Add installComponent() method on CMakeProject and ExtensionManager
- Register cmake.installComponent command in package.json
- Add unit tests for component parsing
- Require CMake >= 3.15 with version guard
Co-authored-by: hanniavalera <90047725+hanniavalera@users.noreply.github.com>
---
CHANGELOG.md | 1 +
package.json | 6 +
package.nls.json | 1 +
src/cmakeProject.ts | 118 +++++++++++++++++
src/extension.ts | 14 ++
src/installUtils.ts | 18 +++
.../backend/installComponents.test.ts | 123 ++++++++++++++++++
7 files changed, 281 insertions(+)
create mode 100644 src/installUtils.ts
create mode 100644 test/unit-tests/backend/installComponents.test.ts
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b2a8bb9870..7120c815c7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
Features:
- Add `cmake.shell` setting to route CMake/CTest/CPack subprocess invocations through a custom shell (e.g., Git Bash, MSYS2), enabling embedded toolchains that require POSIX path translation on Windows. [#1750](https://github.com/microsoft/vscode-cmake-tools/issues/1750)
+- Add "CMake: Install Component" command for installing specific CMake install components. [#4281](https://github.com/microsoft/vscode-cmake-tools/issues/4281)
- triple: Add riscv32be riscv64be support. [#4648](https://github.com/microsoft/vscode-cmake-tools/pull/4648) [@lygstate](https://github.com/lygstate)
- Add command to clear build diagnostics from the Problems pane. [#4691](https://github.com/microsoft/vscode-cmake-tools/pull/4691)
- Clear build diagnostics from the Problems pane when a new build starts and populate them incrementally during the build. [#4608](https://github.com/microsoft/vscode-cmake-tools/issues/4608)
diff --git a/package.json b/package.json
index 374d08b8f9..86bd92727d 100644
--- a/package.json
+++ b/package.json
@@ -490,6 +490,12 @@
"when": "cmake:enableFullFeatureSet",
"category": "CMake"
},
+ {
+ "command": "cmake.installComponent",
+ "title": "%cmake-tools.command.cmake.installComponent.title%",
+ "when": "cmake:enableFullFeatureSet",
+ "category": "CMake"
+ },
{
"command": "cmake.buildWithTarget",
"title": "%cmake-tools.command.cmake.buildWithTarget.title%",
diff --git a/package.nls.json b/package.nls.json
index 0959fe5455..adf7758ff5 100644
--- a/package.nls.json
+++ b/package.nls.json
@@ -40,6 +40,7 @@
"cmake-tools.command.cmake.outline.compileFile.title": "Compile File",
"cmake-tools.command.cmake.install.title": "Install",
"cmake-tools.command.cmake.installAll.title": "Install All Projects",
+ "cmake-tools.command.cmake.installComponent.title": "Install Component",
"cmake-tools.command.cmake.buildWithTarget.title": "Build Target",
"cmake-tools.command.cmake.setDefaultTarget.title": "Set Build Target",
"cmake-tools.command.cmake.cleanConfigure.title": "Delete Cache and Reconfigure",
diff --git a/src/cmakeProject.ts b/src/cmakeProject.ts
index 36bc7ba527..833395b935 100644
--- a/src/cmakeProject.ts
+++ b/src/cmakeProject.ts
@@ -54,6 +54,7 @@ import { DebugTrackerFactory, DebuggerInformation, getDebuggerPipeName } from '@
import { NamedTarget, RichTarget, FolderTarget } from '@cmt/drivers/cmakeDriver';
import { CommandResult, ConfigurationType } from 'vscode-cmake-tools';
+import { parseInstallComponentsFromContent } from '@cmt/installUtils';
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
@@ -2639,6 +2640,121 @@ export class CMakeProject {
return this.build(['install'], false, false, cancellationToken);
}
+ /**
+ * Parse cmake_install.cmake in the build directory to discover install component names.
+ */
+ async getInstallComponents(): Promise {
+ const binaryDir = await this.binaryDir;
+ if (!binaryDir) {
+ return [];
+ }
+ const cmakeInstallFile = path.join(binaryDir, 'cmake_install.cmake');
+ if (!await fs.exists(cmakeInstallFile)) {
+ return [];
+ }
+ try {
+ const content = await fs.readFile(cmakeInstallFile);
+ return parseInstallComponentsFromContent(content);
+ } catch {
+ return [];
+ }
+ }
+
+ /**
+ * Show a picker for selecting an install component. Falls back to input box when no components found.
+ */
+ async showComponentSelector(): Promise {
+ const components = await this.getInstallComponents();
+ if (components.length > 0) {
+ const sel = await vscode.window.showQuickPick(
+ components.map(c => ({ label: c, description: localize('install.component.description', 'Install component') })),
+ { placeHolder: localize('select.install.component', 'Select an install component') }
+ );
+ return sel ? sel.label : null;
+ }
+ return await vscode.window.showInputBox({ prompt: localize('enter.component.name', 'Enter a component name') }) || null;
+ }
+
+ /**
+ * Implementation of `cmake.installComponent`
+ */
+ async installComponent(component?: string): Promise {
+ if (!component) {
+ const selected = await this.showComponentSelector();
+ if (!selected) {
+ return { exitCode: -1 };
+ }
+ component = selected;
+ }
+
+ const cmake = await this.getCMakeExecutable();
+ if (!cmake.isPresent) {
+ void vscode.window.showErrorMessage(localize('bad.executable', 'Bad CMake executable: {0}. Check to make sure it is installed or the value of the {1} setting contains the correct path', `"${cmake.path}"`, '"cmake.cmakePath"'));
+ return { exitCode: -1 };
+ }
+
+ const minInstallVersion = util.parseVersion('3.15.0');
+ if (!cmake.version || !util.versionGreaterOrEquals(cmake.version, minInstallVersion)) {
+ void vscode.window.showErrorMessage(localize('cmake.install.component.version.error', 'CMake version 3.15 or later is required for component-based install. Current version: {0}', cmake.version ? util.versionToString(cmake.version) : 'unknown'));
+ return { exitCode: -1 };
+ }
+
+ const drv = await this.getCMakeDriverInstance();
+ if (!drv) {
+ void vscode.window.showErrorMessage(localize('set.up.before.install.component', 'Set up your CMake project before installing a component.'));
+ return { exitCode: -1 };
+ }
+
+ const configResult = await this.ensureConfigured();
+ if (configResult === null || configResult.exitCode !== 0) {
+ return { exitCode: configResult?.exitCode ?? -1 };
+ }
+
+ // Re-fetch driver after configure
+ const driver = await this.getCMakeDriverInstance();
+ if (!driver) {
+ return { exitCode: -1 };
+ }
+
+ const binaryDir = driver.binaryDir;
+ const args: string[] = ['--install', binaryDir, '--component', component];
+
+ const buildType = await this.currentBuildType();
+ if (buildType) {
+ args.push('--config', buildType);
+ }
+
+ const installPrefix = this.workspaceContext.config.installPrefix;
+ if (installPrefix) {
+ const opts = driver.expansionOptions;
+ const expandedPrefix = await expandString(installPrefix, opts);
+ args.push('--prefix', expandedPrefix);
+ }
+
+ log.showChannel();
+ buildLogger.info(localize('starting.install.component', 'Installing component: {0}', component));
+
+ return vscode.window.withProgress(
+ {
+ location: vscode.ProgressLocation.Window,
+ title: localize('installing.component', 'Installing component: {0}', component),
+ cancellable: true
+ },
+ async (_progress, cancel) => {
+ cancel.onCancellationRequested(() => rollbar.invokeAsync(localize('stop.on.cancellation', 'Stop on cancellation'), () => this.stop()));
+ const child = driver.executeCommand(cmake.path, args, undefined, {});
+ const result = await child.result;
+ if (result.retc !== 0) {
+ buildLogger.error(localize('install.component.failed', 'Install component failed with exit code {0}', result.retc));
+ log.showChannel(true);
+ } else {
+ buildLogger.info(localize('install.component.finished', 'Install component finished successfully'));
+ }
+ return { exitCode: result.retc ?? -1 };
+ }
+ );
+ }
+
/**
* Implementation of `cmake.stop`
*/
@@ -3763,3 +3879,5 @@ export class CMakeProject {
}
export default CMakeProject;
+
+export { parseInstallComponentsFromContent } from '@cmt/installUtils';
diff --git a/src/extension.ts b/src/extension.ts
index d36f7df37f..01d86b83c2 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1541,6 +1541,19 @@ export class ExtensionManager implements vscode.Disposable {
return this.runCMakeCommandForAll(cmakeProject => cmakeProject.install(), undefined, true);
}
+ async installComponent(component?: string) {
+ telemetry.logEvent("install", { command: "installComponent"});
+ this.cleanOutputChannel();
+ let activeProject: CMakeProject | undefined = this.getActiveProject();
+ if (!activeProject) {
+ activeProject = await this.pickCMakeProject();
+ if (!activeProject) {
+ return; // Error or nothing is opened
+ }
+ }
+ return activeProject.installComponent(component);
+ }
+
editCache(folder: vscode.WorkspaceFolder) {
telemetry.logEvent("editCMakeCache", { command: "editCMakeCache" });
return this.runCMakeCommand(cmakeProject => cmakeProject.editCache(), folder);
@@ -2425,6 +2438,7 @@ async function setup(context: vscode.ExtensionContext, progress?: ProgressHandle
'setVariantAll',
'install',
'installAll',
+ 'installComponent',
'editCache',
'clean',
'cleanAll',
diff --git a/src/installUtils.ts b/src/installUtils.ts
new file mode 100644
index 0000000000..6ba01b67dd
--- /dev/null
+++ b/src/installUtils.ts
@@ -0,0 +1,18 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+/**
+ * Parse install component names from cmake_install.cmake file content.
+ * Extracts component names from `if(CMAKE_INSTALL_COMPONENT STREQUAL "")` blocks.
+ */
+export function parseInstallComponentsFromContent(content: string): string[] {
+ const regex = /if\(CMAKE_INSTALL_COMPONENT\s+STREQUAL\s+"([^"]+)"/g;
+ const components = new Set();
+ let match: RegExpExecArray | null;
+ while ((match = regex.exec(content)) !== null) {
+ components.add(match[1]);
+ }
+ return Array.from(components).sort();
+}
diff --git a/test/unit-tests/backend/installComponents.test.ts b/test/unit-tests/backend/installComponents.test.ts
new file mode 100644
index 0000000000..8441768587
--- /dev/null
+++ b/test/unit-tests/backend/installComponents.test.ts
@@ -0,0 +1,123 @@
+import { expect } from 'chai';
+import { parseInstallComponentsFromContent } from '@cmt/installUtils';
+
+suite('parseInstallComponentsFromContent', () => {
+ test('parses single component', () => {
+ const content = `
+if(CMAKE_INSTALL_COMPONENT STREQUAL "Runtime")
+ file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/bin" TYPE EXECUTABLE FILES "/path/to/app")
+endif()
+`;
+ const components = parseInstallComponentsFromContent(content);
+ expect(components).to.deep.equal(['Runtime']);
+ });
+
+ test('parses multiple components', () => {
+ const content = `
+if(CMAKE_INSTALL_COMPONENT STREQUAL "Runtime")
+ file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/bin" TYPE EXECUTABLE FILES "/path/to/app")
+endif()
+
+if(CMAKE_INSTALL_COMPONENT STREQUAL "Development")
+ file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/include" TYPE FILE FILES "/path/to/header.h")
+endif()
+
+if(CMAKE_INSTALL_COMPONENT STREQUAL "Documentation")
+ file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/share/doc" TYPE FILE FILES "/path/to/readme.md")
+endif()
+`;
+ const components = parseInstallComponentsFromContent(content);
+ expect(components).to.deep.equal(['Development', 'Documentation', 'Runtime']);
+ });
+
+ test('deduplicates repeated components', () => {
+ const content = `
+if(CMAKE_INSTALL_COMPONENT STREQUAL "Runtime")
+ file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/bin" TYPE EXECUTABLE FILES "/path/to/app1")
+endif()
+
+if(CMAKE_INSTALL_COMPONENT STREQUAL "Runtime")
+ file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/bin" TYPE EXECUTABLE FILES "/path/to/app2")
+endif()
+`;
+ const components = parseInstallComponentsFromContent(content);
+ expect(components).to.deep.equal(['Runtime']);
+ });
+
+ test('returns empty array when no components found', () => {
+ const content = `
+# No install components in this file
+cmake_minimum_required(VERSION 3.15)
+`;
+ const components = parseInstallComponentsFromContent(content);
+ expect(components).to.deep.equal([]);
+ });
+
+ test('returns empty array for empty content', () => {
+ const components = parseInstallComponentsFromContent('');
+ expect(components).to.deep.equal([]);
+ });
+
+ test('returns sorted components', () => {
+ const content = `
+if(CMAKE_INSTALL_COMPONENT STREQUAL "Zebra")
+ file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/bin" TYPE FILE FILES "/path/to/z")
+endif()
+
+if(CMAKE_INSTALL_COMPONENT STREQUAL "Alpha")
+ file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/bin" TYPE FILE FILES "/path/to/a")
+endif()
+
+if(CMAKE_INSTALL_COMPONENT STREQUAL "Middle")
+ file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/bin" TYPE FILE FILES "/path/to/m")
+endif()
+`;
+ const components = parseInstallComponentsFromContent(content);
+ expect(components).to.deep.equal(['Alpha', 'Middle', 'Zebra']);
+ });
+
+ test('handles typical cmake_install.cmake content', () => {
+ const content = `# Install script for directory: /home/user/project
+
+# Set the install prefix
+if(NOT DEFINED CMAKE_INSTALL_PREFIX)
+ set(CMAKE_INSTALL_PREFIX "/usr/local")
+endif()
+string(REGEX REPLACE "/\$" "" CMAKE_INSTALL_PREFIX "\${CMAKE_INSTALL_PREFIX}")
+
+# Set the install configuration name.
+if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)
+ if(BUILD_TYPE)
+ string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" CMAKE_INSTALL_CONFIG_NAME "\${BUILD_TYPE}")
+ else()
+ set(CMAKE_INSTALL_CONFIG_NAME "")
+ endif()
+ message(STATUS "Install configuration: \\"\${CMAKE_INSTALL_CONFIG_NAME}\\"")
+endif()
+
+if(CMAKE_INSTALL_COMPONENT STREQUAL "Runtime" OR NOT CMAKE_INSTALL_COMPONENT)
+ file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/bin" TYPE EXECUTABLE FILES "/path/to/app")
+endif()
+
+if(CMAKE_INSTALL_COMPONENT STREQUAL "Development" OR NOT CMAKE_INSTALL_COMPONENT)
+ file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/lib" TYPE STATIC_LIBRARY FILES "/path/to/lib.a")
+endif()
+
+if(CMAKE_INSTALL_COMPONENT STREQUAL "Headers" OR NOT CMAKE_INSTALL_COMPONENT)
+ file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/include" TYPE FILE FILES "/path/to/header.h")
+endif()
+`;
+ const components = parseInstallComponentsFromContent(content);
+ expect(components).to.deep.equal(['Development', 'Headers', 'Runtime']);
+ });
+
+ test('handles components with special characters in names', () => {
+ const content = `
+if(CMAKE_INSTALL_COMPONENT STREQUAL "my-component_v2")
+ file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/bin" TYPE FILE FILES "/path/to/file")
+endif()
+`;
+ const components = parseInstallComponentsFromContent(content);
+ expect(components).to.deep.equal(['my-component_v2']);
+ });
+});
From b9c91f2f520148d8deed81fa5e3070bd37b58de1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 25 Feb 2026 01:02:21 +0000
Subject: [PATCH 2/5] refactor: remove unnecessary re-export from
cmakeProject.ts
Co-authored-by: hanniavalera <90047725+hanniavalera@users.noreply.github.com>
---
src/cmakeProject.ts | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/cmakeProject.ts b/src/cmakeProject.ts
index 833395b935..6abe30a23e 100644
--- a/src/cmakeProject.ts
+++ b/src/cmakeProject.ts
@@ -3879,5 +3879,3 @@ export class CMakeProject {
}
export default CMakeProject;
-
-export { parseInstallComponentsFromContent } from '@cmt/installUtils';
From 1f74583ae6fde2f76bf7053915d035f97606a97a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 9 Mar 2026 20:15:17 +0000
Subject: [PATCH 3/5] refactor: make cmake.install a first-class operation
using cmake --install
Previously, cmake.install was implemented as `this.build(['install'])`,
routing through `cmake --build --target install`. This didn't
match CMake's actual command model where build and install are distinct
CLI actions.
Now install uses `cmake --install ` (CMake >= 3.15):
- Add CMakeDriver.getCMakeInstallCommand() for command construction
- Add CMakeDriver.install() as a first-class driver method with its own
concurrency gating, telemetry, and output handling
- Refactor CMakeProject.install() to use drv.install() with proper
progress/cancellation/output UX
- Add CMakeTaskProvider.runInstallTask() so install tasks use the new
path instead of routing through runBuildTask
- Falls back to cmake --build --target install for CMake < 3.15
- Add 6 unit tests for install command construction
All existing API surface, telemetry, cancellation, and output behavior
is preserved. The install task and command-palette install now use the
same coherent code path.
Co-authored-by: hanniavalera <90047725+hanniavalera@users.noreply.github.com>
---
src/cmakeProject.ts | 57 +++++++-
src/cmakeTaskProvider.ts | 39 +++++-
src/drivers/cmakeDriver.ts | 98 +++++++++++++
.../unit-tests/backend/installCommand.test.ts | 132 ++++++++++++++++++
4 files changed, 320 insertions(+), 6 deletions(-)
create mode 100644 test/unit-tests/backend/installCommand.test.ts
diff --git a/src/cmakeProject.ts b/src/cmakeProject.ts
index 6abe30a23e..db3f515e22 100644
--- a/src/cmakeProject.ts
+++ b/src/cmakeProject.ts
@@ -2636,8 +2636,61 @@ export class CMakeProject {
/**
* Implementation of `cmake.install`
*/
- install(cancellationToken?: vscode.CancellationToken): Promise {
- return this.build(['install'], false, false, cancellationToken);
+ async install(cancellationToken?: vscode.CancellationToken): Promise {
+ log.info(localize('run.install', 'Installing folder: {0}', await this.binaryDir || this.folderName));
+
+ const configResult = await this.ensureConfigured(cancellationToken);
+ if (configResult === null) {
+ throw new Error(localize('unable.to.configure', 'Build failed: Unable to configure the project'));
+ } else if (configResult.exitCode !== 0) {
+ return {
+ exitCode: configResult.exitCode,
+ stdout: configResult.stdout,
+ stderr: configResult.stderr
+ };
+ }
+ const drv = await this.getCMakeDriverInstance();
+ if (!drv) {
+ throw new Error(localize('driver.died.after.successful.configure', 'CMake driver died immediately after successful configure'));
+ }
+
+ const isBuildingKey = 'cmake:isBuilding';
+ try {
+ this.statusMessage.set(localize('installing.status', 'Installing'));
+ this.isBusy.set(true);
+
+ return await vscode.window.withProgress(
+ {
+ location: vscode.ProgressLocation.Window,
+ title: localize('installing', 'Installing'),
+ cancellable: true
+ },
+ async (_progress, cancel) => {
+ const combinedToken = util.createCombinedCancellationToken(cancel, cancellationToken);
+ combinedToken.onCancellationRequested(() => rollbar.invokeAsync(localize('stop.on.cancellation', 'Stop on cancellation'), () => this.stop()));
+ log.showChannel();
+ buildLogger.info(localize('starting.install', 'Starting install'));
+ await setContextAndStore(isBuildingKey, true);
+ const rc = await drv!.install(undefined, false);
+ await setContextAndStore(isBuildingKey, false);
+ if (rc !== 0) {
+ log.showChannel(true);
+ }
+ if (rc === null) {
+ buildLogger.info(localize('install.was.terminated', 'Install was terminated'));
+ } else {
+ buildLogger.info(localize('install.finished.with.code', 'Install finished with exit code {0}', rc));
+ }
+ return {
+ exitCode: rc === null ? -1 : rc
+ };
+ }
+ );
+ } finally {
+ await setContextAndStore(isBuildingKey, false);
+ this.statusMessage.set(localize('ready.status', 'Ready'));
+ this.isBusy.set(false);
+ }
}
/**
diff --git a/src/cmakeTaskProvider.ts b/src/cmakeTaskProvider.ts
index 972664a25e..08a3dec74d 100644
--- a/src/cmakeTaskProvider.ts
+++ b/src/cmakeTaskProvider.ts
@@ -398,7 +398,7 @@ export class CustomBuildTaskTerminal extends proc.CommandConsumer implements vsc
await this.runBuildTask(CommandType.build);
break;
case CommandType.install:
- await this.runBuildTask(CommandType.install);
+ await this.runInstallTask();
break;
case CommandType.test:
await this.runTestTask();
@@ -441,9 +441,7 @@ export class CustomBuildTaskTerminal extends proc.CommandConsumer implements vsc
this.writeEmitter.fire(localize("target.is.ignored", "The defined targets in this task are being ignored.") + endOfLine);
}
- if (commandType === CommandType.install) {
- targets = ['install'];
- } else if (commandType === CommandType.clean) {
+ if (commandType === CommandType.clean) {
targets = ['clean'];
} else if (!shouldIgnore && !targetIsDefined && !project.useCMakePresets) {
targets = [await project.buildTargetName() || await project.allTargetName];
@@ -622,6 +620,39 @@ export class CustomBuildTaskTerminal extends proc.CommandConsumer implements vsc
}
}
+ private async runInstallTask(): Promise {
+ this.writeEmitter.fire(localize("install.started", "Install task started...") + endOfLine);
+
+ const project: CMakeProject | undefined = await this.getProject();
+ if (!project || !await this.isTaskCompatibleWithPresets(project)) {
+ return;
+ }
+ telemetry.logEvent("task", { taskType: "install", useCMakePresets: String(project.useCMakePresets) });
+ const cmakeDriver: CMakeDriver | undefined = (await project?.getCMakeDriverInstance()) || undefined;
+
+ if (cmakeDriver) {
+ const installCmd = cmakeDriver.getCMakeInstallCommand();
+ if (installCmd) {
+ this.writeEmitter.fire(proc.buildCmdStr(installCmd.command, installCmd.args) + endOfLine);
+ }
+ const result: number | null = await cmakeDriver.install(this, false);
+ if (result === null || result === undefined) {
+ this.writeEmitter.fire(localize('install.terminated', 'Install was terminated') + endOfLine);
+ this.closeEmitter.fire(-1);
+ } else if (result !== 0) {
+ this.writeEmitter.fire(localize("install.finished.with.error", "Install finished with error(s).") + endOfLine);
+ this.closeEmitter.fire(result);
+ } else {
+ this.writeEmitter.fire(localize('install.finished', 'Install finished successfully') + endOfLine);
+ this.closeEmitter.fire(0);
+ }
+ } else {
+ log.debug(localize("cmake.driver.not.found", 'CMake driver not found.'));
+ this.writeEmitter.fire(localize("install.failed", "Install failed.") + endOfLine);
+ this.closeEmitter.fire(-1);
+ }
+ }
+
private async runTestTask(): Promise {
this.writeEmitter.fire(localize("test.started", "Test task started...") + endOfLine);
diff --git a/src/drivers/cmakeDriver.ts b/src/drivers/cmakeDriver.ts
index 7b6b5b8f67..6049bf804e 100644
--- a/src/drivers/cmakeDriver.ts
+++ b/src/drivers/cmakeDriver.ts
@@ -2066,6 +2066,104 @@ export abstract class CMakeDriver implements vscode.Disposable {
}
}
+ /**
+ * Whether this CMake version supports `cmake --install` (>= 3.15).
+ */
+ get supportsInstallCommand(): boolean {
+ return !!this.cmake.version && util.versionGreaterOrEquals(this.cmake.version, util.parseVersion('3.15.0'));
+ }
+
+ /**
+ * Construct the command line for `cmake --install `.
+ * Returns null if cmake --install is not supported.
+ */
+ getCMakeInstallCommand(): proc.BuildCommand | null {
+ if (!this.supportsInstallCommand) {
+ return null;
+ }
+ const args: string[] = ['--install', this.binaryDir];
+
+ // Multi-config generators need --config
+ if (this.isMultiConfFast || this.isMultiConfig) {
+ args.push('--config', this.currentBuildType);
+ }
+
+ // Honor cmake.installPrefix for --prefix
+ if (this.installDir) {
+ args.push('--prefix', this.installDir);
+ }
+
+ return { command: this.cmake.path, args, build_env: {} };
+ }
+
+ /**
+ * Run cmake --install. Uses the build runner for concurrency gating and cancellation.
+ * Falls back to `cmake --build --target install` on CMake < 3.15.
+ */
+ async install(consumer?: proc.OutputConsumer, isBuildCommand?: boolean): Promise {
+ log.debug(localize('start.install', 'Start install'));
+ if (this.isConfigInProgress) {
+ await this.preconditionHandler(CMakePreconditionProblems.ConfigureIsAlreadyRunning);
+ return -1;
+ }
+ if (this.cmakeBuildRunner.isBuildInProgress()) {
+ await this.preconditionHandler(CMakePreconditionProblems.BuildIsAlreadyRunning);
+ return -1;
+ }
+
+ const installCmd = this.getCMakeInstallCommand();
+ if (!installCmd) {
+ // Fallback for CMake < 3.15: use cmake --build --target install
+ return this.build(['install'], consumer, isBuildCommand);
+ }
+
+ this.cmakeBuildRunner.setBuildInProgress(true);
+
+ const pre_build_ok = await this.doPreBuild();
+ if (!pre_build_ok) {
+ this.cmakeBuildRunner.setBuildInProgress(false);
+ return -1;
+ }
+
+ const timeStart: number = new Date().getTime();
+
+ let outputEnc = this.config.outputLogEncoding;
+ const isAutoEncoding = outputEnc === 'auto';
+ if (isAutoEncoding) {
+ if (process.platform === 'win32') {
+ outputEnc = await codepages.getWindowsCodepage();
+ } else {
+ outputEnc = 'utf8';
+ }
+ }
+ const exeOpt: proc.ExecutionOptions = { environment: installCmd.build_env, outputEncoding: outputEnc, useAutoEncoding: isAutoEncoding };
+ this.cmakeBuildRunner.setBuildProcess(this.executeCommand(installCmd.command, installCmd.args, consumer, exeOpt));
+
+ const child = await this.cmakeBuildRunner.getResult();
+
+ const timeEnd: number = new Date().getTime();
+ const duration: number = timeEnd - timeStart;
+ log.info(localize('install.duration', 'Install completed: {0}', util.msToString(duration)));
+ const telemetryMeasures: telemetry.Measures = { Duration: duration };
+
+ if (child) {
+ telemetry.logEvent('install', undefined, telemetryMeasures);
+ } else {
+ telemetryMeasures['ErrorCount'] = 1;
+ telemetry.logEvent('install', undefined, telemetryMeasures);
+ this.cmakeBuildRunner.setBuildInProgress(false);
+ return -1;
+ }
+
+ if (!this.m_stop_process) {
+ await this._refreshExpansions();
+ }
+
+ return (await child.result.finally(() => {
+ this.cmakeBuildRunner.setBuildInProgress(false);
+ })).retc;
+ }
+
private async _doCMakeBuild(targets?: string[], consumer?: proc.OutputConsumer, isBuildCommand?: boolean): Promise {
const buildcmd = await this.getCMakeBuildCommand(targets);
if (buildcmd) {
diff --git a/test/unit-tests/backend/installCommand.test.ts b/test/unit-tests/backend/installCommand.test.ts
new file mode 100644
index 0000000000..2297337ae8
--- /dev/null
+++ b/test/unit-tests/backend/installCommand.test.ts
@@ -0,0 +1,132 @@
+import { expect } from 'chai';
+
+/**
+ * Mirror of the install command construction logic from CMakeDriver.getCMakeInstallCommand().
+ * This tests the command construction independently from the vscode-dependent driver class.
+ */
+interface BuildCommand {
+ command: string;
+ args: string[];
+ build_env: Record;
+}
+
+interface InstallCommandParams {
+ cmakePath: string;
+ binaryDir: string;
+ isMultiConf: boolean;
+ currentBuildType: string;
+ installDir: string | null;
+ supportsInstallCommand: boolean;
+}
+
+function getCMakeInstallCommand(params: InstallCommandParams): BuildCommand | null {
+ if (!params.supportsInstallCommand) {
+ return null;
+ }
+ const args: string[] = ['--install', params.binaryDir];
+
+ if (params.isMultiConf) {
+ args.push('--config', params.currentBuildType);
+ }
+
+ if (params.installDir) {
+ args.push('--prefix', params.installDir);
+ }
+
+ return { command: params.cmakePath, args, build_env: {} };
+}
+
+suite('[Install Command Construction]', () => {
+ test('basic install command for single-config generator', () => {
+ const cmd = getCMakeInstallCommand({
+ cmakePath: '/usr/bin/cmake',
+ binaryDir: '/home/user/project/build',
+ isMultiConf: false,
+ currentBuildType: 'Release',
+ installDir: null,
+ supportsInstallCommand: true
+ });
+ expect(cmd).to.not.be.null;
+ expect(cmd!.command).to.equal('/usr/bin/cmake');
+ expect(cmd!.args).to.deep.equal(['--install', '/home/user/project/build']);
+ });
+
+ test('install command includes --config for multi-config generator', () => {
+ const cmd = getCMakeInstallCommand({
+ cmakePath: '/usr/bin/cmake',
+ binaryDir: '/home/user/project/build',
+ isMultiConf: true,
+ currentBuildType: 'Debug',
+ installDir: null,
+ supportsInstallCommand: true
+ });
+ expect(cmd).to.not.be.null;
+ expect(cmd!.args).to.deep.equal([
+ '--install', '/home/user/project/build',
+ '--config', 'Debug'
+ ]);
+ });
+
+ test('install command includes --prefix when installDir is set', () => {
+ const cmd = getCMakeInstallCommand({
+ cmakePath: '/usr/bin/cmake',
+ binaryDir: '/home/user/project/build',
+ isMultiConf: false,
+ currentBuildType: 'Release',
+ installDir: '/home/user/project/_install',
+ supportsInstallCommand: true
+ });
+ expect(cmd).to.not.be.null;
+ expect(cmd!.args).to.deep.equal([
+ '--install', '/home/user/project/build',
+ '--prefix', '/home/user/project/_install'
+ ]);
+ });
+
+ test('install command includes both --config and --prefix when needed', () => {
+ const cmd = getCMakeInstallCommand({
+ cmakePath: 'C:\\cmake\\bin\\cmake.exe',
+ binaryDir: 'C:\\project\\build',
+ isMultiConf: true,
+ currentBuildType: 'Release',
+ installDir: 'C:\\project\\_install',
+ supportsInstallCommand: true
+ });
+ expect(cmd).to.not.be.null;
+ expect(cmd!.args).to.deep.equal([
+ '--install', 'C:\\project\\build',
+ '--config', 'Release',
+ '--prefix', 'C:\\project\\_install'
+ ]);
+ });
+
+ test('returns null when cmake version does not support --install', () => {
+ const cmd = getCMakeInstallCommand({
+ cmakePath: '/usr/bin/cmake',
+ binaryDir: '/home/user/project/build',
+ isMultiConf: false,
+ currentBuildType: 'Release',
+ installDir: null,
+ supportsInstallCommand: false
+ });
+ expect(cmd).to.be.null;
+ });
+
+ test('installDir null does not produce --prefix', () => {
+ const cmd = getCMakeInstallCommand({
+ cmakePath: '/usr/bin/cmake',
+ binaryDir: '/build',
+ isMultiConf: true,
+ currentBuildType: 'RelWithDebInfo',
+ installDir: null,
+ supportsInstallCommand: true
+ });
+ expect(cmd).to.not.be.null;
+ expect(cmd!.args).to.deep.equal([
+ '--install', '/build',
+ '--config', 'RelWithDebInfo'
+ ]);
+ // Ensure no --prefix argument when installDir is null
+ expect(cmd!.args).to.not.include('--prefix');
+ });
+});
From 829abe27af35c78c838a7a47095036bff4386356 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 9 Mar 2026 20:24:55 +0000
Subject: [PATCH 4/5] revert: remove old installComponent code, keep only
install refactor
Remove the installComponent/installUtils code from previous PR
iterations. This branch should only contain the install refactor
that makes cmake.install a first-class operation.
Co-authored-by: hanniavalera <90047725+hanniavalera@users.noreply.github.com>
---
CHANGELOG.md | 1 -
package.json | 6 -
package.nls.json | 1 -
src/cmakeProject.ts | 116 -----------------
src/extension.ts | 14 --
src/installUtils.ts | 18 ---
.../backend/installComponents.test.ts | 123 ------------------
7 files changed, 279 deletions(-)
delete mode 100644 src/installUtils.ts
delete mode 100644 test/unit-tests/backend/installComponents.test.ts
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7120c815c7..b2a8bb9870 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,6 @@
Features:
- Add `cmake.shell` setting to route CMake/CTest/CPack subprocess invocations through a custom shell (e.g., Git Bash, MSYS2), enabling embedded toolchains that require POSIX path translation on Windows. [#1750](https://github.com/microsoft/vscode-cmake-tools/issues/1750)
-- Add "CMake: Install Component" command for installing specific CMake install components. [#4281](https://github.com/microsoft/vscode-cmake-tools/issues/4281)
- triple: Add riscv32be riscv64be support. [#4648](https://github.com/microsoft/vscode-cmake-tools/pull/4648) [@lygstate](https://github.com/lygstate)
- Add command to clear build diagnostics from the Problems pane. [#4691](https://github.com/microsoft/vscode-cmake-tools/pull/4691)
- Clear build diagnostics from the Problems pane when a new build starts and populate them incrementally during the build. [#4608](https://github.com/microsoft/vscode-cmake-tools/issues/4608)
diff --git a/package.json b/package.json
index 86bd92727d..374d08b8f9 100644
--- a/package.json
+++ b/package.json
@@ -490,12 +490,6 @@
"when": "cmake:enableFullFeatureSet",
"category": "CMake"
},
- {
- "command": "cmake.installComponent",
- "title": "%cmake-tools.command.cmake.installComponent.title%",
- "when": "cmake:enableFullFeatureSet",
- "category": "CMake"
- },
{
"command": "cmake.buildWithTarget",
"title": "%cmake-tools.command.cmake.buildWithTarget.title%",
diff --git a/package.nls.json b/package.nls.json
index adf7758ff5..0959fe5455 100644
--- a/package.nls.json
+++ b/package.nls.json
@@ -40,7 +40,6 @@
"cmake-tools.command.cmake.outline.compileFile.title": "Compile File",
"cmake-tools.command.cmake.install.title": "Install",
"cmake-tools.command.cmake.installAll.title": "Install All Projects",
- "cmake-tools.command.cmake.installComponent.title": "Install Component",
"cmake-tools.command.cmake.buildWithTarget.title": "Build Target",
"cmake-tools.command.cmake.setDefaultTarget.title": "Set Build Target",
"cmake-tools.command.cmake.cleanConfigure.title": "Delete Cache and Reconfigure",
diff --git a/src/cmakeProject.ts b/src/cmakeProject.ts
index db3f515e22..ea54bf905e 100644
--- a/src/cmakeProject.ts
+++ b/src/cmakeProject.ts
@@ -54,7 +54,6 @@ import { DebugTrackerFactory, DebuggerInformation, getDebuggerPipeName } from '@
import { NamedTarget, RichTarget, FolderTarget } from '@cmt/drivers/cmakeDriver';
import { CommandResult, ConfigurationType } from 'vscode-cmake-tools';
-import { parseInstallComponentsFromContent } from '@cmt/installUtils';
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
@@ -2693,121 +2692,6 @@ export class CMakeProject {
}
}
- /**
- * Parse cmake_install.cmake in the build directory to discover install component names.
- */
- async getInstallComponents(): Promise {
- const binaryDir = await this.binaryDir;
- if (!binaryDir) {
- return [];
- }
- const cmakeInstallFile = path.join(binaryDir, 'cmake_install.cmake');
- if (!await fs.exists(cmakeInstallFile)) {
- return [];
- }
- try {
- const content = await fs.readFile(cmakeInstallFile);
- return parseInstallComponentsFromContent(content);
- } catch {
- return [];
- }
- }
-
- /**
- * Show a picker for selecting an install component. Falls back to input box when no components found.
- */
- async showComponentSelector(): Promise {
- const components = await this.getInstallComponents();
- if (components.length > 0) {
- const sel = await vscode.window.showQuickPick(
- components.map(c => ({ label: c, description: localize('install.component.description', 'Install component') })),
- { placeHolder: localize('select.install.component', 'Select an install component') }
- );
- return sel ? sel.label : null;
- }
- return await vscode.window.showInputBox({ prompt: localize('enter.component.name', 'Enter a component name') }) || null;
- }
-
- /**
- * Implementation of `cmake.installComponent`
- */
- async installComponent(component?: string): Promise {
- if (!component) {
- const selected = await this.showComponentSelector();
- if (!selected) {
- return { exitCode: -1 };
- }
- component = selected;
- }
-
- const cmake = await this.getCMakeExecutable();
- if (!cmake.isPresent) {
- void vscode.window.showErrorMessage(localize('bad.executable', 'Bad CMake executable: {0}. Check to make sure it is installed or the value of the {1} setting contains the correct path', `"${cmake.path}"`, '"cmake.cmakePath"'));
- return { exitCode: -1 };
- }
-
- const minInstallVersion = util.parseVersion('3.15.0');
- if (!cmake.version || !util.versionGreaterOrEquals(cmake.version, minInstallVersion)) {
- void vscode.window.showErrorMessage(localize('cmake.install.component.version.error', 'CMake version 3.15 or later is required for component-based install. Current version: {0}', cmake.version ? util.versionToString(cmake.version) : 'unknown'));
- return { exitCode: -1 };
- }
-
- const drv = await this.getCMakeDriverInstance();
- if (!drv) {
- void vscode.window.showErrorMessage(localize('set.up.before.install.component', 'Set up your CMake project before installing a component.'));
- return { exitCode: -1 };
- }
-
- const configResult = await this.ensureConfigured();
- if (configResult === null || configResult.exitCode !== 0) {
- return { exitCode: configResult?.exitCode ?? -1 };
- }
-
- // Re-fetch driver after configure
- const driver = await this.getCMakeDriverInstance();
- if (!driver) {
- return { exitCode: -1 };
- }
-
- const binaryDir = driver.binaryDir;
- const args: string[] = ['--install', binaryDir, '--component', component];
-
- const buildType = await this.currentBuildType();
- if (buildType) {
- args.push('--config', buildType);
- }
-
- const installPrefix = this.workspaceContext.config.installPrefix;
- if (installPrefix) {
- const opts = driver.expansionOptions;
- const expandedPrefix = await expandString(installPrefix, opts);
- args.push('--prefix', expandedPrefix);
- }
-
- log.showChannel();
- buildLogger.info(localize('starting.install.component', 'Installing component: {0}', component));
-
- return vscode.window.withProgress(
- {
- location: vscode.ProgressLocation.Window,
- title: localize('installing.component', 'Installing component: {0}', component),
- cancellable: true
- },
- async (_progress, cancel) => {
- cancel.onCancellationRequested(() => rollbar.invokeAsync(localize('stop.on.cancellation', 'Stop on cancellation'), () => this.stop()));
- const child = driver.executeCommand(cmake.path, args, undefined, {});
- const result = await child.result;
- if (result.retc !== 0) {
- buildLogger.error(localize('install.component.failed', 'Install component failed with exit code {0}', result.retc));
- log.showChannel(true);
- } else {
- buildLogger.info(localize('install.component.finished', 'Install component finished successfully'));
- }
- return { exitCode: result.retc ?? -1 };
- }
- );
- }
-
/**
* Implementation of `cmake.stop`
*/
diff --git a/src/extension.ts b/src/extension.ts
index 01d86b83c2..d36f7df37f 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1541,19 +1541,6 @@ export class ExtensionManager implements vscode.Disposable {
return this.runCMakeCommandForAll(cmakeProject => cmakeProject.install(), undefined, true);
}
- async installComponent(component?: string) {
- telemetry.logEvent("install", { command: "installComponent"});
- this.cleanOutputChannel();
- let activeProject: CMakeProject | undefined = this.getActiveProject();
- if (!activeProject) {
- activeProject = await this.pickCMakeProject();
- if (!activeProject) {
- return; // Error or nothing is opened
- }
- }
- return activeProject.installComponent(component);
- }
-
editCache(folder: vscode.WorkspaceFolder) {
telemetry.logEvent("editCMakeCache", { command: "editCMakeCache" });
return this.runCMakeCommand(cmakeProject => cmakeProject.editCache(), folder);
@@ -2438,7 +2425,6 @@ async function setup(context: vscode.ExtensionContext, progress?: ProgressHandle
'setVariantAll',
'install',
'installAll',
- 'installComponent',
'editCache',
'clean',
'cleanAll',
diff --git a/src/installUtils.ts b/src/installUtils.ts
deleted file mode 100644
index 6ba01b67dd..0000000000
--- a/src/installUtils.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-/**
- * Parse install component names from cmake_install.cmake file content.
- * Extracts component names from `if(CMAKE_INSTALL_COMPONENT STREQUAL "")` blocks.
- */
-export function parseInstallComponentsFromContent(content: string): string[] {
- const regex = /if\(CMAKE_INSTALL_COMPONENT\s+STREQUAL\s+"([^"]+)"/g;
- const components = new Set();
- let match: RegExpExecArray | null;
- while ((match = regex.exec(content)) !== null) {
- components.add(match[1]);
- }
- return Array.from(components).sort();
-}
diff --git a/test/unit-tests/backend/installComponents.test.ts b/test/unit-tests/backend/installComponents.test.ts
deleted file mode 100644
index 8441768587..0000000000
--- a/test/unit-tests/backend/installComponents.test.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-import { expect } from 'chai';
-import { parseInstallComponentsFromContent } from '@cmt/installUtils';
-
-suite('parseInstallComponentsFromContent', () => {
- test('parses single component', () => {
- const content = `
-if(CMAKE_INSTALL_COMPONENT STREQUAL "Runtime")
- file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/bin" TYPE EXECUTABLE FILES "/path/to/app")
-endif()
-`;
- const components = parseInstallComponentsFromContent(content);
- expect(components).to.deep.equal(['Runtime']);
- });
-
- test('parses multiple components', () => {
- const content = `
-if(CMAKE_INSTALL_COMPONENT STREQUAL "Runtime")
- file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/bin" TYPE EXECUTABLE FILES "/path/to/app")
-endif()
-
-if(CMAKE_INSTALL_COMPONENT STREQUAL "Development")
- file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/include" TYPE FILE FILES "/path/to/header.h")
-endif()
-
-if(CMAKE_INSTALL_COMPONENT STREQUAL "Documentation")
- file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/share/doc" TYPE FILE FILES "/path/to/readme.md")
-endif()
-`;
- const components = parseInstallComponentsFromContent(content);
- expect(components).to.deep.equal(['Development', 'Documentation', 'Runtime']);
- });
-
- test('deduplicates repeated components', () => {
- const content = `
-if(CMAKE_INSTALL_COMPONENT STREQUAL "Runtime")
- file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/bin" TYPE EXECUTABLE FILES "/path/to/app1")
-endif()
-
-if(CMAKE_INSTALL_COMPONENT STREQUAL "Runtime")
- file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/bin" TYPE EXECUTABLE FILES "/path/to/app2")
-endif()
-`;
- const components = parseInstallComponentsFromContent(content);
- expect(components).to.deep.equal(['Runtime']);
- });
-
- test('returns empty array when no components found', () => {
- const content = `
-# No install components in this file
-cmake_minimum_required(VERSION 3.15)
-`;
- const components = parseInstallComponentsFromContent(content);
- expect(components).to.deep.equal([]);
- });
-
- test('returns empty array for empty content', () => {
- const components = parseInstallComponentsFromContent('');
- expect(components).to.deep.equal([]);
- });
-
- test('returns sorted components', () => {
- const content = `
-if(CMAKE_INSTALL_COMPONENT STREQUAL "Zebra")
- file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/bin" TYPE FILE FILES "/path/to/z")
-endif()
-
-if(CMAKE_INSTALL_COMPONENT STREQUAL "Alpha")
- file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/bin" TYPE FILE FILES "/path/to/a")
-endif()
-
-if(CMAKE_INSTALL_COMPONENT STREQUAL "Middle")
- file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/bin" TYPE FILE FILES "/path/to/m")
-endif()
-`;
- const components = parseInstallComponentsFromContent(content);
- expect(components).to.deep.equal(['Alpha', 'Middle', 'Zebra']);
- });
-
- test('handles typical cmake_install.cmake content', () => {
- const content = `# Install script for directory: /home/user/project
-
-# Set the install prefix
-if(NOT DEFINED CMAKE_INSTALL_PREFIX)
- set(CMAKE_INSTALL_PREFIX "/usr/local")
-endif()
-string(REGEX REPLACE "/\$" "" CMAKE_INSTALL_PREFIX "\${CMAKE_INSTALL_PREFIX}")
-
-# Set the install configuration name.
-if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)
- if(BUILD_TYPE)
- string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" CMAKE_INSTALL_CONFIG_NAME "\${BUILD_TYPE}")
- else()
- set(CMAKE_INSTALL_CONFIG_NAME "")
- endif()
- message(STATUS "Install configuration: \\"\${CMAKE_INSTALL_CONFIG_NAME}\\"")
-endif()
-
-if(CMAKE_INSTALL_COMPONENT STREQUAL "Runtime" OR NOT CMAKE_INSTALL_COMPONENT)
- file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/bin" TYPE EXECUTABLE FILES "/path/to/app")
-endif()
-
-if(CMAKE_INSTALL_COMPONENT STREQUAL "Development" OR NOT CMAKE_INSTALL_COMPONENT)
- file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/lib" TYPE STATIC_LIBRARY FILES "/path/to/lib.a")
-endif()
-
-if(CMAKE_INSTALL_COMPONENT STREQUAL "Headers" OR NOT CMAKE_INSTALL_COMPONENT)
- file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/include" TYPE FILE FILES "/path/to/header.h")
-endif()
-`;
- const components = parseInstallComponentsFromContent(content);
- expect(components).to.deep.equal(['Development', 'Headers', 'Runtime']);
- });
-
- test('handles components with special characters in names', () => {
- const content = `
-if(CMAKE_INSTALL_COMPONENT STREQUAL "my-component_v2")
- file(INSTALL DESTINATION "\${CMAKE_INSTALL_PREFIX}/bin" TYPE FILE FILES "/path/to/file")
-endif()
-`;
- const components = parseInstallComponentsFromContent(content);
- expect(components).to.deep.equal(['my-component_v2']);
- });
-});
From 98021c4a06dfb28f40beab4823635cfb01f782ce Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 9 Mar 2026 20:26:23 +0000
Subject: [PATCH 5/5] style: address code review feedback - clarify parameter
names and test docs
Co-authored-by: hanniavalera <90047725+hanniavalera@users.noreply.github.com>
---
src/cmakeTaskProvider.ts | 2 +-
test/unit-tests/backend/installCommand.test.ts | 4 +++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/cmakeTaskProvider.ts b/src/cmakeTaskProvider.ts
index 08a3dec74d..1884d0a14c 100644
--- a/src/cmakeTaskProvider.ts
+++ b/src/cmakeTaskProvider.ts
@@ -635,7 +635,7 @@ export class CustomBuildTaskTerminal extends proc.CommandConsumer implements vsc
if (installCmd) {
this.writeEmitter.fire(proc.buildCmdStr(installCmd.command, installCmd.args) + endOfLine);
}
- const result: number | null = await cmakeDriver.install(this, false);
+ const result: number | null = await cmakeDriver.install(this, /* isBuildCommand */ false);
if (result === null || result === undefined) {
this.writeEmitter.fire(localize('install.terminated', 'Install was terminated') + endOfLine);
this.closeEmitter.fire(-1);
diff --git a/test/unit-tests/backend/installCommand.test.ts b/test/unit-tests/backend/installCommand.test.ts
index 2297337ae8..76ba76c320 100644
--- a/test/unit-tests/backend/installCommand.test.ts
+++ b/test/unit-tests/backend/installCommand.test.ts
@@ -2,7 +2,9 @@ import { expect } from 'chai';
/**
* Mirror of the install command construction logic from CMakeDriver.getCMakeInstallCommand().
- * This tests the command construction independently from the vscode-dependent driver class.
+ * Backend tests cannot import modules that depend on 'vscode', so we mirror the pure
+ * command-construction logic here. If the driver implementation changes, update this mirror
+ * to match. See also: targetMap.test.ts and shell-propagation.test.ts for the same pattern.
*/
interface BuildCommand {
command: string;