diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a7d1628..01d5a5a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Breaking changes: - The `Run CTest` button in the status bar now only reveals the test explorer, and test results are removed from its text. [PR #3032](https://github.com/microsoft/vscode-cmake-tools/pull/3032) - All test starting method, such as command `CMake: Run Tests` and test task, now runs through the test explorer. Tests can't run in parallel for now. [PR #3032](https://github.com/microsoft/vscode-cmake-tools/pull/3032) - Catch test framework support is removed. [PR #3043](https://github.com/microsoft/vscode-cmake-tools/pull/3043) +- Rename `cmake.mingwSearchDirs` to `cmake.additionalCompilerSearchDirs`, make it more general and fix quirks with it. [PR #3056](https://github.com/microsoft/vscode-cmake-tools/pull/3056) [@philippewarren](https://github.com/philippewarren) Improvements: - Automatically configure CMake project directories when the kit or the configuration preset is changed. [PR #2973](https://github.com/microsoft/vscode-cmake-tools/pull/2973) [@maxmitti](https://github.com/maxmitti) @@ -17,6 +18,7 @@ Improvements: - When starting debugging, also build the selected build target. [PR #2987](https://github.com/microsoft/vscode-cmake-tools/pull/2987) [@Maddimax](https://github.com/Maddimax) - Add support for CMake Presets V5. [#2979](https://github.com/microsoft/vscode-cmake-tools/issues/2979) - Print the build time in the output window. [#3008](https://github.com/microsoft/vscode-cmake-tools/issues/3008) +- Allow using all of MSYS2 MinGW installations, which are also now found by default while scanning for kits if MSYS2 is installed at the default location (`C:\msys64\{wingw64|mingw32|clang64|clang32|clangarm64|ucrt64}\bin`). [PR #3056](https://github.com/microsoft/vscode-cmake-tools/pull/3056) [@philippewarren](https://github.com/philippewarren) Bug Fixes: - Check if "CMakeLists.txt" exists after renaming. [#2986](https://github.com/microsoft/vscode-cmake-tools/issues/2986) @@ -25,6 +27,7 @@ Bug Fixes: - CMake tools not fully loaded when opening multi-project folders. [#3000](https://github.com/microsoft/vscode-cmake-tools/issues/3000) - Expand variables in task's targets while searching matching taks. [#2970](https://github.com/microsoft/vscode-cmake-tools/issues/2970) [@piomis](https://github.com/piomis) - Fix typo in `cmake.skipConfigureWhenCachePresent`. [#3040](https://github.com/microsoft/vscode-cmake-tools/issues/3040) [@Mlekow](https://github.com/Mlekow) +- Fix MinGW detection when not in PATH using `cmake.mingwSearchDirs` (now named `cmake.additionalCompilerSearchDirs`). [PR #3056](https://github.com/microsoft/vscode-cmake-tools/pull/3056) [@philippewarren](https://github.com/philippewarren) ## 1.13.45 Bug Fixes: diff --git a/docs/cmake-presets.md b/docs/cmake-presets.md index 9df6c03c..abdbc5e8 100644 --- a/docs/cmake-presets.md +++ b/docs/cmake-presets.md @@ -89,7 +89,7 @@ Use forward slashes (`/`) for paths in `CMakePresets.json` and `CMakeUserPresets ### Add new Configure Presets -To add a new Configure Preset to `CMakePresets.json`, run the **CMake: Add Configure Preset** command. This command lists several Configure Preset templates, along with a **[Scan for Compilers]** option in the command palette. **[Scan for Compilers]** returns all of the GCC and Clang compilers on your `PATH`, all compilers found in `cmake.mingwSearchDir` and `cmake.emscriptenSearchDirs`, and the latest instances of `cl.exe` installed with Visual Studio. +To add a new Configure Preset to `CMakePresets.json`, run the **CMake: Add Configure Preset** command. This command lists several Configure Preset templates, along with a **[Scan for Compilers]** option in the command palette. **[Scan for Compilers]** returns all of the GCC and Clang compilers on your `PATH`, all compilers found in `cmake.additionalCompilerSearchDirs` and `cmake.emscriptenSearchDirs`, and the latest instances of `cl.exe` installed with Visual Studio. ![Screenshot of the list of configure presets.](images/add-configure-preset-vscode.png) diff --git a/docs/cmake-settings.md b/docs/cmake-settings.md index 1732f9b4..d39c1bc7 100644 --- a/docs/cmake-settings.md +++ b/docs/cmake-settings.md @@ -28,7 +28,7 @@ Options that support substitution, in the table below, allow variable references | `cmake.generator` | Set to a string to override CMake Tools preferred generator logic. If set, CMake will unconditionally use it as the `-G` CMake generator command line argument. ||no| | `cmake.installPrefix` | If specified, sets a value for `CMAKE_INSTALL_PREFIX` when running CMake configure. If not set, no value will be passed.
If `CMAKE_INSTALL_PREFIX` is set via `cmake.configureArgs` or `cmake.configureSettings`, `cmake.installPrefix` will be ignored.| `null` (no value specified) | yes | | `cmake.loggingLevel` | A string setting that specifies how much output CMake Tools produces in its output channel. Set to one of `"trace"`, `"debug"`, `"info"`, `"note"`, `"warning"`, `"error"`, or `"fatal"`. `"trace"` is the most verbose.

Regardless of the logging level, CMake Tools writes all levels of logging to the CMake Tools log file. This file is useful if you need to [troubleshoot CMake Tools](troubleshoot.md) | `"info"` | no | -| `cmake.mingwSearchDirs`| List of paths to search for a MinGW installation. This means that GCC does not need to be on your `$PATH` for it to be found via kit scanning. For example: `["C:\\MinGW\\bin"]` (Search in C:\MinGW\bin for a MinGW installation) | [] | no | +| `cmake.additionalCompilerSearchDirs`| List of paths to search for additional compilers, like a MinGW installation. This means that GCC does not need to be on your `$PATH` for it to be found via kit scanning. For example: `["C:\\MinGW\\bin"]` (Search in C:\MinGW\bin for a MinGW installation) | [] | no | | `cmake.parallelJobs` | Specify the number of jobs run in parallel during the build. Using the value `1` will disable build parallelism. | | no | | `cmake.preferredGenerators` | A list of strings of generator names to try, in order, when configuring a CMake project for the first time. | | no | | `cmake.saveBeforeBuild` | If `true` (the default), saves open text documents when build or configure is invoked before running CMake. | `true` | no | diff --git a/package.json b/package.json index 4cd85e18..65e406c5 100644 --- a/package.json +++ b/package.json @@ -1105,34 +1105,42 @@ "default": {}, "additionalProperties": { "oneOf": [ - { - "type": "string" - }, - { - "type": "boolean" - }, - { - "type": "number" - }, - { - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ "", "BOOL", "STRING", "FILEPATH", "PATH"] - }, - "value": { - } - }, - "additionalProperties": false, - "required": ["type", "value"] + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "array", + "items": { + "type": "string" } + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "", + "BOOL", + "STRING", + "FILEPATH", + "PATH" + ] + }, + "value": {} + }, + "additionalProperties": false, + "required": [ + "type", + "value" + ] + } ] }, "description": "%cmake-tools.configuration.cmake.configureSettings.description%", @@ -1523,6 +1531,17 @@ }, "default": [], "description": "%cmake-tools.configuration.cmake.mingwSearchDirs.description%", + "scope": "window", + "markdownDeprecationMessage": "**Deprecated**: Please use `#cmake.additionalCompilerSearchDirs#` instead." + }, + "cmake.additionalCompilerSearchDirs": { + "type": "array", + "items": { + "type": "string", + "description": "%cmake-tools.configuration.cmake.searchDirs.items.description%" + }, + "default": [], + "description": "%cmake-tools.configuration.cmake.additionalCompilerSearchDirs.description%", "scope": "window" }, "cmake.emscriptenSearchDirs": { @@ -2190,4 +2209,4 @@ "extensionPack": [ "twxs.cmake" ] -} \ No newline at end of file +} diff --git a/package.nls.json b/package.nls.json index e5b28df2..09666159 100644 --- a/package.nls.json +++ b/package.nls.json @@ -82,13 +82,28 @@ "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.description": "The number of parallel test jobs. Use zero to use the value of cmake.parallelJobs.", "cmake-tools.configuration.cmake.parseBuildDiagnostics.description": "Parse compiler output for warnings and errors.", - "cmake-tools.configuration.cmake.enabledOutputParsers.description": { "message": "Output parsers to use. Supported parsers `cmake`, `gcc`, `gnuld` for GNULD-style linker output, `msvc` for Microsoft Visual C++, `ghs` for the Green Hills compiler with --no_wrap_diagnostics --brief_diagnostics, and `diab` for the Wind River Diab compiler.", "comment": [ "Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered." ] }, + "cmake-tools.configuration.cmake.enabledOutputParsers.description": { + "message": "Output parsers to use. Supported parsers `cmake`, `gcc`, `gnuld` for GNULD-style linker output, `msvc` for Microsoft Visual C++, `ghs` for the Green Hills compiler with --no_wrap_diagnostics --brief_diagnostics, and `diab` for the Wind River Diab compiler.", + "comment": [ + "Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered." + ] + }, "cmake-tools.configuration.cmake.debugConfig.description": "The debug configuration to use when debugging a target.", "cmake-tools.configuration.cmake.debugConfig.symbolSearchPath.description": "Visual Studio debugger symbol search paths.", "cmake-tools.configuration.cmake.debugConfig.additionalSOLibSearchPath.description": "Paths for GDB or LLDB to search for .so files.", "cmake-tools.configuration.cmake.debugConfig.externalConsole.description": "Launch an external console for the program.", - "cmake-tools.configuration.cmake.debugConfig.console.description": { "message": "Where to launch the debug target. Defaults to `internalConsole` if not defined.", "comment": [ "Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered." ] }, - "cmake-tools.configuration.cmake.debugConfig.console.internalConsole.description": { "message": "Output to the VS Code Debug Console. This doesn't support reading console input (ex:`std::cin` or `scanf`).", "comment": [ "Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered." ] }, + "cmake-tools.configuration.cmake.debugConfig.console.description": { + "message": "Where to launch the debug target. Defaults to `internalConsole` if not defined.", + "comment": [ + "Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered." + ] + }, + "cmake-tools.configuration.cmake.debugConfig.console.internalConsole.description": { + "message": "Output to the VS Code Debug Console. This doesn't support reading console input (ex:`std::cin` or `scanf`).", + "comment": [ + "Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered." + ] + }, "cmake-tools.configuration.cmake.debugConfig.console.integratedTerminal.description": "VS Code's integrated terminal.", "cmake-tools.configuration.cmake.debugConfig.console.externalTerminal.description": "Console applications will be launched in an external terminal window. The window will be reused in relaunch scenarios and will not automatically disappear when the application exits.", "cmake-tools.configuration.cmake.debugConfig.console.newExternalWindow.description": "Console applications will be launched in their own external console window which will end when the application stops. Non-console applications will run without a terminal, and stdout/stderr will be ignored.", @@ -116,6 +131,7 @@ "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.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.", "cmake-tools.configuration.cmake.emscriptenSearchDirs.description": "Directories where Emscripten may be installed.", "cmake-tools.configuration.cmake.mergedCompileCommands.description": "Recursively collect and merge all compile_commands.json found in the cmake.buildDirectory.", @@ -150,7 +166,12 @@ "cmake-tools.configuration.cmake.useCMakePresets.description": "Use CMakePresets.json to configure drive CMake configure, build, and test. When using CMakePresets.json, kits, variants, and some settings in settings.json will be ignored.", "cmake-tools.configuration.cmake.allowCommentsInPresetsFile.description": "Allow the use of JSON extensions such as comments in CMakePresets.json. Please note that your CMakePresets.json file may be considered invalid by other IDEs or on the command line if you use non-standard JSON.", "cmake-tools.configuration.cmake.allowUnsupportedPresetsVersions.description": "Enables the use of presets files that are using features from the versions that CMake Tools extension doesn't currently support. Unknown properties and macros will be ignored.", - "cmake-tools.configuration.cmake.ignoreCMakeListsMissing.description": { "message": "If `true`, the extension will not ask the user to select a CMakeLists.txt file for configuration when one is found in the workspace but not in the root folder.", "comment": [ "Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered." ] }, + "cmake-tools.configuration.cmake.ignoreCMakeListsMissing.description": { + "message": "If `true`, the extension will not ask the user to select a CMakeLists.txt file for configuration when one is found in the workspace but not in the root folder.", + "comment": [ + "Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered." + ] + }, "cmake-tools.configuration.cmake.launchBehavior.description": "Controls what happens with the launch terminal when you launch a target.", "cmake-tools.configuration.cmake.automaticReconfigure.description": "Automatically configure CMake project directories when the kit or the configuration preset is changed.", "cmake-tools.taskDefinitions.properties.label.description": "The name of the task", @@ -159,10 +180,20 @@ "cmake-tools.taskDefinitions.properties.preset.description": "CMake preset name. This is a configuration, build or test preset, based on the CMake command", "cmake-tools.taskDefinitions.properties.options.description": "Additional command options", "cmake-tools.taskDefinitions.properties.options.cwd.description": "The current working directory of the executed program or script. If omitted Code's current workspace root is used.", - "cmake-tools.taskDefinitions.properties.options.environment.markdownDescription": { "message": "Environment variables in the format of \"name\": \"value\".", "comment": [ "\"name\": \"value\" should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation should not be altered." ] }, + "cmake-tools.taskDefinitions.properties.options.environment.markdownDescription": { + "message": "Environment variables in the format of \"name\": \"value\".", + "comment": [ + "\"name\": \"value\" should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation should not be altered." + ] + }, "cmake-tools.taskDefinitions.properties.details.description": "Additional details of the task", "cmake-tools.configuration.cmake.launchBehavior.reuseTerminal.markdownDescriptions": "The launch terminal instance is reused and the target will launch as soon as the terminal is idle.", - "cmake-tools.configuration.cmake.launchBehavior.breakAndReuseTerminal.markdownDescriptions": { "message": "The launch terminal instance is reused and a `break` command is sent to terminate any active foreground process before launching the target.", "comment": [ "Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered." ] }, + "cmake-tools.configuration.cmake.launchBehavior.breakAndReuseTerminal.markdownDescriptions": { + "message": "The launch terminal instance is reused and a `break` command is sent to terminate any active foreground process before launching the target.", + "comment": [ + "Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered." + ] + }, "cmake-tools.configuration.cmake.launchBehavior.newTerminal.markdownDescriptions": "A new terminal instance is created and the target is launched in it. Existing terminals are not automatically cleaned up.", - "cmake-tools.configuration.cmake.loadCompileCommands.description" : "Controls whether the extension reads compile_commands.json to enable single file compilation." + "cmake-tools.configuration.cmake.loadCompileCommands.description": "Controls whether the extension reads compile_commands.json to enable single file compilation." } diff --git a/src/config.ts b/src/config.ts index c06bb1fb..4845a170 100644 --- a/src/config.ts +++ b/src/config.ts @@ -119,7 +119,8 @@ export interface ExtensionConfigurationSettings { configureEnvironment: Environment; buildEnvironment: Environment; testEnvironment: Environment; - mingwSearchDirs: string[]; + mingwSearchDirs: string[]; // Deprecated in 1.14, replaced by additionalCompilerSearchDirs, but kept for backwards compatibility + additionalCompilerSearchDirs: string[]; emscriptenSearchDirs: string[]; mergedCompileCommands: string | null; copyCompileCommands: string | null; @@ -396,8 +397,13 @@ export class ConfigurationReader implements vscode.Disposable { return ctestJobs; } - get mingwSearchDirs(): string[] { - return this.configData.mingwSearchDirs; + get additionalCompilerSearchDirs(): string[] { + // mingwSearchDirs is deprecated, but we still use it if additionalCompilerSearchDirs is not set for backwards compatibility + if (this.configData.additionalCompilerSearchDirs.length === 0 && this.configData.mingwSearchDirs.length > 0) { + log.warning(localize('please.upgrade.configuration', 'The setting {0} is replaced by {1}. Please upgrade your configuration.', '"mingwSearchDirs"', '"additionalCompilerSearchDirs"')); + return this.configData.mingwSearchDirs; + } + return this.configData.additionalCompilerSearchDirs; } get additionalKits(): string[] { return this.configData.additionalKits; @@ -483,7 +489,8 @@ export class ConfigurationReader implements vscode.Disposable { configureEnvironment: new vscode.EventEmitter(), buildEnvironment: new vscode.EventEmitter(), testEnvironment: new vscode.EventEmitter(), - mingwSearchDirs: new vscode.EventEmitter(), + mingwSearchDirs: new vscode.EventEmitter(), // Deprecated in 1.14, replaced by additionalCompilerSearchDirs, but kept for backwards compatibility + additionalCompilerSearchDirs: new vscode.EventEmitter(), emscriptenSearchDirs: new vscode.EventEmitter(), mergedCompileCommands: new vscode.EventEmitter(), copyCompileCommands: new vscode.EventEmitter(), diff --git a/src/extension.ts b/src/extension.ts index 86621d87..3471ff76 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -165,6 +165,13 @@ export class ExtensionManager implements vscode.Disposable { } this.statusBar.setAutoSelectActiveProject(v); }); + this.workspaceConfig.onChange('additionalCompilerSearchDirs', async _ => { + KitsController.additionalCompilerSearchDirs = await this.getAdditionalCompilerDirs(); + }); + this.workspaceConfig.onChange('mingwSearchDirs', async _ => { // Deprecated in 1.14, replaced by additionalCompilerSearchDirs, but kept for backwards compatibility + KitsController.additionalCompilerSearchDirs = await this.getAdditionalCompilerDirs(); + }); + KitsController.additionalCompilerSearchDirs = await this.getAdditionalCompilerDirs(); let isMultiProject = false; if (vscode.workspace.workspaceFolders) { @@ -781,7 +788,7 @@ export class ExtensionManager implements vscode.Disposable { } async scanForKits() { - KitsController.minGWSearchDirs = await this.getMinGWDirs(); + KitsController.additionalCompilerSearchDirs = await this.getAdditionalCompilerDirs(); if (!vscode.workspace.workspaceFolders || vscode.workspace.workspaceFolders.length < 1) { return; } @@ -803,9 +810,9 @@ export class ExtensionManager implements vscode.Disposable { } /** - * Get the current MinGW search directories + * Get the current additional compiler search directories, like MinGW directories */ - private async getMinGWDirs(): Promise { + private async getAdditionalCompilerDirs(): Promise { const optsVars: KitContextVars = { userHome: paths.userHome, @@ -831,7 +838,7 @@ export class ExtensionManager implements vscode.Disposable { sourceDir: "" }; const result = new Set(); - for (const dir of this.workspaceConfig.mingwSearchDirs) { + for (const dir of this.workspaceConfig.additionalCompilerSearchDirs) { const expandedDir: string = util.lightNormalizePath(await expandString(dir, { vars: optsVars })); result.add(expandedDir); } diff --git a/src/kit.ts b/src/kit.ts index 13c20284..2494fd8a 100644 --- a/src/kit.ts +++ b/src/kit.ts @@ -207,7 +207,12 @@ export async function getCompilerVersion(vendor: CompilerVendorEnum, binPath: st if (install_dir_mat && vendor === 'Clang') { installedDir = install_dir_mat[1]; } - const detectedName = `${vendor} ${version} ${target.triple}`; + let detectedName = `${vendor} ${version} ${target.triple}`; + if (isMsys(binPath)) { + // Add the MSYS environment to the name, so that we can distinguish between different MSYS environments + const msysEnvDirName = path.dirname(path.dirname(binPath)); + detectedName += ` (${path.basename(msysEnvDirName)})`; + } log.debug(localize('detected.compiler', 'Detected {0} compiler: {1}', vendor, binPath)); return { vendor, @@ -278,6 +283,52 @@ export async function getKitDetect(kit: Kit): Promise { } } +function isMsys(bin: string): boolean { + const isWin32 = process.platform === 'win32'; + const isMsys = isWin32 && bin.toLowerCase().includes('msys'); + return isMsys; +} + +function isMingw(bin: string): boolean { + const isWin32 = process.platform === 'win32'; + const isMingw = isWin32 && ( + bin.toLowerCase().includes('mingw') || + isMsys(bin) + ); + return isMingw; +} + +async function asMingwKit(bin: string, kit: Kit): Promise { + const binParentPath = path.dirname(bin); + const mingwMakePath = path.join(binParentPath, 'mingw32-make.exe'); + const mingwMakeExists = await fs.exists(mingwMakePath); + + // During a scan, binParentPath must be a directory already in the PATH. + // Therefore, we will assume that MinGW will remain in the user's PATH + // and do not need to record the current state of PATH (leave it to the + // user to rescan later or specify an explicit path to MinGW if this + // changes). Additionally, caching the current state of PATH can cause + // complications on later invocation when using the kit environment + // because its PATH will take precedence. If a user makes changes to + // their PATH later without rescanning for kits, then the kit's cached + // PATH will clobber the actual current PATH. We will, however, record + // the MinGW path in case we want to use it later. + const ENV_PATH = `${binParentPath}`; + kit.environmentVariables = { CMT_MINGW_PATH: ENV_PATH }; + + if (mingwMakeExists) { + // Check for working mingw32-make + const execMake = await proc.execute(mingwMakePath, ['-v'], null, { environment: { PATH: ENV_PATH }, timeout: 30000 }).result; + if (execMake.retc !== 0) { + log.debug(localize('bad.mingw32-make.binary', 'Bad mingw32-make binary ({0} returns non-zero): {1}', "\"-v\"", bin)); + } else { + kit.preferredGenerator = { name: 'MinGW Makefiles' }; + } + } + + return kit; +} + /** * Convert a binary (by path) to a CompilerKit. This checks if the named binary * is a GCC or Clang compiler and gets its version. If it is not a compiler, @@ -294,6 +345,7 @@ export async function kitIfCompiler(bin: string, pr?: ProgressReporter): Promise const gcc_res = gcc_regex.exec(fname); const clang_res = clang_regex.exec(fname); const clang_cl_res = clang_cl_regex.exec(fname); + if (gcc_res) { const version = await getCompilerVersion('GCC', bin, pr); if (version === null) { @@ -336,45 +388,11 @@ export async function kitIfCompiler(bin: string, pr?: ProgressReporter): Promise compilers: gccCompilers }; - const isWin32 = process.platform === 'win32'; - if (isWin32 && bin.toLowerCase().includes('mingw')) { - const binParentPath = path.dirname(bin); - const mingwMakePath = path.join(binParentPath, 'mingw32-make.exe'); - if (await fs.exists(mingwMakePath)) { - // During a scan, binParentPath must be a directory already in the PATH. - // Therefore, we will assume that MinGW will remain in the user's PATH - // and do not need to record the current state of PATH (leave it to the - // user to rescan later or specify an explicit path to MinGW if this - // changes). Additionally, caching the current state of PATH can cause - // complications on later invocation when using the kit environment - // because its PATH will take precedence. If a user makes changes to - // their PATH later without rescanning for kits, then the kit's cached - // PATH will clobber the actual current PATH. We will, however, record - // the MinGW path in case we want to use it later. - const ENV_PATH = `${binParentPath}`; - // Check for working mingw32-make - const execMake = await proc.execute(mingwMakePath, ['-v'], null, { environment: { PATH: ENV_PATH }, timeout: 30000 }).result; - if (execMake.retc !== 0) { - log.debug(localize('bad.mingw32-make.binary', 'Bad mingw32-make binary ({0} returns non-zero): {1}', "\"-v\"", bin)); - } else { - let make_version_output = execMake.stdout; - if (make_version_output.length === 0) { - make_version_output = execMake.stderr; - } - const output_line_sep = make_version_output.trim().split('\n'); - const isMake = output_line_sep[0].includes('Make'); - const isMingwTool = output_line_sep[1].includes('mingw32'); - - if (isMake && isMingwTool) { - gccKit.preferredGenerator = { name: 'MinGW Makefiles' }; - // save the ENV_PATH as a benign name unlikely to already exist in - // the user's environment, like CMT_MINGW_PATH - gccKit.environmentVariables = { CMT_MINGW_PATH: ENV_PATH }; - } - } - } + if (isMingw(bin)) { + return asMingwKit(bin, gccKit); + } else { + return gccKit; } - return gccKit; } else if (clang_res || clang_cl_res) { const version = await getCompilerVersion('Clang', bin, pr); @@ -389,6 +407,11 @@ export async function kitIfCompiler(bin: string, pr?: ProgressReporter): Promise // mentions msvc. return null; } + if (version.target && version.target.triple.includes('msvc') && clang_cl_res && isMingw(bin)) { + // Skip clang-cl.exe from mingw, as it won't work (correct access to MSVC environment is not granted). + // TODO: handle this case correctly at some point. + return null; + } const clangCompilers: { [lang: string]: string } = {}; const clangxx_fname = clang_cl_res ? fname : fname.replace(/^clang/, 'clang++'); @@ -423,10 +446,16 @@ export async function kitIfCompiler(bin: string, pr?: ProgressReporter): Promise clangCompilers.CXX = clangxx_bin2; } } - return { + const clangKit: Kit = { name: clang_cl_res ? version.detectedName.replace(/^Clang/, 'Clang-cl') : version.detectedName, compilers: clangCompilers }; + + if (isMingw(bin)) { + return asMingwKit(bin, clangKit); + } else { + return clangKit; + } } else { return null; } @@ -945,7 +974,10 @@ export async function effectiveKitEnvironment(kit: Kit, opts?: expand.ExpansionO path_list.push(mingwPath); } if (env.hasOwnProperty('PATH')) { - path_list.unshift(env['PATH'] ?? ''); + // Remove other mingw from PATH as it will cause conflict + const current_path_list = (env['PATH']?.split(';') ?? []); + const current_path_list_without_mingw = current_path_list.filter(p => !isMingw(p)); + path_list.unshift(current_path_list_without_mingw.join(';')); env['PATH'] = path_list.join(';'); } } @@ -982,7 +1014,6 @@ export async function findCLCompilerPath(env?: Environment): Promise[]; - if (isWin32 && kit_options.minGWSearchDirs) { - for (const dir of kit_options.minGWSearchDirs) { - scan_paths.add(dir); - } - } // Default installation locations - scan_paths.add(paths.windows.ProgramFilesX86! + '\\LLVM\\bin'); - scan_paths.add(paths.windows.ProgramFiles! + '\\LLVM\\bin'); + paths.windows.defaultCompilerPaths.LLVM.forEach(scan_paths.add, scan_paths); + paths.windows.defaultCompilerPaths.MSYS2.forEach(scan_paths.add, scan_paths); + const compiler_kits = Array.from(scan_paths).map(path_el => scanDirForCompilerKits(path_el, pr)); kit_promises = kit_promises.concat(compiler_kits); @@ -1047,10 +1072,6 @@ export async function scanForKits(cmakePath?: string, opt?: KitScanOptions) { clang_paths.add(llvm_root); } - // Default installation locations - clang_paths.add(paths.windows.ProgramFiles! + '\\LLVM\\bin'); - clang_paths.add(paths.windows.ProgramFilesX86! + '\\LLVM\\bin'); - // PATH environment variable locations scan_paths.forEach(path_el => clang_paths.add(path_el)); // LLVM bundled in VS locations diff --git a/src/kitsController.ts b/src/kitsController.ts index 4807f407..d6a2bce4 100644 --- a/src/kitsController.ts +++ b/src/kitsController.ts @@ -37,7 +37,7 @@ export enum KitsReadMode { // TODO: migrate all kit related things in extension.ts to this class. export class KitsController { - static minGWSearchDirs: string[] | undefined; + static additionalCompilerSearchDirs: string[] | undefined; /** * The kits available from the user-local kits file */ @@ -215,11 +215,7 @@ export class KitsController { // We don't have any kits defined. Scan for kits if (!KitsController.checkingHaveKits) { KitsController.checkingHaveKits = true; - if (!KitsController.minGWSearchDirs) { - await KitsController.scanForKits(await this.project.getCMakePathofProject()); - } else { - await vscode.commands.executeCommand('cmake.scanForKits'); - } + await KitsController.scanForKits(await this.project.getCMakePathofProject()); KitsController.checkingHaveKits = false; return true; } else { @@ -495,7 +491,7 @@ export class KitsController { log.debug(localize('rescanning.for.kits', 'Rescanning for kits')); // Do the scan: - const discovered_kits = await scanForKits(cmakePath, { minGWSearchDirs: KitsController.minGWSearchDirs }); + const discovered_kits = await scanForKits(cmakePath, { scanDirs: KitsController.additionalCompilerSearchDirs }); // The list with the new definition user kits starts with the non VS ones, // which do not have any variations in the way they can be defined. diff --git a/src/paths.ts b/src/paths.ts index 4c342175..37cbecd8 100644 --- a/src/paths.ts +++ b/src/paths.ts @@ -17,7 +17,33 @@ interface VSCMakePaths { ninja?: string; } +class WindowsDefaultCompilerPaths { + constructor(private readonly _env: WindowsEnvironment) { + this._env = _env; + } + + get LLVM(): string[] { + return [ + this._env.ProgramFiles! + "\\LLVM\\bin", + this._env.ProgramFilesX86! + "\\LLVM\\bin" + ]; + } + + get MSYS2(): string[] { + return [ + paths.windows.SystemDrive! + '\\msys64\\mingw32\\bin', + paths.windows.SystemDrive! + '\\msys64\\mingw64\\bin', + paths.windows.SystemDrive! + '\\msys64\\clang32\\bin', + paths.windows.SystemDrive! + '\\msys64\\clang64\\bin', + paths.windows.SystemDrive! + '\\msys64\\clangarm64\\bin', + paths.windows.SystemDrive! + '\\msys64\\ucrt64\\bin' + ]; + } +} + class WindowsEnvironment { + readonly defaultCompilerPaths: WindowsDefaultCompilerPaths = new WindowsDefaultCompilerPaths(this); + get AppData(): string | undefined { if (util.isTestMode()) { return path.join(vscode.workspace.workspaceFolders![0].uri.fsPath, '.vscode'); diff --git a/test/extension-tests/successful-build/test/scan_kits.test.ts b/test/extension-tests/successful-build/test/scan_kits.test.ts index 1cfdb640..8f17eeb4 100644 --- a/test/extension-tests/successful-build/test/scan_kits.test.ts +++ b/test/extension-tests/successful-build/test/scan_kits.test.ts @@ -14,8 +14,7 @@ suite('MinGW Tests', () => { test('Test scan of mingw', async () => { const kits = await scanForKits(undefined, { - scanDirs: [], - minGWSearchDirs: mingw_dirs, + scanDirs: mingw_dirs, ignorePath: true }); const is_kit_MinGW_present = kits.find(kit => kit.name.indexOf('GCC for i686-w64-mingw32 4.9.2') >= 0) ? true : false; diff --git a/test/unit-tests/config.test.ts b/test/unit-tests/config.test.ts index 1e1f56e5..6983b2f1 100644 --- a/test/unit-tests/config.test.ts +++ b/test/unit-tests/config.test.ts @@ -35,7 +35,8 @@ function createConfig(conf: Partial): Configurat configureEnvironment: {}, buildEnvironment: {}, testEnvironment: {}, - mingwSearchDirs: [], + mingwSearchDirs: [], // Deprecated in 1.14, replaced by additionalCompilerSearchDirs, but kept for backwards compatibility + additionalCompilerSearchDirs: [], emscriptenSearchDirs: [], mergedCompileCommands: null, copyCompileCommands: null,