diff --git a/.eslintrc.js b/.eslintrc.js index ec1189c6e..795d0b79e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -92,7 +92,7 @@ module.exports = { "no-trailing-spaces": "error", "no-undef-init": "error", "no-unsafe-finally": "error", - "no-unused-expressions": "error", + "no-unused-expressions": "off", "no-unused-labels": "error", "no-var": "error", "one-var": [ diff --git a/CHANGELOG.md b/CHANGELOG.md index c5816fe0f..8536f19e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Improvements: - Improve when the "Configure with Debugger" popup appears and allow for "Do Not Show Again". [#3343](https://github.com/microsoft/vscode-cmake-tools/issues/3343) - Add option to disable "Not all open documents were saved" popup. [#2889](https://github.com/microsoft/vscode-cmake-tools/issues/2889) +- Allow overriding of CMakePresets cache variables and arguments [#1836](https://github.com/microsoft/vscode-cmake-tools/issues/1836) Bug Fixes: diff --git a/docs/cmake-presets.md b/docs/cmake-presets.md index 6c839abf0..6422e546a 100644 --- a/docs/cmake-presets.md +++ b/docs/cmake-presets.md @@ -223,7 +223,7 @@ CMake Tools supports command substitution for launch commands when `CMakePresets `CMakePresets.json` should be the source of truth for all settings related to configure, build, and test. This eliminates behavior specific to Visual Studio Code and ensures that your CMake and CTest invocations can be reproduced from the command line. -The following settings in `settings.json` either duplicate options in `CMakePresets.json` or no longer apply. These settings will be ignored when `CMakePresets.json` integration is enabled. Ignored settings will be logged to the Output Window when you run **CMake: Configure**. +The following settings in `settings.json` either duplicate options in `CMakePresets.json` or no longer apply. These settings will be ignored when `CMakePresets.json` integration is enabled. | Ignored setting in `settings.json` | `CMakePresets.json` equivalent | |--|--| @@ -231,7 +231,6 @@ The following settings in `settings.json` either duplicate options in `CMakePres | `cmake.buildDirectory` | `configurePresets.binaryDir` | | `cmake.buildEnvironment` | `buildPresets.environment` | | `cmake.buildToolsArgs` | `buildPresets.nativeToolOptions` | -| `cmake.cmakePath` | `configurePresets.cmakeExecutable` | | `cmake.configureArgs` | Various options in `configurePreset` | | `cmake.configureEnvironment` | `configurePresets.environment` | | `cmake.configureSettings` | `configurePresets.cacheVariables` | @@ -249,6 +248,19 @@ The following settings in `settings.json` either duplicate options in `CMakePres | `cmake.testEnvironment` | `testPresets.environment` | | `cmake.toolset` | `configurePresets.toolset` | +## Settings that can be used to override CMakePresets.json settings for temporary testing + +The following settings can be used temporarily when CMakePresets integration is enabled. + +| Setting in `settings.json` | `CMakePresets.json` equivalent | +|--|--| +| `cmake.buildArgs` | Various options in `buildPreset` | +| `cmake.buildEnvironment` | `buildPresets.environment` | +| `cmake.buildToolsArgs` | `buildPresets.nativeToolOptions` | +| `cmake.configureArgs` | Various options in `configurePreset` | +| `cmake.configureEnvironment` | `configurePresets.environment` | +| `cmake.testEnvironment` | `testPresets.environment` | + ## Unsupported commands The following commands are not supported when `CMakePresets.json` integration is enabled: diff --git a/package.json b/package.json index 9cccfbdb5..4211382e5 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,13 @@ "when": "cmake:enableFullFeatureSet && useCMakePresets", "category": "CMake" }, + { + "command": "cmake.projectStatus.viewConfigureSettings", + "title": "%cmake-tools.command.cmake.viewConfigureSettings.title%", + "when": "cmake:enabelFullFeatureSet && useCMakePresets", + "category": "CMake", + "icon": "$(settings-gear)" + }, { "command": "cmake.projectStatus.selectConfigurePreset", "title": "%cmake-tools.command.cmake.selectConfigurePreset.title%", @@ -105,6 +112,13 @@ "when": "cmake:enableFullFeatureSet && useCMakePresets", "category": "CMake" }, + { + "command": "cmake.projectStatus.viewBuildSettings", + "title": "%cmake-tools.command.cmake.viewBuildSettings.title%", + "when": "cmake:enableFullFeatureSet && useCMakePresets", + "category": "CMake", + "icon": "$(settings-gear)" + }, { "command": "cmake.projectStatus.selectBuildPreset", "title": "%cmake-tools.command.cmake.selectBuildPreset.title%", @@ -112,6 +126,13 @@ "category": "CMake", "icon": "$(edit)" }, + { + "command": "cmake.projectStatus.viewTestSettings", + "title": "%cmake-tools.command.cmake.viewTestSettings.title%", + "when": "cmake:enableFullFeatureSet && useCMakePresets", + "category": "CMake", + "icon": "$(settings-gear)" + }, { "command": "cmake.selectTestPreset", "title": "%cmake-tools.command.cmake.selectTestPreset.title%", @@ -1304,10 +1325,18 @@ "command": "cmake.projectStatus.selectConfigurePreset", "when": "never" }, + { + "command": "cmake.projectStatus.viewConfigureSettings", + "when": "never" + }, { "command": "cmake.projectStatus.build", "when": "never" }, + { + "command": "cmake.projectStatus.viewBuildSettings", + "when": "never" + }, { "command": "cmake.projectStatus.setDefaultTarget", "when": "never" @@ -1316,6 +1345,10 @@ "command": "cmake.projectStatus.selectBuildPreset", "when": "never" }, + { + "command": "cmake.projectStatus.viewTestSettings", + "when": "never" + }, { "command": "cmake.projectStatus.ctest", "when": "never" @@ -1451,7 +1484,12 @@ }, { "command": "cmake.projectStatus.selectConfigurePreset", - "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'configPreset'", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem =~ /configPreset/", + "group": "inline" + }, + { + "command": "cmake.projectStatus.viewConfigureSettings", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'configPreset - overrides present'", "group": "inline" }, { @@ -1466,9 +1504,14 @@ }, { "command": "cmake.projectStatus.selectBuildPreset", - "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'buildPreset'", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem =~ /buildPreset/", "group": "inline" }, + { + "command": "cmake.projectStatus.viewBuildSettings", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'buildPreset - overrides present'", + "group": "inline" + }, { "command": "cmake.projectStatus.ctest", "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'test'", @@ -1481,9 +1524,14 @@ }, { "command": "cmake.projectStatus.selectTestPreset", - "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'testPreset'", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem =~ /testPreset/", "group": "inline" }, + { + "command": "cmake.projectStatus.viewTestSettings", + "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'testPreset - overrides present'", + "group": "inline" + }, { "command": "cmake.projectStatus.debugTarget", "when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'debug'", diff --git a/package.nls.json b/package.nls.json index 8add3ce64..c92b088ec 100644 --- a/package.nls.json +++ b/package.nls.json @@ -4,8 +4,11 @@ "cmake-tools.command.cmake.addBuildPreset.title": "Add Build Preset", "cmake-tools.command.cmake.addTestPreset.title": "Add Test Preset", "cmake-tools.command.cmake.selectConfigurePreset.title": "Select Configure Preset", + "cmake-tools.command.cmake.viewConfigureSettings.title": "View the settings overriding your Configure Preset", "cmake-tools.command.cmake.selectBuildPreset.title": "Select Build Preset", + "cmake-tools.command.cmake.viewBuildSettings.title": "View the settings overriding your Build Preset", "cmake-tools.command.cmake.selectTestPreset.title": "Select Test Preset", + "cmake-tools.command.cmake.viewTestSettings.title": "View the settings overriding your Test Preset", "cmake-tools.command.cmake.viewLog.title": "Open the CMake Tools Log File", "cmake-tools.command.cmake.logDiagnostics.title": "Log Diagnostics", "cmake-tools.command.cmake.editKits.title": "Edit User-Local CMake Kits", @@ -74,7 +77,7 @@ "cmake-tools.configuration.cmake.saveBeforeBuild.description": "Save open files before building.", "cmake-tools.configuration.cmake.buildBeforeRun.description": "Build the target before running it.", "cmake-tools.configuration.cmake.clearOutputBeforeBuild.description": "Clear build output before each build.", - "cmake-tools.configuration.cmake.configureSettings.description": "CMake variables to set on the command line.", + "cmake-tools.configuration.cmake.configureSettings.description": "CMake variables to set on the command line. This setting is specific to kits and will not be used for CMake Presets.", "cmake-tools.configuration.cmake.cacheInit.string.description": "Path to a cache-initializing CMake file.", "cmake-tools.configuration.cmake.cacheInit.array.description": "List of cache initializer files.", "cmake-tools.configuration.cmake.cacheInit.array.string.description": "A cache initializing CMake file.", @@ -83,9 +86,9 @@ "cmake-tools.configuration.cmake.generator.description": "The CMake generator to use.", "cmake-tools.configuration.cmake.toolset.description": "The CMake toolset to use when configuring.", "cmake-tools.configuration.cmake.platform.description": "The CMake platform to use when configuring.", - "cmake-tools.configuration.cmake.configureArgs.description": "Additional arguments to pass to CMake when configuring.", - "cmake-tools.configuration.cmake.buildArgs.description": "Additional arguments to pass to CMake when building.", - "cmake-tools.configuration.cmake.buildToolArgs.description": "Additional arguments to pass to the underlying build tool when building.", + "cmake-tools.configuration.cmake.configureArgs.description": "Additional arguments to pass to CMake when configuring. When using CMake Presets, these arguments are temporarily appended to the arguments provided by the active configure preset.", + "cmake-tools.configuration.cmake.buildArgs.description": "Additional arguments to pass to CMake when building. When using CMake Presets, these arguments are temporarily appended to the arguments provided by the active build preset.", + "cmake-tools.configuration.cmake.buildToolArgs.description": "Additional arguments to pass to the underlying build tool when building. When using CMake Presets, these arguments are temporarily appended to the arguments provided by the active build preset to invoke the build tool.", "cmake-tools.configuration.cmake.parallelJobs.description": "The number of parallel build jobs. Use zero to automatically detect the number of CPUs. Setting this to 1 will omit the parallelism flag (-j) from the underlying build command, which has a generator-dependent effect on build parallelism.", "cmake-tools.configuration.cmake.ctestPath.description": "Path to CTest executable. If null, will be inferred from cmake.cmakePath (recommended to leave null).", "cmake-tools.configuration.cmake.ctest.parallelJobs.markdownDescription": { @@ -136,13 +139,13 @@ "cmake-tools.configuration.cmake.defaultVariants.buildType.release.long": "Optimize for speed - exclude debug information.", "cmake-tools.configuration.cmake.defaultVariants.buildType.minsize.long": "Optimize for smallest binary size - exclude debug information.", "cmake-tools.configuration.cmake.defaultVariants.buildType.reldeb.long": "Optimize for speed - include debug information.", - "cmake-tools.configuration.cmake.ctestArgs.description": "Arguments to pass to CTest.", + "cmake-tools.configuration.cmake.ctestArgs.description": "Additional arguments to pass to CTest. When using CMake Presets, these arguments are temporarily added to the arguments provided by the active test preset.", "cmake-tools.configuration.cmake.ctestDefaultArgs.description": "Arguments passed by default to CTest.", - "cmake-tools.configuration.cmake.environment.description": "Environment variables to set when running CMake commands.", + "cmake-tools.configuration.cmake.environment.description": "Environment variables to set when running CMake commands. When using CMake Presets, these are temporarily added to the environment used for CMake commands.", "cmake-tools.configuration.cmake.environment.additionalProperties.description": "Value for the environment variable.", - "cmake-tools.configuration.cmake.configureEnvironment.description": "Environment variables to pass to CMake during configure.", - "cmake-tools.configuration.cmake.buildEnvironment.description": "Environment variables to pass to CMake during build.", - "cmake-tools.configuration.cmake.testEnvironment.description": "Environment variables to pass to CTest.", + "cmake-tools.configuration.cmake.configureEnvironment.description": "Environment variables to pass to CMake during configure. When using CMake Presets, these are temporarily added to the environment provided by the active configure preset.", + "cmake-tools.configuration.cmake.buildEnvironment.description": "Environment variables to pass to CMake during build. When using CMake Presets, these are temporarily added to the environment provided by the active build preset.", + "cmake-tools.configuration.cmake.testEnvironment.description": "Environment variables to pass to CTest. When using CMake Presets, these are temporarily added to the environment provided by the active test preset.", "cmake-tools.configuration.cmake.mingwSearchDirs.description": "Directories where MinGW may be installed.", "cmake-tools.configuration.cmake.additionalCompilerSearchDirs.description": "Additional directories to search for compilers.", "cmake-tools.configuration.cmake.searchDirs.items.description": "Path to a directory.", diff --git a/src/cmakeProject.ts b/src/cmakeProject.ts index a969bec9f..bb8e57716 100644 --- a/src/cmakeProject.ts +++ b/src/cmakeProject.ts @@ -40,7 +40,7 @@ import { VariantManager } from './variant'; import * as nls from 'vscode-nls'; import { ConfigurationWebview } from './cacheView'; import { enableFullFeatureSet, extensionManager, updateFullFeatureSet } from './extension'; -import { CMakeCommunicationMode, ConfigurationReader, OptionConfig, UseCMakePresets } from './config'; +import { CMakeCommunicationMode, ConfigurationReader, OptionConfig, UseCMakePresets, checkConfigureOverridesPresent } from './config'; import * as preset from '@cmt/preset'; import * as util from '@cmt/util'; import { Environment, EnvironmentUtils } from './environmentVariables'; @@ -2348,6 +2348,10 @@ export class CMakeProject { // Add environment variables from ConfigureEnvironment. const configureEnv = await drv?.getConfigureEnvironment(); + if ((drv?.useCMakePresets ?? false) && (checkConfigureOverridesPresent(this.workspaceContext.config) ?? false)) { + log.info(localize('launch.with.overrides', `NOTE: You are launching a target and there are some environment overrides being applied from your VS Code settings.`)); + } + return EnvironmentUtils.merge([env, configureEnv]); } diff --git a/src/config.ts b/src/config.ts index b2a1d6445..ee4b7d945 100644 --- a/src/config.ts +++ b/src/config.ts @@ -604,3 +604,32 @@ const activeChangeEvents: PromiseTracker = new PromiseTracker(); export function getSettingsChangePromise(): Promise { return activeChangeEvents.getAwaiter(); } + +export function checkConfigureOverridesPresent(config: ConfigurationReader): boolean { + if (config.configureArgs.length > 0 || Object.values(config.configureEnvironment).length > 0 || checkGeneralEnvironmentOverridesPresent(config)) { + return true; + } + + return false; +} + +export function checkBuildOverridesPresent(config: ConfigurationReader): boolean { + if (config.buildArgs.length > 0 || config.buildToolArgs.length > 0 + || Object.values(config.buildEnvironment).length > 0 || checkGeneralEnvironmentOverridesPresent(config)) { + return true; + } + + return false; +} + +export function checkTestOverridesPresent(config: ConfigurationReader): boolean { + if (Object.values(config.testEnvironment).length > 0 || config.ctestArgs.length > 0 || checkGeneralEnvironmentOverridesPresent(config)) { + return true; + } + + return false; +} + +export function checkGeneralEnvironmentOverridesPresent(config: ConfigurationReader): boolean { + return Object.values(config.environment).length > 0; +} diff --git a/src/ctest.ts b/src/ctest.ts index b8b667105..5b7e3db69 100644 --- a/src/ctest.ts +++ b/src/ctest.ts @@ -247,29 +247,29 @@ export class CTestDriver implements vscode.Disposable { private async getCTestArgs(driver: CMakeDriver, customizedTask: boolean = false, testPreset?: TestPreset): Promise { let ctestArgs: string[]; + const opts = driver.expansionOptions; + const initialArgs = await Promise.all(this.ws.config.ctestDefaultArgs.map(async (value) => expandString(value, driver.expansionOptions))); + const additionalArgs = await Promise.all(this.ws.config.ctestArgs.map(async (value) => expandString(value, driver.expansionOptions))); + + ctestArgs = initialArgs.slice(0); + if (customizedTask && testPreset) { - ctestArgs = ['-T', 'test'].concat(testArgs(testPreset)); + ctestArgs = ctestArgs.concat(testArgs(testPreset)); } else if (!customizedTask && driver.useCMakePresets) { if (!driver.testPreset) { // Test explorer doesn't handle errors well, so we need to deal with them ourselves return undefined; } // Add a few more args so we can show the result in status bar - ctestArgs = ['-T', 'test'].concat(testArgs(driver.testPreset)); + ctestArgs = ctestArgs.concat(testArgs(driver.testPreset)); } else { const configuration = driver.currentBuildType; - const opts = driver.expansionOptions; const jobs = await expandString(this.ws.config.numCTestJobs, opts); - const defaultArgs = []; - for (const value of this.ws.config.ctestDefaultArgs) { - defaultArgs.push(await expandString(value, opts)); - } - const args = []; - for (const value of this.ws.config.ctestArgs) { - args.push(await expandString(value, opts)); - } - ctestArgs = [`-j${jobs}`, '-C', configuration].concat(defaultArgs, args); + ctestArgs = [`-j${jobs}`, '-C', configuration].concat(ctestArgs); } + + ctestArgs = ctestArgs.concat(additionalArgs); + return ctestArgs; } @@ -301,29 +301,11 @@ export class CTestDriver implements vscode.Disposable { return -2; } - let ctestArgs: string[]; - if (customizedTask && testPreset) { - ctestArgs = ['-T', 'test'].concat(testArgs(testPreset)); - } else if (!customizedTask && driver.useCMakePresets) { - if (!driver.testPreset) { - log.error(localize('test.preset.not.set', 'Test preset is not set')); - return -3; - } - // Add a few more args so we can show the result in status bar - ctestArgs = ['-T', 'test'].concat(testArgs(driver.testPreset)); - } else { - const configuration = driver.currentBuildType; - const opts = driver.expansionOptions; - const jobs = await expandString(this.ws.config.numCTestJobs, opts); - const defaultArgs = []; - for (const value of this.ws.config.ctestDefaultArgs) { - defaultArgs.push(await expandString(value, opts)); - } - const args = []; - for (const value of this.ws.config.ctestArgs) { - args.push(await expandString(value, opts)); - } - ctestArgs = [`-j${jobs}`, '-C', configuration].concat(defaultArgs, args); + const ctestArgs = await this.getCTestArgs(driver, customizedTask, testPreset); + + if (!driver.testPreset) { + log.error(localize('test.preset.not.set', 'Test preset is not set')); + return -3; } const child = driver.executeCommand( diff --git a/src/drivers/cmakeDriver.ts b/src/drivers/cmakeDriver.ts index e3170797c..3a7e0af97 100644 --- a/src/drivers/cmakeDriver.ts +++ b/src/drivers/cmakeDriver.ts @@ -9,7 +9,7 @@ import { CMakeExecutable } from '@cmt/cmake/cmakeExecutable'; import * as codepages from '@cmt/codePageTable'; import { ConfigureTrigger, DiagnosticsConfiguration } from "@cmt/cmakeProject"; import { CompileCommand } from '@cmt/compilationDatabase'; -import { ConfigurationReader, defaultNumJobs } from '@cmt/config'; +import { ConfigurationReader, checkBuildOverridesPresent, checkConfigureOverridesPresent, checkTestOverridesPresent, defaultNumJobs } from '@cmt/config'; import { CMakeBuildConsumer, CompileOutputConsumer } from '@cmt/diagnostics/build'; import { CMakeOutputConsumer } from '@cmt/diagnostics/cmake'; import { RawDiagnosticParser } from '@cmt/diagnostics/util'; @@ -34,7 +34,7 @@ import { getValue } from '@cmt/preset'; import { CacheEntry } from '@cmt/cache'; import { CMakeBuildRunner } from '@cmt/cmakeBuildRunner'; import { DebuggerInformation } from '@cmt/debug/debuggerConfigureDriver'; - +import { onBuildSettingsChange, onTestSettingsChange } from '@cmt/ui/util'; nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })(); const localize: nls.LocalizeFunc = nls.loadMessageBundle(); @@ -244,7 +244,7 @@ export abstract class CMakeDriver implements vscode.Disposable { for (const term of this._compileTerms.values()) { term.dispose(); } - for (const sub of [this._settingsSub, this._argsSub, this._envSub]) { + for (const sub of [this._settingsSub, this._argsSub, this._envSub, this._buildArgsSub, this._buildEnvSub, this._testArgsSub, this._testEnvSub, this._generalEnvSub]) { sub.dispose(); } rollbar.invokeAsync(localize('async.disposing.cmake.driver', 'Async disposing CMake driver'), () => this.asyncDispose()); @@ -276,6 +276,8 @@ export abstract class CMakeDriver implements vscode.Disposable { let envs; if (this.useCMakePresets) { envs = EnvironmentUtils.create(configurePreset ? configurePreset.environment : this._configurePreset?.environment); + envs = EnvironmentUtils.merge([envs, await this.computeExpandedEnvironment(this.config.environment, envs)]); + envs = EnvironmentUtils.merge([envs, await this.computeExpandedEnvironment(this.config.configureEnvironment, envs)]); } else { envs = this._kitEnvironmentVariables; /* NOTE: By mergeEnvironment one by one to enable expanding self containd variable such as PATH properly */ @@ -295,7 +297,10 @@ export abstract class CMakeDriver implements vscode.Disposable { */ async getCMakeBuildCommandEnvironment(in_env?: Environment): Promise { if (this.useCMakePresets) { - return EnvironmentUtils.merge([in_env, this._buildPreset?.environment]); + let envs = EnvironmentUtils.merge([in_env, this._buildPreset?.environment]); + envs = EnvironmentUtils.merge([envs, await this.computeExpandedEnvironment(this.config.environment, envs)]); + envs = EnvironmentUtils.merge([envs, await this.computeExpandedEnvironment(this.config.buildEnvironment, envs)]); + return envs; } else { let envs = EnvironmentUtils.merge([in_env, this._kitEnvironmentVariables]); envs = EnvironmentUtils.merge([envs, await this.computeExpandedEnvironment(this.config.environment, envs)]); @@ -310,7 +315,15 @@ export abstract class CMakeDriver implements vscode.Disposable { */ async getCTestCommandEnvironment(): Promise { if (this.useCMakePresets) { - return EnvironmentUtils.create(this._testPreset?.environment); + let envs = EnvironmentUtils.create(this._testPreset?.environment); + envs = EnvironmentUtils.merge([envs, await this.computeExpandedEnvironment(this.config.environment, envs)]); + envs = EnvironmentUtils.merge([envs, await this.computeExpandedEnvironment(this.config.testEnvironment, envs)]); + + if (this.useCMakePresets && this.testPreset !== null && checkTestOverridesPresent(this.config)) { + log.info(localize('test.with.overrides', 'NOTE: You are testing with preset {0}, but there are some overrides being applied from your VS Code settings.', this.testPreset.displayName ?? this.testPreset.name)); + } + + return envs; } else { let envs = this._kitEnvironmentVariables; envs = EnvironmentUtils.merge([envs, await this.computeExpandedEnvironment(this.config.environment, envs)]); @@ -453,6 +466,11 @@ export abstract class CMakeDriver implements vscode.Disposable { */ async runCompileCommand(cmd: CompileCommand): Promise { const env = await this.getCMakeBuildCommandEnvironment(); + + if (this.useCMakePresets && this._buildPreset && checkBuildOverridesPresent(this.config)) { + log.info(localize('compile.with.overrides', 'NOTE: You are compiling with preset {0}, but there are some overrides being applied from your VS Code settings.', this._buildPreset.displayName ?? this._buildPreset.name)); + } + const key = `${cmd.directory}${JSON.stringify(env)}`; let existing = this._compileTerms.get(key); if (existing && this.config.clearOutputBeforeBuild) { @@ -1282,10 +1300,11 @@ export abstract class CMakeDriver implements vscode.Disposable { true : false; } - public generateConfigArgsFromPreset(configPreset: preset.ConfigurePreset): string[] { + public async generateConfigArgsFromPreset(configPreset: preset.ConfigurePreset): Promise { // Cache flags will construct the command line for cmake. const init_cache_flags = this.generateInitCacheFlags(); - return init_cache_flags.concat(preset.configureArgs(configPreset)); + // Make sure that we expand the config.configureArgs. Right now, preset args are expanded upon switching to the preset. + return init_cache_flags.concat(preset.configureArgs(configPreset), await Promise.all(this.config.configureArgs.map(async (value) => expand.expandString(value, { ...this.expansionOptions, envOverride: await this.getConfigureEnvironment()})))); } public async generateConfigArgsFromSettings(extra_args: string[] = [], withoutCmakeSettings: boolean = false): Promise { @@ -1349,7 +1368,11 @@ export abstract class CMakeDriver implements vscode.Disposable { return { result: -3, resultType: ConfigureResultType.NoConfigurePreset }; } // For now, fields in presets are expanded when the preset is selected - expanded_flags = this.generateConfigArgsFromPreset(configurePreset); + expanded_flags = await this.generateConfigArgsFromPreset(configurePreset); + + if (!showCommandOnly && !shouldUseCachedConfiguration && checkConfigureOverridesPresent(this.config)) { + log.info(localize('configure.with.overrides', 'NOTE: You are configuring with preset {0}, but there are some overrides being applied from your VS Code settings.', configurePreset.displayName ?? configurePreset.name)); + } } else { expanded_flags = await this.generateConfigArgsFromSettings(extra_args, withoutCmakeSettings); } @@ -1683,14 +1706,31 @@ export abstract class CMakeDriver implements vscode.Disposable { return true; } - protected abstract doConfigureSettingsChange(): void; + protected abstract doConfigureSettingsChange(): Promise; - /** + /**g * Subscribe to changes that affect the CMake configuration */ - private readonly _settingsSub = this.config.onChange('configureSettings', () => this.doConfigureSettingsChange()); - private readonly _argsSub = this.config.onChange('configureArgs', () => this.doConfigureSettingsChange()); - private readonly _envSub = this.config.onChange('configureEnvironment', () => this.doConfigureSettingsChange()); + private readonly _settingsSub = this.config.onChange('configureSettings', async () => this.doConfigureSettingsChange()); + private readonly _argsSub = this.config.onChange('configureArgs', async () => this.doConfigureSettingsChange()); + private readonly _envSub = this.config.onChange('configureEnvironment', async () => this.doConfigureSettingsChange()); + private readonly _buildArgsSub = this.config.onChange('buildArgs', async () => { + await onBuildSettingsChange(); + }); + private readonly _buildEnvSub = this.config.onChange('buildEnvironment', async () => { + await onBuildSettingsChange(); + }); + private readonly _testArgsSub = this.config.onChange('ctestArgs', async () => { + await onTestSettingsChange(); + }); + private readonly _testEnvSub = this.config.onChange('testEnvironment', async () => { + await onTestSettingsChange(); + }); + private readonly _generalEnvSub = this.config.onChange('environment', async () => { + await this.doConfigureSettingsChange(); + await onBuildSettingsChange(); + await onTestSettingsChange(); + }); private cmakeBuildRunner: CMakeBuildRunner = new CMakeBuildRunner(); protected configureProcess: proc.Subprocess | null = null; @@ -1714,9 +1754,18 @@ export abstract class CMakeDriver implements vscode.Disposable { } else { buildPreset.__targets = buildPreset.targets; } - const args = preset.buildArgs(buildPreset); + const args = preset.buildArgs(buildPreset, this.config.buildArgs, this.config.buildToolArgs); + const initialEnvironment = EnvironmentUtils.create(buildPreset.environment); + const build_env = await this.getCMakeBuildCommandEnvironment(initialEnvironment); + const expanded_args_promises = args.map(async (value: string) => expand.expandString(value, { ...this.expansionOptions, envOverride: build_env })); + const expanded_args = await Promise.all(expanded_args_promises) as string[]; log.trace(localize('cmake.build.args.are', 'CMake build args are: {0}', JSON.stringify(args))); - return { command: this.cmake.path, args, build_env: EnvironmentUtils.create(buildPreset.environment) }; + + if (checkBuildOverridesPresent(this.config)) { + log.info(localize('build.with.overrides', 'NOTE: You are building with preset {0}, but there are some overrides being applied from your VS Code settings.', buildPreset.displayName ?? buildPreset.name)); + } + + return { command: this.cmake.path, args: expanded_args, build_env}; } async generateBuildCommandFromSettings(targets?: string[]): Promise { diff --git a/src/drivers/cmakeFileApiDriver.ts b/src/drivers/cmakeFileApiDriver.ts index f24f5fcb1..79a752577 100644 --- a/src/drivers/cmakeFileApiDriver.ts +++ b/src/drivers/cmakeFileApiDriver.ts @@ -32,6 +32,7 @@ import { DebuggerInformation } from '@cmt/debug/debuggerConfigureDriver'; import { CMakeOutputConsumer, StateMessage } from '@cmt/diagnostics/cmake'; import { ConfigureTrigger } from '@cmt/cmakeProject'; import { logCMakeDebuggerTelemetry } from '@cmt/debug/cmakeDebuggerTelemetry'; +import { onConfigureSettingsChange } from '@cmt/ui/util'; nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })(); const localize: nls.LocalizeFunc = nls.loadMessageBundle(); @@ -154,8 +155,9 @@ export class CMakeFileApiDriver extends CMakeDriver { }); } - doConfigureSettingsChange() { + async doConfigureSettingsChange(): Promise { this._needsReconfigure = true; + await onConfigureSettingsChange(); } async checkNeedsReconfigure(): Promise { return this._needsReconfigure; diff --git a/src/drivers/cmakeLegacyDriver.ts b/src/drivers/cmakeLegacyDriver.ts index 0cd846220..8a730a853 100644 --- a/src/drivers/cmakeLegacyDriver.ts +++ b/src/drivers/cmakeLegacyDriver.ts @@ -19,6 +19,7 @@ import * as nls from 'vscode-nls'; import { BuildPreset, ConfigurePreset, getValue, TestPreset } from '@cmt/preset'; import { CodeModelContent } from './codeModel'; import { ConfigureTrigger } from '@cmt/cmakeProject'; +import { onConfigureSettingsChange } from '@cmt/ui/util'; nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })(); const localize: nls.LocalizeFunc = nls.loadMessageBundle(); @@ -43,8 +44,9 @@ export class CMakeLegacyDriver extends CMakeDriver { } private _needsReconfigure = true; - doConfigureSettingsChange() { + async doConfigureSettingsChange(): Promise { this._needsReconfigure = true; + await onConfigureSettingsChange(); } async checkNeedsReconfigure(): Promise { return this._needsReconfigure; diff --git a/src/drivers/cmakeServerDriver.ts b/src/drivers/cmakeServerDriver.ts index d47f9759a..12791ece9 100644 --- a/src/drivers/cmakeServerDriver.ts +++ b/src/drivers/cmakeServerDriver.ts @@ -26,6 +26,7 @@ import * as nls from 'vscode-nls'; import { BuildPreset, ConfigurePreset, TestPreset } from '@cmt/preset'; import { CodeModelConfiguration, CodeModelContent, CodeModelFileGroup, CodeModelProject, CodeModelTarget } from '@cmt/drivers/codeModel'; import { ConfigureTrigger } from '@cmt/cmakeProject'; +import { onConfigureSettingsChange } from '@cmt/ui/util'; nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })(); const localize: nls.LocalizeFunc = nls.loadMessageBundle(); @@ -307,8 +308,9 @@ export class CMakeServerDriver extends CMakeDriver { * Track if the user changes the settings of the configure via settings.json */ private _hadConfigurationChanged = true; - protected doConfigureSettingsChange() { + protected async doConfigureSettingsChange(): Promise { this._hadConfigurationChanged = true; + await onConfigureSettingsChange(); } async checkNeedsReconfigure(): Promise { diff --git a/src/extension.ts b/src/extension.ts index 93b0f9966..0b243dbc0 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1714,6 +1714,10 @@ export class ExtensionManager implements vscode.Disposable { return presetSelected; } + viewConfigureSettings(): void { + void vscode.commands.executeCommand('workbench.action.openSettings', '@id:cmake.configureArgs, @id:cmake.configureEnvironment, @id:cmake.environment'); + } + /** * Show UI to allow the user to select an active build preset */ @@ -1739,6 +1743,10 @@ export class ExtensionManager implements vscode.Disposable { return presetSelected; } + viewBuildSettings(): void { + void vscode.commands.executeCommand('workbench.action.openSettings', '@id:cmake.buildArgs, @id:cmake.buildToolArgs @id:cmake.buildEnvironment @id:cmake.environment'); + } + /** * Show UI to allow the user to select an active test preset */ @@ -1764,6 +1772,10 @@ export class ExtensionManager implements vscode.Disposable { return presetSelected; } + viewTestSettings(): void { + void vscode.commands.executeCommand('workbench.action.openSettings', '@id:cmake.ctestArgs, @id:cmake.testEnvironment, @id:cmake.environment'); + } + public api: CMakeToolsApiImpl; get onBuildTargetChanged() { @@ -1843,8 +1855,11 @@ async function setup(context: vscode.ExtensionContext, progress?: ProgressHandle 'addBuildPreset', 'addTestPreset', 'selectConfigurePreset', + 'viewConfigureSettings', 'selectBuildPreset', + 'viewBuildSettings', 'selectTestPreset', + 'viewTestSettings', 'selectActiveFolder', 'editKits', 'scanForKits', diff --git a/src/preset.ts b/src/preset.ts index cf96fa68f..d6bfce46f 100644 --- a/src/preset.ts +++ b/src/preset.ts @@ -1558,7 +1558,7 @@ export function configureArgs(preset: ConfigurePreset): string[] { return result; } -export function buildArgs(preset: BuildPreset): string[] { +export function buildArgs(preset: BuildPreset, tempOverrideArgs?: string[], tempOverrideBuildToolArgs?: string[]): string[] { const result: string[] = []; preset.__binaryDir && result.push('--build', preset.__binaryDir); @@ -1573,7 +1573,9 @@ export function buildArgs(preset: BuildPreset): string[] { result.push('--target', ...preset.__targets); } + tempOverrideArgs && result.push(...tempOverrideArgs); preset.nativeToolOptions && result.push('--', ...preset.nativeToolOptions); + tempOverrideBuildToolArgs && result.push(...tempOverrideBuildToolArgs); return result; } diff --git a/src/projectStatus.ts b/src/projectStatus.ts index 8b823379f..3dac96599 100644 --- a/src/projectStatus.ts +++ b/src/projectStatus.ts @@ -3,7 +3,7 @@ import * as nls from 'vscode-nls'; import CMakeProject from './cmakeProject'; import * as preset from './preset'; import { runCommand } from './util'; -import { OptionConfig } from './config'; +import { OptionConfig, checkBuildOverridesPresent, checkConfigureOverridesPresent, checkTestOverridesPresent } from './config'; nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })(); const localize: nls.LocalizeFunc = nls.loadMessageBundle(); @@ -12,7 +12,7 @@ const noConfigPresetSelected = localize('no.configure.preset.selected', '[No Con const noBuildPresetSelected = localize('no.build.preset.selected', '[No Build Preset Selected]'); const noTestPresetSelected = localize('no.test.preset.selected', '[No Test Preset Selected]'); -let treeDataProvider: TreeDataProvider; +export let treeDataProvider: TreeDataProvider; export class ProjectStatus { @@ -43,6 +43,9 @@ export class ProjectStatus { await runCommand('selectConfigurePreset'); await this.refresh(node); }), + vscode.commands.registerCommand('cmake.projectStatus.viewConfigureSettings', async (_node: Node) => { + await runCommand('viewConfigureSettings'); + }), vscode.commands.registerCommand('cmake.projectStatus.configure', async (_node: Node) => { void runCommand('configure'); }), @@ -61,6 +64,9 @@ export class ProjectStatus { await runCommand('selectBuildPreset'); await this.refresh(node); }), + vscode.commands.registerCommand('cmake.projectStatus.viewBuildSettings', async (_node: Node) => { + await runCommand('viewBuildSettings'); + }), vscode.commands.registerCommand('cmake.projectStatus.ctest', async (_node: Node) => { void runCommand('ctest'); }), @@ -71,6 +77,9 @@ export class ProjectStatus { await runCommand('selectTestPreset'); await this.refresh(node); }), + vscode.commands.registerCommand('cmake.projectStatus.viewTestSettings', async (_node: Node) => { + await runCommand('viewTestSettings'); + }), vscode.commands.registerCommand('cmake.projectStatus.debugTarget', async (_node: Node) => { await runCommand('debugTarget'); }), @@ -151,6 +160,9 @@ class TreeDataProvider implements vscode.TreeDataProvider, vscode.Disposab private isDebugButtonHidden: boolean = false; private isLaunchButtonHidden: boolean = false; private isBusy: boolean = false; + private configNode: ConfigNode | undefined; + private buildNode: BuildNode | undefined; + private testNode: TestNode | undefined; get onDidChangeTreeData(): vscode.Event { return this._onDidChangeTreeData.event; @@ -180,6 +192,25 @@ class TreeDataProvider implements vscode.TreeDataProvider, vscode.Disposab await this.refresh(); } + public async refreshNode(node: ConfigNode | BuildNode | TestNode | undefined): Promise { + if (node) { + await node.refresh(); + this._onDidChangeTreeData.fire(node); + } + } + + public async refreshConfigNode(): Promise { + await this.refreshNode(this.configNode); + } + + public async refreshBuildNode(): Promise { + await this.refreshNode(this.buildNode); + } + + public async refreshTestNode(): Promise { + await this.refreshNode(this.testNode); + } + public async refresh(node?: Node): Promise { if (node) { await node.refresh(); @@ -218,6 +249,7 @@ class TreeDataProvider implements vscode.TreeDataProvider, vscode.Disposab } if (!this.isConfigButtonHidden) { const configNode = new ConfigNode(); + this.configNode = configNode; await configNode.initialize(); if (this.isBusy) { configNode.convertToStopCommand(); @@ -226,6 +258,7 @@ class TreeDataProvider implements vscode.TreeDataProvider, vscode.Disposab } if (!this.isBuildButtonHidden) { const buildNode = new BuildNode(); + this.buildNode = buildNode; await buildNode.initialize(); if (this.isBusy) { buildNode.convertToStopCommand(); @@ -234,6 +267,7 @@ class TreeDataProvider implements vscode.TreeDataProvider, vscode.Disposab } if (!this.isTestButtonHidden) { const testNode = new TestNode(); + this.testNode = testNode; await testNode.initialize(); if (this.isBusy) { testNode.convertToStopCommand(); @@ -420,6 +454,10 @@ class ConfigNode extends Node { return this.initialize(); } + async refresh(): Promise { + await this.configPreset?.refresh(); + } + } class BuildNode extends Node { @@ -472,6 +510,10 @@ class BuildNode extends Node { return this.initialize(); } + async refresh(): Promise { + await this.buildPreset?.refresh(); + } + } class TestNode extends Node { @@ -514,6 +556,10 @@ class TestNode extends Node { } } + async refresh(): Promise { + await this.testPreset?.refresh(); + } + } class DebugNode extends Node { @@ -621,6 +667,7 @@ class ConfigPreset extends Node { this.tooltip = 'Change Configure Preset'; this.contextValue = 'configPreset'; this.collapsibleState = vscode.TreeItemCollapsibleState.None; + await this.updateDescription(); } async refresh() { @@ -628,6 +675,21 @@ class ConfigPreset extends Node { return; } this.label = (treeDataProvider.cmakeProject.configurePreset?.displayName ?? treeDataProvider.cmakeProject.configurePreset?.name) || noConfigPresetSelected; + await this.updateDescription(); + } + + private async updateDescription(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + const config = (await treeDataProvider.cmakeProject.getCMakeDriverInstance())?.config; + if (config && checkConfigureOverridesPresent(config)) { + this.description = "Override settings applied"; + this.contextValue = 'configPreset - overrides present'; + } else { + this.description = ""; + this.contextValue = 'configPreset'; + } } } @@ -644,6 +706,7 @@ class BuildPreset extends Node { this.tooltip = 'Change Build Preset'; this.contextValue = 'buildPreset'; this.collapsibleState = vscode.TreeItemCollapsibleState.None; + await this.updateDescription(); } async refresh() { @@ -651,6 +714,22 @@ class BuildPreset extends Node { return; } this.label = (treeDataProvider.cmakeProject.buildPreset?.displayName ?? treeDataProvider.cmakeProject.buildPreset?.name) || noBuildPresetSelected; + await this.updateDescription(); + } + + private async updateDescription(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + + const config = (await treeDataProvider.cmakeProject.getCMakeDriverInstance())?.config; + if (config && checkBuildOverridesPresent(config)) { + this.description = "Override settings applied"; + this.contextValue = 'buildPreset - overrides present'; + } else { + this.description = ""; + this.contextValue = 'buildPreset'; + } } } @@ -667,6 +746,7 @@ class TestPreset extends Node { this.tooltip = 'Change Test Preset'; this.contextValue = 'testPreset'; this.collapsibleState = vscode.TreeItemCollapsibleState.None; + await this.updateDescription(); } async refresh() { @@ -674,6 +754,22 @@ class TestPreset extends Node { return; } this.label = (treeDataProvider.cmakeProject.testPreset?.displayName ?? treeDataProvider.cmakeProject.testPreset?.name) || noTestPresetSelected; + await this.updateDescription(); + } + + private async updateDescription(): Promise { + if (!treeDataProvider.cmakeProject) { + return; + } + + const config = (await treeDataProvider.cmakeProject.getCMakeDriverInstance())?.config; + if (config && checkTestOverridesPresent(config)) { + this.description = "Override settings applied"; + this.contextValue = 'testPreset - overrides present'; + } else { + this.description = ""; + this.contextValue = 'testPreset'; + } } } diff --git a/src/status.ts b/src/status.ts index 7e09db26c..83509ce5f 100644 --- a/src/status.ts +++ b/src/status.ts @@ -1,4 +1,4 @@ -import { ConfigurationReader, StatusBarOptionVisibility, StatusBarTextOptionVisibility, StatusBarStaticOptionVisibility, StatusBarIconOptionVisibility } from '@cmt/config'; +import { ConfigurationReader, StatusBarOptionVisibility, StatusBarTextOptionVisibility, StatusBarStaticOptionVisibility, StatusBarIconOptionVisibility, checkConfigureOverridesPresent, checkBuildOverridesPresent, checkTestOverridesPresent } from '@cmt/config'; import { SpecialKits } from '@cmt/kit'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; @@ -545,7 +545,7 @@ export class ConfigurePresetSelection extends Button { if (text.length === 0) { return ConfigurePresetSelection._noPresetSelected; } - return this.bracketText; + return checkConfigureOverridesPresent(this.config) ? `*${this.bracketText}` : this.bracketText; } protected getTextShort(): string { @@ -588,7 +588,7 @@ export class BuildPresetSelection extends Button { if (text.length === 0) { return BuildPresetSelection._noPresetSelected; } - return this.bracketText; + return checkBuildOverridesPresent(this.config) ? `*${this.bracketText}` : this.bracketText; } protected getTextShort(): string { @@ -631,7 +631,7 @@ export class TestPresetSelection extends Button { if (text.length === 0) { return TestPresetSelection._noPresetSelected; } - return this.bracketText; + return checkTestOverridesPresent(this.config) ? `*${this.bracketText}` : this.bracketText; } protected getTextShort(): string { @@ -741,12 +741,21 @@ export class StatusBar implements vscode.Disposable { setConfigurePresetName(v: string): void { this._configurePresetButton.text = v; } + updateConfigurePresetButton(): void { + this._configurePresetButton.update(); + } setBuildPresetName(v: string): void { this._buildPresetButton.text = v; } + updateBuildPresetButton(): void { + this._buildPresetButton.update(); + } setTestPresetName(v: string): void { this._testPresetButton.text = v; this.setCTestEnabled(true); } + updateTestPresetButton(): void { + this._testPresetButton.update(); + } hideLaunchButton(shouldHide: boolean = true): void { this._launchButton.hidden = shouldHide; diff --git a/src/ui/util.ts b/src/ui/util.ts new file mode 100644 index 000000000..89ccce033 --- /dev/null +++ b/src/ui/util.ts @@ -0,0 +1,17 @@ +import { getStatusBar } from "@cmt/extension"; +import { treeDataProvider } from "@cmt/projectStatus"; + +export async function onConfigureSettingsChange(): Promise { + await treeDataProvider.refreshConfigNode(); + getStatusBar()?.updateConfigurePresetButton(); +} + +export async function onBuildSettingsChange(): Promise { + await treeDataProvider.refreshBuildNode(); + getStatusBar()?.updateBuildPresetButton(); +} + +export async function onTestSettingsChange(): Promise { + await treeDataProvider.refreshTestNode(); + getStatusBar()?.updateTestPresetButton(); +} diff --git a/tsconfig.json b/tsconfig.json index 2b847f4b8..153476371 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,6 @@ "rootDir": ".", "strict": true, "experimentalDecorators": true, - "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitThis": true, "skipLibCheck": true