From 95a17bdc686c8a00d96c74232aef588ba88c5258 Mon Sep 17 00:00:00 2001 From: Ben McMorran Date: Thu, 12 Oct 2023 06:10:25 -0700 Subject: [PATCH] Support $penv{} in include paths (#3377) * Support $penv{} in include paths * Address PR comments --- CHANGELOG.md | 1 + src/expand.ts | 48 +++++++++++++++++++++++++--------------- src/presetsController.ts | 9 ++++++-- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1312b119a..3abbd5bca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Improvements: - In multi-root workspace, the Project Outline View now shows all configured projects. [PR #3270](https://github.com/microsoft/vscode-cmake-tools/pull/3270) [@vlavati](https://github.com/vlavati) - Added script mode and ability to connect to externally launched CMake processes. [PR #3277](https://github.com/microsoft/vscode-cmake-tools/pull/3277) - Added buttons to the project nodes in the Project Outline View. [PR #3354](https://github.com/microsoft/vscode-cmake-tools/pull/3354) [@vlavati](https://github.com/vlavati) +- `$penv{}` macros are expanded in `include` paths in CMakePresets.json as long as `version` is 7 or higher. [#3310](https://github.com/microsoft/vscode-cmake-tools/issues/3310) Bug Fixes: - Fix Unhandled Exception if no args are specified in `cmake.getLaunchTargetFilename` inside an input context of a task. [PR #3348](https://github.com/microsoft/vscode-cmake-tools/issues/3348) [@vlavati](https://github.com/vlavati) diff --git a/src/expand.ts b/src/expand.ts index 13b7967e9..e8222eb83 100644 --- a/src/expand.ts +++ b/src/expand.ts @@ -126,6 +126,11 @@ export async function expandString(input: string | T, opts: ExpansionOptions) return replaceAll(result, '${dollar}', '$'); } +// Regular expression for variable value (between the variable suffix and the next ending curly bracket): +// .+? matches any character (except line terminators) between one and unlimited times, +// as few times as possible, expanding as needed (lazy) +const varValueRegexp = ".+?"; + async function expandStringHelper(input: string, opts: ExpansionOptions) { const envPreNormalize = opts.envOverride ? opts.envOverride : process.env; const env = EnvironmentUtils.create(envPreNormalize); @@ -153,10 +158,6 @@ async function expandStringHelper(input: string, opts: ExpansionOptions) { } } - // Regular expression for variable value (between the variable suffix and the next ending curly bracket): - // .+? matches any character (except line terminators) between one and unlimited times, - // as few times as possible, expanding as needed (lazy) - const varValueRegexp = ".+?"; const envRegex1 = RegExp(`\\$\\{env:(${varValueRegexp})\\}`, "g"); while ((mat = envRegex1.exec(input))) { const full = mat[0]; @@ -189,13 +190,7 @@ async function expandStringHelper(input: string, opts: ExpansionOptions) { subs.set(full, replacement); } - const parentEnvRegex = RegExp(`\\$penv\\{(${varValueRegexp})\\}`, "g"); - while ((mat = parentEnvRegex.exec(input))) { - const full = mat[0]; - const varName = mat[1]; - const replacement = fixPaths(process.env[varName]) || ''; - subs.set(full, replacement); - } + getParentEnvSubstitutions(input, subs); if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) { const folderRegex = RegExp(`\\$\\{workspaceFolder:(${varValueRegexp})\\}`, "g"); @@ -241,6 +236,19 @@ async function expandStringHelper(input: string, opts: ExpansionOptions) { } } + return { ...substituteAll(input, subs), circularReference }; +} + +export async function expandStrings(inputs: string[], opts: ExpansionOptions): Promise { + const expandedInputs: string[] = []; + for (const input of inputs) { + const expandedInput: string = await expandString(input, opts); + expandedInputs.push(expandedInput); + } + return expandedInputs; +} + +export function substituteAll(input: string, subs: Map) { let finalString = input; let didReplacement = false; subs.forEach((value, key) => { @@ -249,14 +257,18 @@ async function expandStringHelper(input: string, opts: ExpansionOptions) { didReplacement = true; } }); - return { result: finalString, didReplacement, circularReference }; + return { result: finalString, didReplacement }; } -export async function expandStrings(inputs: string[], opts: ExpansionOptions): Promise { - const expandedInputs: string[] = []; - for (const input of inputs) { - const expandedInput: string = await expandString(input, opts); - expandedInputs.push(expandedInput); +export function getParentEnvSubstitutions(input: string, subs: Map): Map { + let mat: RegExpMatchArray | null = null; + const parentEnvRegex = RegExp(`\\$penv\\{(${varValueRegexp})\\}`, "g"); + while ((mat = parentEnvRegex.exec(input))) { + const full = mat[0]; + const varName = mat[1]; + const replacement = fixPaths(process.env[varName]) || ''; + subs.set(full, replacement); } - return expandedInputs; + + return subs; } diff --git a/src/presetsController.ts b/src/presetsController.ts index 05febbeff..e609bb617 100644 --- a/src/presetsController.ts +++ b/src/presetsController.ts @@ -9,7 +9,7 @@ import { fs } from '@cmt/pr'; import * as preset from '@cmt/preset'; import * as util from '@cmt/util'; import rollbar from '@cmt/rollbar'; -import { ExpansionOptions } from '@cmt/expand'; +import { ExpansionOptions, getParentEnvSubstitutions, substituteAll } from '@cmt/expand'; import paths from '@cmt/paths'; import { KitsController } from '@cmt/kitsController'; import { descriptionForKit, Kit, SpecialKits } from '@cmt/kit'; @@ -1025,7 +1025,12 @@ export class PresetsController { // Merge the includes in reverse order so that the final presets order is correct for (let i = presetsFile.include.length - 1; i >= 0; i--) { - const fullIncludePath = path.normalize(path.resolve(path.dirname(file), presetsFile.include[i])); + const rawInclude = presetsFile.include[i]; + const includePath = presetsFile.version >= 7 ? + // Version 7 and later support $penv{} expansions in include paths + substituteAll(rawInclude, getParentEnvSubstitutions(rawInclude, new Map())).result : + rawInclude; + const fullIncludePath = path.normalize(path.resolve(path.dirname(file), includePath)); // Do not include files more than once if (referencedFiles.has(fullIncludePath)) {