From fc53a505843c7fe0126278f05dfeb760f1c7443f Mon Sep 17 00:00:00 2001 From: Mark Schofield <34426337+MarkSchofield@users.noreply.github.com> Date: Tue, 12 Dec 2023 23:14:09 -0800 Subject: [PATCH 01/37] Update to version 0.10.0 --- Version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Version b/Version index 899f24f..2774f85 100644 --- a/Version +++ b/Version @@ -1 +1 @@ -0.9.0 \ No newline at end of file +0.10.0 \ No newline at end of file From 845f8a6332f5590a84eac93f5525488d39d20427 Mon Sep 17 00:00:00 2001 From: Mark Schofield <34426337+MarkSchofield@users.noreply.github.com> Date: Wed, 13 Dec 2023 17:03:26 -0800 Subject: [PATCH 02/37] Update README.md; small copy edits --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b69070a..34fecab 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,11 @@ provide a declaration - through a JSON file - of the available builds, where the sorts of useful metadata that makes it easier to build standard tooling around the build. This PowerShell module is one attempt at such tooling. -The PSCMake PowerShell module supports; +The PSCMake PowerShell module supports: - 1. configuring and building a CMake build, - 2. generating a 'DGML'- or 'DOT'- file for a build, - 3. invoking executable CMake build outputs. +1. configuring and building a CMake build, +2. generating a 'DGML'- or 'DOT'- file for a build, +3. invoking executable CMake build outputs. By leveraging the CMake file API, the module is able to offer tab-completion for presets, configurations and targets, and can implicitly scope the build based on the current working directory. @@ -37,7 +37,7 @@ The module provides the following commands: 3. `Write-CMakeBuild` - To output the CMake build as a DOT or DGML graph. 4. `Invoke-CMakeOutput` - To run an executable output from a CMake build, by target name or implicitly by scope. -Running `Build-CMakeBuild` by itself would run the first `buildConfiguration`. Run either command with `-?` to get more +Running `Build-CMakeBuild` by itself would run the first `buildConfiguration`. Run any command with `-?` to get more details. [cmake-presets]: "CMake Presets" From b05137777959fac08b2c3b824c089a281edf0af4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 17 Dec 2023 18:24:03 -0800 Subject: [PATCH 03/37] Bump actions/download-artifact from 3 to 4 (#44) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 770ecda..a3418bd 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -59,7 +59,7 @@ jobs: steps: - name: Download __packages id: download - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: __packages path: __packages From 18c9ff56a6ebe9daddf8256bfb5077b9e19944d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 17 Dec 2023 18:24:25 -0800 Subject: [PATCH 04/37] Bump actions/upload-artifact from 3 to 4 (#45) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 2 +- .github/workflows/pull-request.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a3418bd..2a9aa7b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -47,7 +47,7 @@ jobs: run: | & .\Build\Publish.ps1 -Verbose - name: Upload __packages - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: __packages path: __packages diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 4d4b1f7..1038ba2 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -31,7 +31,7 @@ jobs: run: | & .\Build\Publish.ps1 -Verbose - name: Upload __packages - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: __packages path: __packages From a7f16fa0154fbb55d91b43c5f1144f97d23ed568 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 22:18:46 -0800 Subject: [PATCH 05/37] Bump codecov/codecov-action from 3 to 4 (#46) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2a9aa7b..180d20b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -35,7 +35,7 @@ jobs: } } Invoke-Pester -Configuration $Configuration - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v4 with: files: '${{ env.GITHUB_WORKSPACE }}/Pester-Coverage.xml' flags: unittests From 1f874ad9ec4c0a1982d33f998fb239975cbed495 Mon Sep 17 00:00:00 2001 From: Mark Schofield <34426337+MarkSchofield@users.noreply.github.com> Date: Sat, 9 Mar 2024 10:52:36 -0800 Subject: [PATCH 06/37] Specify a CodeCov token to 'codecov/codecov-action@v4' (#47) --- .github/workflows/ci.yaml | 7 +++++-- .github/workflows/pull-request.yaml | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 180d20b..82116bc 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -14,6 +14,8 @@ on: jobs: build: runs-on: windows-latest + environment: + name: General steps: - name: Checkout uses: actions/checkout@v4 @@ -31,15 +33,16 @@ jobs: CodeCoverage = @{ Enabled = $true OutputFormat = 'JaCoCo' - OutputPath = '${{ env.GITHUB_WORKSPACE }}/Pester-Coverage.xml' + OutputPath = '${{ github.workspace }}/Pester-Coverage.xml' } } Invoke-Pester -Configuration $Configuration - uses: codecov/codecov-action@v4 with: - files: '${{ env.GITHUB_WORKSPACE }}/Pester-Coverage.xml' + files: '${{ github.workspace }}/Pester-Coverage.xml' flags: unittests name: codecov-umbrella + token: '${{ secrets.CODECOV_TOKEN }}' fail_ci_if_error: true verbose: true - name: Build diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 1038ba2..897b9b6 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -5,6 +5,8 @@ on: jobs: build: runs-on: windows-latest + environment: + name: General steps: - name: Checkout uses: actions/checkout@v4 @@ -22,7 +24,7 @@ jobs: CodeCoverage = @{ Enabled = $true OutputFormat = 'JaCoCo' - OutputPath = '${{ env.GITHUB_WORKSPACE }}/Pester-Coverage.xml' + OutputPath = '${{ github.workspace }}/Pester-Coverage.xml' } } Invoke-Pester -Configuration $Configuration From d794aeab86f16a544e8dc7e1963d49730cac980d Mon Sep 17 00:00:00 2001 From: Mark Schofield <34426337+MarkSchofield@users.noreply.github.com> Date: Sat, 9 Mar 2024 11:09:13 -0800 Subject: [PATCH 07/37] Argument completer for 'Invoke-CMakeOutput -Target' should only include executables (#48) --- PSCMake/Common/CMake.ps1 | 21 +++++++++++++++++ PSCMake/PSCMake.psm1 | 51 +++++++++++++++++++++++++++++----------- 2 files changed, 58 insertions(+), 14 deletions(-) diff --git a/PSCMake/Common/CMake.ps1 b/PSCMake/Common/CMake.ps1 index 936fdd6..618d576 100644 --- a/PSCMake/Common/CMake.ps1 +++ b/PSCMake/Common/CMake.ps1 @@ -391,6 +391,27 @@ function Enable-CMakeBuildQuery { } } +# For the 'code model' JSON that was found, load the full 'target' JSON to be able to find 'EXECUTABLE' targets. +# +function FilterExecutableTargets{ + param ( + $CodeModelDirectory, + $TargetTuplesCodeModel + ) + $TargetJsons = $TargetTuplesCodeModel | + ForEach-Object { + Join-Path -Path $CodeModelDirectory -ChildPath $_.jsonFile | + Get-Item | + Get-Content | + ConvertFrom-Json + } + + $TargetJsons | + Where-Object { + $_.type -eq 'EXECUTABLE' + } +} + function Get-CMakeBuildCodeModelDirectory { param( [string] $BinaryDirectory diff --git a/PSCMake/PSCMake.psm1 b/PSCMake/PSCMake.psm1 index ab49438..22249f2 100644 --- a/PSCMake/PSCMake.psm1 +++ b/PSCMake/PSCMake.psm1 @@ -101,6 +101,40 @@ function BuildTargetsCompleter { Where-Object { $_ -ilike "$WordToComplete*" } } +<# + .Synopsis + An argument-completer for `Build-CMakeBuild`'s `-Targets` parameter. +#> +function ExecutableTargetsCompleter { + param( + $CommandName, + $ParameterName, + $WordToComplete, + $CommandAst, + $FakeBoundParameters + ) + $CMakePresetsJson = GetCMakePresets -Silent + $PresetNames = GetBuildPresetNames $CMakePresetsJson + $PresetName = $FakeBoundParameters['Presets'] ?? $PresetNames | + Select-Object -First 1 + $BuildPreset, $ConfigurePreset = ResolvePresets $CMakePresetsJson 'buildPresets' $PresetName + $BinaryDirectory = GetBinaryDirectory $CMakePresetsJson $ConfigurePreset + $CMakeCodeModel = Get-CMakeBuildCodeModel $BinaryDirectory + + # TODO: See if the $BuildPreset has a configuration. + $ConfigurationName = $FakeBoundParameters['Configurations'] ?? $CMakeCodeModel.configurations.Name | + Select-Object -First 1 + $ConfigurationsJson = $CMakeCodeModel.configurations | + Where-Object -Property 'name' -EQ $ConfigurationName + + $TargetTuplesCodeModel = $ConfigurationsJson.targets | + Where-Object { $_.name -ilike "$WordToComplete*" } + + # Use the 'code model' JSON to load the target-specific JSON to filter to targets with 'type' equal to 'EXECUTABLE' + $TargetTuples = FilterExecutableTargets (Get-CMakeBuildCodeModelDirectory $BinaryDirectory) $TargetTuplesCodeModel + $TargetTuples.name +} + <# .Synopsis An argument-completer for `Configure-CMakeBuild`'s `-Presets` parameter. @@ -446,19 +480,8 @@ function Invoke-CMakeOutput { GetScopedTargets $CodeModel $Configuration $ScopeLocation } - # For the 'code model' JSON that was found, load the full 'target' JSON to be able to find 'EXECUTABLE' targets. - # - $TargetTuples = $TargetTuplesCodeModel | - ForEach-Object { - Join-Path -Path (Get-CMakeBuildCodeModelDirectory $BinaryDirectory) -ChildPath $_.jsonFile | - Get-Item | - Get-Content | - ConvertFrom-Json - } - $ExecutableTargetTuples = $TargetTuples | - Where-Object { - $_.type -eq 'EXECUTABLE' - } + # Use the 'code model' JSON to load the target-specific JSON to filter to targets with 'type' equal to 'EXECUTABLE' + $ExecutableTargetTuples = FilterExecutableTargets (Get-CMakeBuildCodeModelDirectory $BinaryDirectory) $TargetTuplesCodeModel $Count = ($ExecutableTargetTuples | Measure-Object).Count if ($Count -eq 0) { Write-Error "No executable target in scope." @@ -490,7 +513,7 @@ function Invoke-CMakeOutput { Register-ArgumentCompleter -CommandName Invoke-CMakeOutput -ParameterName Preset -ScriptBlock $function:BuildPresetsCompleter Register-ArgumentCompleter -CommandName Invoke-CMakeOutput -ParameterName Configuration -ScriptBlock $function:BuildConfigurationsCompleter -Register-ArgumentCompleter -CommandName Invoke-CMakeOutput -ParameterName Target -ScriptBlock $function:BuildTargetsCompleter +Register-ArgumentCompleter -CommandName Invoke-CMakeOutput -ParameterName Target -ScriptBlock $function:ExecutableTargetsCompleter Register-ArgumentCompleter -CommandName Build-CMakeBuild -ParameterName Presets -ScriptBlock $function:BuildPresetsCompleter Register-ArgumentCompleter -CommandName Build-CMakeBuild -ParameterName Configurations -ScriptBlock $function:BuildConfigurationsCompleter From 6321d498307acdcd295baed06f595e244e6b778a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 20:34:17 -0800 Subject: [PATCH 08/37] Bump codecov/codecov-action from 4 to 5 (#50) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4 to 5. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v4...v5) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 82116bc..8760db6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -37,7 +37,7 @@ jobs: } } Invoke-Pester -Configuration $Configuration - - uses: codecov/codecov-action@v4 + - uses: codecov/codecov-action@v5 with: files: '${{ github.workspace }}/Pester-Coverage.xml' flags: unittests From 03f29cb12963f9aa32d8e3a1bfca38a7c8a4a570 Mon Sep 17 00:00:00 2001 From: Mark Schofield Date: Sat, 10 May 2025 21:44:20 -0700 Subject: [PATCH 09/37] Write-Build; style dgml nodes by type, reference the CMake file for target definitions --- PSCMake/Common/CMake.ps1 | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/PSCMake/Common/CMake.ps1 b/PSCMake/Common/CMake.ps1 index 618d576..3b4cd41 100644 --- a/PSCMake/Common/CMake.ps1 +++ b/PSCMake/Common/CMake.ps1 @@ -521,17 +521,33 @@ function WriteDgml { $Targets = @{} '' '' + '' + '' + '' + '' + '' + '' + '' + $SourcePath = $CodeModel.paths.source '' ($CodeModel.configurations | Where-Object { $_.name -eq $Configuration }).targets | ForEach-Object { + $TargetJson = Get-Content (Join-Path -Path $CodeModelDirectory -ChildPath $_.jsonFile) | + ConvertFrom-Json + + $ReferenceFileIndex = $TargetJson.backtraceGraph.nodes[0].file + $Definition = Join-Path -Path $SourcePath -ChildPath $TargetJson.backtraceGraph.files[$ReferenceFileIndex] + '' - $TargetJson = Get-Content (Join-Path -Path $CodeModelDirectory -ChildPath $_.jsonFile) | - ConvertFrom-Json - Get-MemberValue -InputObject $TargetJson -Name artifacts -Or @() | ForEach-Object { ' Date: Tue, 12 Aug 2025 18:48:02 -0700 Subject: [PATCH 10/37] Bump actions/download-artifact from 4 to 5 (#52) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8760db6..97f4cfc 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -62,7 +62,7 @@ jobs: steps: - name: Download __packages id: download - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: __packages path: __packages From da9913bbb814639e6b0d7adc36be6598803daad5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 Sep 2025 09:54:34 -0700 Subject: [PATCH 11/37] Bump actions/checkout from 4 to 5 (#53) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 2 +- .github/workflows/pull-request.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 97f4cfc..a3eaa62 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -18,7 +18,7 @@ jobs: name: General steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 - name: Invoke-Pester diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 897b9b6..79fd98b 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -9,7 +9,7 @@ jobs: name: General steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 - name: Invoke-Pester From 5549634baaba4250e28bd27c99a085a3ed626991 Mon Sep 17 00:00:00 2001 From: Mark Schofield <34426337+MarkSchofield@users.noreply.github.com> Date: Sat, 20 Sep 2025 14:49:25 -0700 Subject: [PATCH 12/37] 'FindCMakeRoot' shouldn't attempt to cache the CMake root path (#58) --- PSCMake/Common/CMake.ps1 | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/PSCMake/Common/CMake.ps1 b/PSCMake/Common/CMake.ps1 index 3b4cd41..a4fde8d 100644 --- a/PSCMake/Common/CMake.ps1 +++ b/PSCMake/Common/CMake.ps1 @@ -30,7 +30,6 @@ $ErrorActionPreference = 'Stop' $PEnv = Get-ChildItem env: | ToHashTable -$PreviousLocation = $null $CMakeCandidates = @( (Get-Command 'cmake' -ErrorAction SilentlyContinue) if ($IsWindows) { @@ -44,13 +43,7 @@ $CMakeCandidates = @( #> function FindCMakeRoot { $CurrentLocation = (Get-Location).Path - if ($CurrentLocation -ne $script:PreviousLocation) { - Write-Verbose "PreviousLocation = $script:PreviousLocation" - Write-Verbose "CurrentLocation = $CurrentLocation" - $script:PreviousLocation = $CurrentLocation - $script:CMakeRoot = GetPathOfFileAbove $CurrentLocation 'CMakePresets.json' - } - $script:CMakeRoot + GetPathOfFileAbove $CurrentLocation 'CMakePresets.json' } $script:CMakePresetsPath = $null @@ -478,13 +471,14 @@ function GetScopedTargets { } else { $CodeModel.configurations[0] } + $SourceDir = $CodeModel.paths.source $CodeModelConfiguration.targets | Where-Object { $Folder = $CodeModelConfiguration.directories[$_.directoryIndex].build $Folder = if ($Folder -eq '.') { - $CMakeRoot + $SourceDir } else { - Join-Path -Path $CMakeRoot -ChildPath $Folder + Join-Path -Path $SourceDir -ChildPath $Folder } $Folder.StartsWith($ScopeLocation) } From d38a75d6d2aa7180d05da379cae8e1d80c32e65a Mon Sep 17 00:00:00 2001 From: Mark Schofield <34426337+MarkSchofield@users.noreply.github.com> Date: Sat, 20 Sep 2025 22:31:50 -0700 Subject: [PATCH 13/37] Add '.vscode/settings.json' with PowerShell code formatting settings; format PowerShell files (#59) --- .vscode/settings.json | 4 ++++ PSCMake/Common/CMake.ps1 | 21 ++++++++++----------- PSCMake/Common/Common.ps1 | 2 +- PSCMake/Common/Console.ps1 | 4 ++-- PSCMake/Common/Ninja.ps1 | 16 ++++++++-------- PSCMake/PSCMake.psm1 | 4 ++-- Tests/EvaluateCondition.Tests.ps1 | 6 +++--- Tests/GetConfigurePresetNames.Tests.ps1 | 6 +++--- Tests/MacroReplacement.Tests.ps1 | 6 +++--- Tests/Using-Location.Tests.ps1 | 6 +++++- Tests/Write-CMakeBuild.Tests.ps1 | 2 +- 11 files changed, 42 insertions(+), 35 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a83440e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline", + "powershell.codeFormatting.newLineAfterCloseBrace": false +} diff --git a/PSCMake/Common/CMake.ps1 b/PSCMake/Common/CMake.ps1 index a4fde8d..c2089cc 100644 --- a/PSCMake/Common/CMake.ps1 +++ b/PSCMake/Common/CMake.ps1 @@ -96,11 +96,11 @@ function GetBuildPresetNames { # Filter presets that have configure presets that have conditions that evaluate to $false $Presets = $Presets | Where-Object { $BuildPresetJson = $_ - $ConfigurePresetJson = $CMakePresetsJson.configurePresets | - Where-Object { $_.name -eq $BuildPresetJson.configurePreset } | - Where-Object { EvaluatePresetCondition $_ $CMakePresetsJson.configurePresets } + $ConfigurePresetJson = $CMakePresetsJson.configurePresets | Where-Object { $_.name -eq $BuildPresetJson.configurePreset } | Where-Object { EvaluatePresetCondition $_ $CMakePresetsJson.configurePresets } + $null -ne $ConfigurePresetJson } + $Presets.name } } @@ -218,8 +218,7 @@ function EvaluateCondition { $ConditionJson, $PresetJson ) - switch ($ConditionJson.type) - { + switch ($ConditionJson.type) { 'equals' { return (MacroReplacement $ConditionJson.lhs $PresetJson) -eq (MacroReplacement $ConditionJson.rhs $PresetJson) } @@ -302,8 +301,8 @@ function GetMacroConstants { } @{ - '${hostSystemName}'=$HostSystemName - '$vendor{PSCMake}'='true' + '${hostSystemName}' = $HostSystemName + '$vendor{PSCMake}' = 'true' } } @@ -386,7 +385,7 @@ function Enable-CMakeBuildQuery { # For the 'code model' JSON that was found, load the full 'target' JSON to be able to find 'EXECUTABLE' targets. # -function FilterExecutableTargets{ +function FilterExecutableTargets { param ( $CodeModelDirectory, $TargetTuplesCodeModel @@ -397,12 +396,12 @@ function FilterExecutableTargets{ Get-Item | Get-Content | ConvertFrom-Json - } + } $TargetJsons | Where-Object { - $_.type -eq 'EXECUTABLE' - } + $_.type -eq 'EXECUTABLE' + } } function Get-CMakeBuildCodeModelDirectory { diff --git a/PSCMake/Common/Common.ps1 b/PSCMake/Common/Common.ps1 index b4cfb69..e58acac 100644 --- a/PSCMake/Common/Common.ps1 +++ b/PSCMake/Common/Common.ps1 @@ -40,7 +40,7 @@ function Get-MemberValue { .Synopsis Performs linear interpolation from the first color to the second. #> -function ColorInterpolation{ +function ColorInterpolation { param( [System.Drawing.Color]$FromColor, [System.Drawing.Color]$ToColor, diff --git a/PSCMake/Common/Console.ps1 b/PSCMake/Common/Console.ps1 index d97a389..9efaef3 100644 --- a/PSCMake/Common/Console.ps1 +++ b/PSCMake/Common/Console.ps1 @@ -104,7 +104,7 @@ function IsVirtualTerminalProcessingEnabled { .Synopsis Returns the control codes to set the foreground color to the specified value. #> -function ColorToControlCode{ +function ColorToControlCode { param( [System.Drawing.Color]$Color ) @@ -117,7 +117,7 @@ function ColorToControlCode{ .Synopsis Returns the control codes to reset foreground attributes, if virtual terminal processing is enable. #> -function ResetForegroundControlCode{ +function ResetForegroundControlCode { if (IsVirtualTerminalProcessingEnabled) { "`e[39m" } diff --git a/PSCMake/Common/Ninja.ps1 b/PSCMake/Common/Ninja.ps1 index 4bdcdee..a9a8f4a 100644 --- a/PSCMake/Common/Ninja.ps1 +++ b/PSCMake/Common/Ninja.ps1 @@ -43,18 +43,18 @@ function TryParseNinjaLog { ) if (Test-Path -Path $NinjaLogPath -PathType Leaf) { Get-Content $NinjaLogPath | - Where-Object {$_[0] -ne '#'} | + Where-Object { $_[0] -ne '#' } | ForEach-Object { $Tokens = $_ -split "\t" [int]$StartTime = $Tokens[0] [int]$EndTime = $Tokens[1] [pscustomobject]@{ - StartTime=$StartTime - EndTime=$EndTime - WriteTime=([long]$Tokens[2]) - File=$Tokens[3] - CommandHash=$Tokens[4] - Duration=($EndTime - $StartTime) + StartTime = $StartTime + EndTime = $EndTime + WriteTime = ([long]$Tokens[2]) + File = $Tokens[3] + CommandHash = $Tokens[4] + Duration = ($EndTime - $StartTime) } } } @@ -97,7 +97,7 @@ function Report-NinjaBuild { ) $BuildStartNinjaTime = ConvertTo-NinjaTime $BuildStartTime $Entries = (TryParseNinjaLog $NinjaLogPath) | - Where-Object {$_.WriteTime -ge $BuildStartNinjaTime} + Where-Object { $_.WriteTime -ge $BuildStartNinjaTime } $Statistics = $Entries | Measure-Object -Property Duration -Maximum if (-not $Statistics) { return diff --git a/PSCMake/PSCMake.psm1 b/PSCMake/PSCMake.psm1 index 22249f2..869ade8 100644 --- a/PSCMake/PSCMake.psm1 +++ b/PSCMake/PSCMake.psm1 @@ -194,7 +194,7 @@ function ConfigureCMake { .Parameter Presets The configure preset names to use. -.Parameter Fresh + .Parameter Fresh A switch specifying whether a 'fresh' configuration is performed - removing any existing cache. .Example @@ -432,7 +432,7 @@ function Write-CMakeBuild { #> function Invoke-CMakeOutput { - [CmdletBinding(PositionalBinding=$false)] + [CmdletBinding(PositionalBinding = $false)] param( [Parameter()] [string] $Preset, diff --git a/Tests/EvaluateCondition.Tests.ps1 b/Tests/EvaluateCondition.Tests.ps1 index 45327b6..4f60401 100644 --- a/Tests/EvaluateCondition.Tests.ps1 +++ b/Tests/EvaluateCondition.Tests.ps1 @@ -4,9 +4,9 @@ BeforeAll { . $PSScriptRoot/../PSCMake/Common/CMake.ps1 Mock GetMacroConstants { @{ - '${hostSystemName}'='Linux' - '$vendor{PSCMake}'='true' - } } + '${hostSystemName}' = 'Linux' + '$vendor{PSCMake}' = 'true' + } } } Describe 'EvaluateCondition' { diff --git a/Tests/GetConfigurePresetNames.Tests.ps1 b/Tests/GetConfigurePresetNames.Tests.ps1 index a15274a..8a99252 100644 --- a/Tests/GetConfigurePresetNames.Tests.ps1 +++ b/Tests/GetConfigurePresetNames.Tests.ps1 @@ -4,9 +4,9 @@ BeforeAll { . $PSScriptRoot/../PSCMake/Common/CMake.ps1 Mock GetMacroConstants { @{ - '${hostSystemName}'='Linux' - '$vendor{PSCMake}'='true' - } } + '${hostSystemName}' = 'Linux' + '$vendor{PSCMake}' = 'true' + } } } Describe 'GetConfigurePresetNames' { diff --git a/Tests/MacroReplacement.Tests.ps1 b/Tests/MacroReplacement.Tests.ps1 index f6c62a6..393a416 100644 --- a/Tests/MacroReplacement.Tests.ps1 +++ b/Tests/MacroReplacement.Tests.ps1 @@ -6,9 +6,9 @@ BeforeAll { $env:PSCMAKE_ENV_TEST = 43 Mock GetMacroConstants { @{ - '${hostSystemName}'='Linux' - '$vendor{PSCMake}'='true' - } } + '${hostSystemName}' = 'Linux' + '$vendor{PSCMake}' = 'true' + } } Mock GetCMakePresetsPath { 'C:\chunky\bacon\CMakePresets.json' diff --git a/Tests/Using-Location.Tests.ps1 b/Tests/Using-Location.Tests.ps1 index 2dbb424..cd20637 100644 --- a/Tests/Using-Location.Tests.ps1 +++ b/Tests/Using-Location.Tests.ps1 @@ -15,17 +15,21 @@ AfterAll { Describe 'Using-Location' { It 'Navigates to the given location and back again afterwards.' { Set-Location $PSScriptRoot + Using-Location $TestFolder { Get-Location | Should -Be $TestFolder } + Get-Location | Should -Be $PSScriptRoot } It 'Restores the location when the scriptlet fails.' { Set-Location $PSScriptRoot - {Using-Location $TestFolder { Write-Error "Oh no!" } } | + + { Using-Location $TestFolder { Write-Error "Oh no!" } } | Should -Throw + Get-Location | Should -Be $PSScriptRoot } diff --git a/Tests/Write-CMakeBuild.Tests.ps1 b/Tests/Write-CMakeBuild.Tests.ps1 index b2fa6cd..15444fb 100644 --- a/Tests/Write-CMakeBuild.Tests.ps1 +++ b/Tests/Write-CMakeBuild.Tests.ps1 @@ -26,7 +26,7 @@ digraph CodeModel { } '@ ((Write-CMakeBuild) -join '') | - Should -Be ($ExpectedDotFile -replace '\r\n','') + Should -Be ($ExpectedDotFile -replace '\r\n', '') } } } From 747dabdd807fb1a5a307e902a520182fa0fc8145 Mon Sep 17 00:00:00 2001 From: Mark Schofield <34426337+MarkSchofield@users.noreply.github.com> Date: Sun, 21 Sep 2025 01:19:48 -0700 Subject: [PATCH 14/37] Include CMake standard targets in 'BuildTargetsCompleter' (#60) --- PSCMake/PSCMake.psm1 | 14 ++++++++++++++ Tests/BuildTargetsCompleter.Tests.ps1 | 5 ++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/PSCMake/PSCMake.psm1 b/PSCMake/PSCMake.psm1 index 869ade8..137ea84 100644 --- a/PSCMake/PSCMake.psm1 +++ b/PSCMake/PSCMake.psm1 @@ -97,6 +97,20 @@ function BuildTargetsCompleter { $ConfigurationsJson = $CMakeCodeModel.configurations | Where-Object -Property 'name' -EQ $ConfigurationName $TargetNames = $ConfigurationsJson.targets.name + + # Add standard CMake targets 'all', 'clean', 'install' + $TargetNames += @( + 'all' + 'clean' + 'install' + ) + + # Add standard CMake target 'test' if 'CTestTestfile.cmake' exists in the binary directory. + $CTestFilePath = Join-Path -Path $BinaryDirectory -ChildPath 'CTestTestfile.cmake' + if (Test-Path -Path $CTestFilePath -PathType Leaf -ErrorAction SilentlyContinue) { + $TargetNames += 'test' + } + $TargetNames | Where-Object { $_ -ilike "$WordToComplete*" } } diff --git a/Tests/BuildTargetsCompleter.Tests.ps1 b/Tests/BuildTargetsCompleter.Tests.ps1 index 7ab50e5..765e247 100644 --- a/Tests/BuildTargetsCompleter.Tests.ps1 +++ b/Tests/BuildTargetsCompleter.Tests.ps1 @@ -20,10 +20,13 @@ Describe 'BuildTargetsCompleter' { Using-Location "$PSScriptRoot/ReferenceBuild" { $Completions = Get-CommandCompletions "Build-CMakeBuild -Targets " - $Completions.CompletionMatches.Count | Should -Be 3 + $Completions.CompletionMatches.Count | Should -Be 6 $Completions.CompletionMatches[0].CompletionText | Should -Be 'A_Library' $Completions.CompletionMatches[1].CompletionText | Should -Be 'B_Library' $Completions.CompletionMatches[2].CompletionText | Should -Be 'C_Library' + $Completions.CompletionMatches[3].CompletionText | Should -Be 'all' + $Completions.CompletionMatches[4].CompletionText | Should -Be 'clean' + $Completions.CompletionMatches[5].CompletionText | Should -Be 'install' } } } From a5c3df457634312b73b3c6eab664b8bb9a5fb3d9 Mon Sep 17 00:00:00 2001 From: Mark Schofield <34426337+MarkSchofield@users.noreply.github.com> Date: Sun, 21 Sep 2025 18:01:44 -0700 Subject: [PATCH 15/37] Update copyright dates --- LICENSE | 2 +- PSCMake/Common/CMake.ps1 | 2 +- PSCMake/Common/Common.ps1 | 2 +- PSCMake/Common/Console.ps1 | 2 +- PSCMake/Common/Ninja.ps1 | 2 +- PSCMake/PSCMake.psm1 | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/LICENSE b/LICENSE index 74afde0..21c1e11 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 Mark Schofield +Copyright (c) 2025 Mark Schofield Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/PSCMake/Common/CMake.ps1 b/PSCMake/Common/CMake.ps1 index c2089cc..c24888a 100644 --- a/PSCMake/Common/CMake.ps1 +++ b/PSCMake/Common/CMake.ps1 @@ -1,7 +1,7 @@ #---------------------------------------------------------------------------------------------------------------------- # MIT License # -# Copyright (c) 2021 Mark Schofield +# Copyright (c) 2025 Mark Schofield # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/PSCMake/Common/Common.ps1 b/PSCMake/Common/Common.ps1 index e58acac..a26ede4 100644 --- a/PSCMake/Common/Common.ps1 +++ b/PSCMake/Common/Common.ps1 @@ -1,7 +1,7 @@ #---------------------------------------------------------------------------------------------------------------------- # MIT License # -# Copyright (c) 2021 Mark Schofield +# Copyright (c) 2025 Mark Schofield # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/PSCMake/Common/Console.ps1 b/PSCMake/Common/Console.ps1 index 9efaef3..e09a593 100644 --- a/PSCMake/Common/Console.ps1 +++ b/PSCMake/Common/Console.ps1 @@ -1,7 +1,7 @@ #---------------------------------------------------------------------------------------------------------------------- # MIT License # -# Copyright (c) 2021 Mark Schofield +# Copyright (c) 2025 Mark Schofield # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/PSCMake/Common/Ninja.ps1 b/PSCMake/Common/Ninja.ps1 index a9a8f4a..45c9949 100644 --- a/PSCMake/Common/Ninja.ps1 +++ b/PSCMake/Common/Ninja.ps1 @@ -1,7 +1,7 @@ #---------------------------------------------------------------------------------------------------------------------- # MIT License # -# Copyright (c) 2021 Mark Schofield +# Copyright (c) 2025 Mark Schofield # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/PSCMake/PSCMake.psm1 b/PSCMake/PSCMake.psm1 index 137ea84..8e310ca 100644 --- a/PSCMake/PSCMake.psm1 +++ b/PSCMake/PSCMake.psm1 @@ -1,7 +1,7 @@ #---------------------------------------------------------------------------------------------------------------------- # MIT License # -# Copyright (c) 2021 Mark Schofield +# Copyright (c) 2025 Mark Schofield # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal From 2b0c8921cc604cc289e152aabf6853e9ff58df2d Mon Sep 17 00:00:00 2001 From: Mark Schofield <34426337+MarkSchofield@users.noreply.github.com> Date: Sun, 21 Sep 2025 18:15:14 -0700 Subject: [PATCH 16/37] Canonicalize banner comments --- PSCMake/Common/CMake.ps1 | 48 +++++++-------- PSCMake/Common/Common.ps1 | 16 ++--- PSCMake/Common/Console.ps1 | 16 ++--- PSCMake/Common/Ninja.ps1 | 24 ++++---- PSCMake/PSCMake.psm1 | 121 ++++++++++++++++++------------------- 5 files changed, 112 insertions(+), 113 deletions(-) diff --git a/PSCMake/Common/CMake.ps1 b/PSCMake/Common/CMake.ps1 index c24888a..1bd0d17 100644 --- a/PSCMake/Common/CMake.ps1 +++ b/PSCMake/Common/CMake.ps1 @@ -38,8 +38,8 @@ $CMakeCandidates = @( ) <# - .Synopsis - Finds the root of the CMake build - the current or ancestral folder containing a 'CMakePresets.json' file. + .Synopsis + Finds the root of the CMake build - the current or ancestral folder containing a 'CMakePresets.json' file. #> function FindCMakeRoot { $CurrentLocation = (Get-Location).Path @@ -49,16 +49,16 @@ function FindCMakeRoot { $script:CMakePresetsPath = $null <# - .Synopsis - Gets the path that the most recently loaded CMakePresets.json was loaded from. + .Synopsis + Gets the path that the most recently loaded CMakePresets.json was loaded from. #> function GetCMakePresetsPath { $script:CMakePresetsPath } <# - .Synopsis - Loads the CMakePresets.json into a PowerShell representation. + .Synopsis + Loads the CMakePresets.json into a PowerShell representation. #> function GetCMakePresets { param( @@ -77,8 +77,8 @@ function GetCMakePresets { } <# - .Synopsis - Gets names of the 'buildPresets' in the specified CMakePresets.json object. + .Synopsis + Gets names of the 'buildPresets' in the specified CMakePresets.json object. #> function GetBuildPresetNames { param( @@ -106,8 +106,8 @@ function GetBuildPresetNames { } <# - .Synopsis - Gets names of the 'configurePresets' in the specified CMakePresets.json object. + .Synopsis + Gets names of the 'configurePresets' in the specified CMakePresets.json object. #> function GetConfigurePresetNames { param( @@ -127,8 +127,8 @@ function GetConfigurePresetNames { } <# - .Synopsis - Finds the 'CMake' command. + .Synopsis + Finds the 'CMake' command. #> function GetCMake { $CMake = Get-Variable -Name 'CMake' -ValueOnly -Scope global -ErrorAction SilentlyContinue @@ -412,11 +412,11 @@ function Get-CMakeBuildCodeModelDirectory { } <# - .Synopsis - Gets PowerShell representation of the CodeModel JSON for the given binary directory. + .Synopsis + Gets PowerShell representation of the CodeModel JSON for the given binary directory. - .Outputs - The PowerShell representation of the CodeModel JSON for the given binary directory, or `$null` if it can't be found. + .Outputs + The PowerShell representation of the CodeModel JSON for the given binary directory, or `$null` if it can't be found. #> function Get-CMakeBuildCodeModel { param( @@ -429,11 +429,11 @@ function Get-CMakeBuildCodeModel { } <# - .Synopsis - Gets the target with the given name, for the given configuration from the specified code model. + .Synopsis + Gets the target with the given name, for the given configuration from the specified code model. - .Outputs - The PowerShell representation of the target from the CodeModel JSON. + .Outputs + The PowerShell representation of the target from the CodeModel JSON. #> function GetNamedTarget { param( @@ -453,11 +453,11 @@ function GetNamedTarget { } <# - .Synopsis - Gets all targets within the given folder scope, for the given configuration from the specified code model. + .Synopsis + Gets all targets within the given folder scope, for the given configuration from the specified code model. - .Outputs - The PowerShell representation of the target(s) from the CodeModel JSON. + .Outputs + The PowerShell representation of the target(s) from the CodeModel JSON. #> function GetScopedTargets { param( diff --git a/PSCMake/Common/Common.ps1 b/PSCMake/Common/Common.ps1 index a26ede4..b8bd851 100644 --- a/PSCMake/Common/Common.ps1 +++ b/PSCMake/Common/Common.ps1 @@ -37,8 +37,8 @@ function Get-MemberValue { } <# - .Synopsis - Performs linear interpolation from the first color to the second. + .Synopsis + Performs linear interpolation from the first color to the second. #> function ColorInterpolation { param( @@ -56,8 +56,8 @@ function ColorInterpolation { } <# - .Synopsis - Checks whether the given file is newer than any subsequently specified files. + .Synopsis + Checks whether the given file is newer than any subsequently specified files. #> function IsUpToDate($Target) { $Dependencies = $args @@ -96,8 +96,8 @@ function DownloadFile([string] $Url, [string] $DownloadPath) { } <# - .Synopsis - Searches the given location and parent folders looking for the given file. + .Synopsis + Searches the given location and parent folders looking for the given file. #> function GetPathOfFileAbove([string]$Location, [string]$File) { for (; $Location.Length -ne 0; $Location = Split-Path $Location) { @@ -109,8 +109,8 @@ function GetPathOfFileAbove([string]$Location, [string]$File) { } <# - .Synopsis - Converts named items on a Pipeline into a hash table. + .Synopsis + Converts named items on a Pipeline into a hash table. #> filter ToHashTable { begin { $Result = @{} } diff --git a/PSCMake/Common/Console.ps1 b/PSCMake/Common/Console.ps1 index e09a593..9fa465d 100644 --- a/PSCMake/Common/Console.ps1 +++ b/PSCMake/Common/Console.ps1 @@ -74,11 +74,11 @@ function GetConsole { } <# - .Synopsis - Returns whether virtual terminal processing is enabled for the current console. + .Synopsis + Returns whether virtual terminal processing is enabled for the current console. - .Outputs - `$true` if virtual terminal processing is enabled, `$false` otherwise. + .Outputs + `$true` if virtual terminal processing is enabled, `$false` otherwise. #> function IsVirtualTerminalProcessingEnabled { if ($null -eq $script:IsVirtualTerminalProcessingEnabled) { @@ -101,8 +101,8 @@ function IsVirtualTerminalProcessingEnabled { } <# - .Synopsis - Returns the control codes to set the foreground color to the specified value. + .Synopsis + Returns the control codes to set the foreground color to the specified value. #> function ColorToControlCode { param( @@ -114,8 +114,8 @@ function ColorToControlCode { } <# - .Synopsis - Returns the control codes to reset foreground attributes, if virtual terminal processing is enable. + .Synopsis + Returns the control codes to reset foreground attributes, if virtual terminal processing is enable. #> function ResetForegroundControlCode { if (IsVirtualTerminalProcessingEnabled) { diff --git a/PSCMake/Common/Ninja.ps1 b/PSCMake/Common/Ninja.ps1 index 45c9949..63c67bd 100644 --- a/PSCMake/Common/Ninja.ps1 +++ b/PSCMake/Common/Ninja.ps1 @@ -30,11 +30,11 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot/Console.ps1 <# - .Synopsis - Tries to parse the specified Ninja log. + .Synopsis + Tries to parse the specified Ninja log. - .Outputs - The entries from the Ninja log. + .Outputs + The entries from the Ninja log. #> function TryParseNinjaLog { [CmdletBinding()] @@ -63,11 +63,11 @@ function TryParseNinjaLog { $FileTimeOffset = [long]12622770400 * [long]10000000 <# - .Synopsis - Converts the specified Ninja log time representation into a [datetime]. + .Synopsis + Converts the specified Ninja log time representation into a [datetime]. - .Notes - This function is currently Windows-only. + .Notes + This function is currently Windows-only. #> function ConvertFrom-NinjaTime { param( @@ -77,11 +77,11 @@ function ConvertFrom-NinjaTime { } <# - .Synopsis - Converts the specified [datetime] into Ninja log time representation. + .Synopsis + Converts the specified [datetime] into Ninja log time representation. - .Notes - This function is currently Windows-only. + .Notes + This function is currently Windows-only. #> function ConvertTo-NinjaTime { param( diff --git a/PSCMake/PSCMake.psm1 b/PSCMake/PSCMake.psm1 index 8e310ca..34242d3 100644 --- a/PSCMake/PSCMake.psm1 +++ b/PSCMake/PSCMake.psm1 @@ -31,8 +31,8 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot/Common/Ninja.ps1 <# - .Synopsis - An argument-completer for `Build-CMakeBuild`'s `-Preset` parameter. + .Synopsis + An argument-completer for `Build-CMakeBuild`'s `-Preset` parameter. #> function BuildPresetsCompleter { param( @@ -47,8 +47,8 @@ function BuildPresetsCompleter { } <# - .Synopsis - An argument-completer for `Build-CMakeBuild`'s `-Configurations` parameter. + .Synopsis + An argument-completer for `Build-CMakeBuild`'s `-Configurations` parameter. #> function BuildConfigurationsCompleter { param( @@ -72,8 +72,8 @@ function BuildConfigurationsCompleter { } <# - .Synopsis - An argument-completer for `Build-CMakeBuild`'s `-Targets` parameter. + .Synopsis + An argument-completer for `Build-CMakeBuild`'s `-Targets` parameter. #> function BuildTargetsCompleter { param( @@ -116,8 +116,8 @@ function BuildTargetsCompleter { } <# - .Synopsis - An argument-completer for `Build-CMakeBuild`'s `-Targets` parameter. + .Synopsis + An argument-completer for `Build-CMakeBuild`'s `-Targets` parameter. #> function ExecutableTargetsCompleter { param( @@ -150,8 +150,8 @@ function ExecutableTargetsCompleter { } <# - .Synopsis - An argument-completer for `Configure-CMakeBuild`'s `-Presets` parameter. + .Synopsis + An argument-completer for `Configure-CMakeBuild`'s `-Presets` parameter. #> function ConfigurePresetsCompleter { param( @@ -199,21 +199,21 @@ function ConfigureCMake { } <# - .Synopsis - Configures a CMake build. + .Synopsis + Configures a CMake build. - .Description - Configures the specified 'configurePresets' entries from a CMakePresets.json file in the current-or-higher folder. + .Description + Configures the specified 'configurePresets' entries from a CMakePresets.json file in the current-or-higher folder. - .Parameter Presets - The configure preset names to use. + .Parameter Presets + The configure preset names to use. - .Parameter Fresh - A switch specifying whether a 'fresh' configuration is performed - removing any existing cache. + .Parameter Fresh + A switch specifying whether a 'fresh' configuration is performed - removing any existing cache. - .Example - # Configure the 'windows-x64' and 'windows-x86' CMake builds. - Configure-CMakeBuild -Presets windows-x64,windows-x86 + .Example + # Configure the 'windows-x64' and 'windows-x86' CMake builds. + Configure-CMakeBuild -Presets windows-x64,windows-x86 #> function Configure-CMakeBuild { [CmdletBinding()] @@ -248,38 +248,38 @@ function Configure-CMakeBuild { } <# - .Synopsis - Builds a CMake build. + .Synopsis + Builds a CMake build. - .Description - Builds the specified 'buildPresets' entries from a CMakePresets.json file in the current-or-higher folder. + .Description + Builds the specified 'buildPresets' entries from a CMakePresets.json file in the current-or-higher folder. - .Parameter Presets + .Parameter Presets - .Parameter Configurations + .Parameter Configurations - .Parameter Targets - One or more + .Parameter Targets + One or more - .Parameter Configure - A switch specifying whether the necessary configuration should be performed before the build is run. + .Parameter Configure + A switch specifying whether the necessary configuration should be performed before the build is run. - .Parameter Report - [Exploration] A switch specifying whether a report should be written of the command times of the build. Ninja builds only. + .Parameter Report + [Exploration] A switch specifying whether a report should be written of the command times of the build. Ninja builds only. - .Parameter Fresh - A switch specifying whether a 'fresh' configuration should be performed before the build is run. + .Parameter Fresh + A switch specifying whether a 'fresh' configuration should be performed before the build is run. - .Example - # Build the 'windows-x64' and 'windows-x86' CMake builds. - Build-CMakeBuild -Presets windows-x64,windows-x86 + .Example + # Build the 'windows-x64' and 'windows-x86' CMake builds. + Build-CMakeBuild -Presets windows-x64,windows-x86 - # Build the 'windows-x64' and 'windows-x86' CMake builds, with the 'Release' configuration. - Build-CMakeBuild -Presets windows-x64,windows-x86 -Configurations Release + # Build the 'windows-x64' and 'windows-x86' CMake builds, with the 'Release' configuration. + Build-CMakeBuild -Presets windows-x64,windows-x86 -Configurations Release - # Build the 'HelperLibrary' target, for the 'windows-x64' and 'windows-x86' CMake builds, with the 'Release' - # configuration. - Build-CMakeBuild -Presets windows-x64,windows-x86 -Configurations Release -Targets HelperLibrary + # Build the 'HelperLibrary' target, for the 'windows-x64' and 'windows-x86' CMake builds, with the 'Release' + # configuration. + Build-CMakeBuild -Presets windows-x64,windows-x86 -Configurations Release -Targets HelperLibrary #> function Build-CMakeBuild { [CmdletBinding()] @@ -420,30 +420,29 @@ function Write-CMakeBuild { } <# - .Synopsis - Runs the output of a CMake build. + .Synopsis + Runs the output of a CMake build. - .Description - `Invoke-CMakeOutput` runs the output of a CMake build. A {preset,configuration,target} can be specified, and `Invoke-CMakeOutput` - will build the target, use the CMake code-model to discover the path to the generated executable and run it, passing any - extra parameter specified. If `Invoke-CMakeOutput` is run from a folder that only contains a single executable target, - then that target will be built and run. + .Description + `Invoke-CMakeOutput` runs the output of a CMake build. A {preset,configuration,target} can be specified, and `Invoke-CMakeOutput` + will build the target, use the CMake code-model to discover the path to the generated executable and run it, passing any + extra parameter specified. If `Invoke-CMakeOutput` is run from a folder that only contains a single executable target, + then that target will be built and run. - .Parameter Preset - The CMake preset to use. If none is specified, then the first valid preset from CMakePresets.json is used. + .Parameter Preset + The CMake preset to use. If none is specified, then the first valid preset from CMakePresets.json is used. - .Parameter Configuration - The CMake configuration to use. If none is specified, then the first valid configuration is used. + .Parameter Configuration + The CMake configuration to use. If none is specified, then the first valid configuration is used. - .Parameter Target - The CMake target that produces an executable to run. + .Parameter Target + The CMake target that produces an executable to run. - .Parameter SkipBuild - If specified, the build will be skipped, otherwise a build will be run before invoking the output. - - .Parameter Arguments - All other parameters will be passed to the discovered executable. + .Parameter SkipBuild + If specified, the build will be skipped, otherwise a build will be run before invoking the output. + .Parameter Arguments + All other parameters will be passed to the discovered executable. #> function Invoke-CMakeOutput { [CmdletBinding(PositionalBinding = $false)] From d86a312f1cfde4f1c7c77c7899b33bca8a0cf39c Mon Sep 17 00:00:00 2001 From: Mark Schofield <34426337+MarkSchofield@users.noreply.github.com> Date: Sun, 21 Sep 2025 18:15:36 -0700 Subject: [PATCH 17/37] Default ninja download to v1.13.1 --- PSCMake/Common/Ninja.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PSCMake/Common/Ninja.ps1 b/PSCMake/Common/Ninja.ps1 index 63c67bd..7177414 100644 --- a/PSCMake/Common/Ninja.ps1 +++ b/PSCMake/Common/Ninja.ps1 @@ -116,8 +116,8 @@ function Report-NinjaBuild { function Download-Ninja { param( [string] $OutputPath, - $NinjaVersion = '1.11.1', - $NinjaArchiveSha256Hash = '524B344A1A9A55005EAF868D991E090AB8CE07FA109F1820D40E74642E289ABC' + $NinjaVersion = '1.13.1', + $NinjaArchiveSha256Hash = '26a40fa8595694dec2fad4911e62d29e10525d2133c9a4230b66397774ae25bf' ) $NinjaArchiveUrl = "https://github.com/ninja-build/ninja/releases/download/v$NinjaVersion/ninja-win.zip" $NinjaArchivePath = Join-Path -Path $OutputPath -ChildPath 'ninja-win.zip' From affc7dc4f0629629bc7adfd91283a7b7db7f4317 Mon Sep 17 00:00:00 2001 From: Mark Schofield <34426337+MarkSchofield@users.noreply.github.com> Date: Sat, 20 Sep 2025 23:26:17 -0700 Subject: [PATCH 18/37] Fix PSScriptAnalyzer errors --- PSCMake/Common/CMake.ps1 | 6 ++--- PSCMake/PSCMake.psm1 | 23 ++++++++++++++++++-- Tests/BuildConfigurationsCompleter.Tests.ps1 | 4 ++-- Tests/BuildPresetsCompleter.Tests.ps1 | 4 ++-- Tests/BuildTargetsCompleter.Tests.ps1 | 2 +- Tests/Get-MemberValue.Tests.ps1 | 8 +++---- Tests/MacroReplacement.Tests.ps1 | 2 +- Tests/TestUtilities.ps1 | 2 +- Tests/Using-Location.Tests.ps1 | 4 ++-- 9 files changed, 37 insertions(+), 18 deletions(-) diff --git a/PSCMake/Common/CMake.ps1 b/PSCMake/Common/CMake.ps1 index 1bd0d17..03f3c90 100644 --- a/PSCMake/Common/CMake.ps1 +++ b/PSCMake/Common/CMake.ps1 @@ -131,12 +131,12 @@ function GetConfigurePresetNames { Finds the 'CMake' command. #> function GetCMake { - $CMake = Get-Variable -Name 'CMake' -ValueOnly -Scope global -ErrorAction SilentlyContinue + $CMake = Get-Variable -Name 'CMake' -ValueOnly -Scope script -ErrorAction SilentlyContinue if (-not $CMake) { foreach ($CMakeCandidate in $CMakeCandidates) { $CMake = Get-Command $CMakeCandidate -ErrorAction SilentlyContinue if ($CMake) { - $global:CMake = $CMake + $script:CMake = $CMake break } } @@ -280,7 +280,7 @@ function GetBinaryDirectory { $CMakePresetsJson, $ConfigurePreset ) - $BinaryDirectory = ResolvePresetProperty $CMakePresetsJson $ConfigurePreset 'binaryDir' + $BinaryDirectory = ResolvePresetProperty -CMakePresetsJson $CMakePresetsJson $ConfigurePreset 'binaryDir' # Perform macro-replacement $Result = MacroReplacement $BinaryDirectory $ConfigurePreset diff --git a/PSCMake/PSCMake.psm1 b/PSCMake/PSCMake.psm1 index 34242d3..358529b 100644 --- a/PSCMake/PSCMake.psm1 +++ b/PSCMake/PSCMake.psm1 @@ -42,6 +42,10 @@ function BuildPresetsCompleter { $CommandAst, $FakeBoundParameters ) + $null = $CommandName + $null = $ParameterName + $null = $CommandAst + $null = $FakeBoundParameters $CMakePresetsJson = GetCMakePresets -Silent GetBuildPresetNames $CMakePresetsJson | Where-Object { $_ -ilike "$WordToComplete*" } } @@ -58,6 +62,11 @@ function BuildConfigurationsCompleter { $CommandAst, $FakeBoundParameters ) + $null = $CommandName + $null = $ParameterName + $null = $CommandAst + $null = $FakeBoundParameters + # TODO: A meaningful implementation would: # * If a buildPreset can be resolved, see if it has a `configuration` and use that. # * If not, look for a code model and use that. @@ -83,6 +92,9 @@ function BuildTargetsCompleter { $CommandAst, $FakeBoundParameters ) + $null = $CommandName + $null = $ParameterName + $null = $CommandAst $CMakePresetsJson = GetCMakePresets -Silent $PresetNames = GetBuildPresetNames $CMakePresetsJson $PresetName = $FakeBoundParameters['Presets'] ?? $PresetNames | @@ -127,6 +139,9 @@ function ExecutableTargetsCompleter { $CommandAst, $FakeBoundParameters ) + $null = $CommandName + $null = $ParameterName + $null = $CommandAst $CMakePresetsJson = GetCMakePresets -Silent $PresetNames = GetBuildPresetNames $CMakePresetsJson $PresetName = $FakeBoundParameters['Presets'] ?? $PresetNames | @@ -161,6 +176,10 @@ function ConfigurePresetsCompleter { $CommandAst, $FakeBoundParameters ) + $null = $CommandName + $null = $ParameterName + $null = $CommandAst + $null = $FakeBoundParameters $CMakePresetsJson = GetCMakePresets -Silent GetConfigurePresetNames $CMakePresetsJson | Where-Object { $_ -ilike "$WordToComplete*" } } @@ -242,7 +261,7 @@ function Configure-CMakeBuild { Write-Error "Unable to find configuration preset '$Preset' in $script:CMakePresetsPath" } - ConfigureCMake $CMake $CMakePresetsJson $ConfigurePreset -Fresh:$Fresh + ConfigureCMake -CMake $CMake $CMakePresetsJson $ConfigurePreset -Fresh:$Fresh } } } @@ -337,7 +356,7 @@ function Build-CMakeBuild { $Fresh -or (-not (Test-Path -Path $CMakeCacheFile -PathType Leaf)) -or (-not (Test-Path -Path (Get-CMakeBuildCodeModelDirectory $BinaryDirectory) -PathType Container))) { - ConfigureCMake $CMake $CMakePresetsJson $ConfigurePreset -Fresh:$Fresh + ConfigureCMake -CMake $CMake $CMakePresetsJson $ConfigurePreset -Fresh:$Fresh } $CodeModel = Get-CMakeBuildCodeModel $BinaryDirectory diff --git a/Tests/BuildConfigurationsCompleter.Tests.ps1 b/Tests/BuildConfigurationsCompleter.Tests.ps1 index 0796e24..a5c7320 100644 --- a/Tests/BuildConfigurationsCompleter.Tests.ps1 +++ b/Tests/BuildConfigurationsCompleter.Tests.ps1 @@ -11,7 +11,7 @@ BeforeAll { Describe 'BuildConfigurationsCompleter' { It 'Returns the default configurations when no preset is specified' { - $Completions = Get-CommandCompletions "Build-CMakeBuild -Configurations " + $Completions = Get-CommandCompletion "Build-CMakeBuild -Configurations " $Completions.CompletionMatches.Count | Should -Be 4 $Completions.CompletionMatches[0].CompletionText | Should -Be 'Release' $Completions.CompletionMatches[1].CompletionText | Should -Be 'Debug' @@ -20,7 +20,7 @@ Describe 'BuildConfigurationsCompleter' { } It 'Returns the default configurations when no preset is specified, filtered by the word to complete' { - $Completions = Get-CommandCompletions "Build-CMakeBuild -Configurations D" + $Completions = Get-CommandCompletion "Build-CMakeBuild -Configurations D" $Completions.CompletionMatches.Count | Should -Be 1 $Completions.CompletionMatches[0].CompletionText | Should -Be 'Debug' } diff --git a/Tests/BuildPresetsCompleter.Tests.ps1 b/Tests/BuildPresetsCompleter.Tests.ps1 index 5a80768..10d24af 100644 --- a/Tests/BuildPresetsCompleter.Tests.ps1 +++ b/Tests/BuildPresetsCompleter.Tests.ps1 @@ -11,14 +11,14 @@ BeforeAll { Describe 'BuildPresetsCompleter' { It 'Returns the presets from the discovered presets file, in the order that they are defined' { - $Completions = Get-CommandCompletions "Build-CMakeBuild -Preset " + $Completions = Get-CommandCompletion "Build-CMakeBuild -Preset " $Completions.CompletionMatches.Count | Should -Be 2 $Completions.CompletionMatches[0].CompletionText | Should -Be 'windows-x64' $Completions.CompletionMatches[1].CompletionText | Should -Be 'windows-arm' } It 'Returns the presets from the discovered presets file, filtered by the word to complete' { - $Completions = Get-CommandCompletions "Build-CMakeBuild -Preset windows-a" + $Completions = Get-CommandCompletion "Build-CMakeBuild -Preset windows-a" $Completions.CompletionMatches.Count | Should -Be 1 $Completions.CompletionMatches[0].CompletionText | Should -Be 'windows-arm' } diff --git a/Tests/BuildTargetsCompleter.Tests.ps1 b/Tests/BuildTargetsCompleter.Tests.ps1 index 765e247..537a8c6 100644 --- a/Tests/BuildTargetsCompleter.Tests.ps1 +++ b/Tests/BuildTargetsCompleter.Tests.ps1 @@ -18,7 +18,7 @@ BeforeAll { Describe 'BuildTargetsCompleter' { It 'Returns the targets of the default preset, default configuration when neither is specified' { Using-Location "$PSScriptRoot/ReferenceBuild" { - $Completions = Get-CommandCompletions "Build-CMakeBuild -Targets " + $Completions = Get-CommandCompletion "Build-CMakeBuild -Targets " $Completions.CompletionMatches.Count | Should -Be 6 $Completions.CompletionMatches[0].CompletionText | Should -Be 'A_Library' diff --git a/Tests/Get-MemberValue.Tests.ps1 b/Tests/Get-MemberValue.Tests.ps1 index ff767e1..3dd189e 100644 --- a/Tests/Get-MemberValue.Tests.ps1 +++ b/Tests/Get-MemberValue.Tests.ps1 @@ -3,24 +3,24 @@ BeforeAll { . $PSScriptRoot/../PSCMake/Common/Common.ps1 - $TestObject = [PSCustomObject]@{ + $script:TestObject = [PSCustomObject]@{ Breakfast = 'Chunky Bacon' } } Describe 'Get-MemberValue' { It 'Returns the member value when available' { - Get-MemberValue -InputObject $TestObject -Name Breakfast -Or Cereal | + Get-MemberValue -InputObject $script:TestObject -Name Breakfast -Or Cereal | Should -Be 'Chunky Bacon' } It 'Returns null when the member value is not available' { - Get-MemberValue -InputObject $TestObject -Name Lunch | + Get-MemberValue -InputObject $script:TestObject -Name Lunch | Should -BeNullOrEmpty } It 'Returns the -Or value when the member value is not available' { - Get-MemberValue -InputObject $TestObject -Name Lunch -Or Sandwich | + Get-MemberValue -InputObject $script:TestObject -Name Lunch -Or Sandwich | Should -Be Sandwich } } diff --git a/Tests/MacroReplacement.Tests.ps1 b/Tests/MacroReplacement.Tests.ps1 index 393a416..332641f 100644 --- a/Tests/MacroReplacement.Tests.ps1 +++ b/Tests/MacroReplacement.Tests.ps1 @@ -14,7 +14,7 @@ BeforeAll { 'C:\chunky\bacon\CMakePresets.json' } - $PresetJson = ConvertFrom-Json -InputObject @' + $script:PresetJson = ConvertFrom-Json -InputObject @' { "name": "windows-x64", "configurePreset": "windows-x64" diff --git a/Tests/TestUtilities.ps1 b/Tests/TestUtilities.ps1 index 4e367ab..6f25df1 100644 --- a/Tests/TestUtilities.ps1 +++ b/Tests/TestUtilities.ps1 @@ -3,6 +3,6 @@ Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' -function Get-CommandCompletions([string] $InputScript) { +function Get-CommandCompletion([string] $InputScript) { [System.Management.Automation.CommandCompletion]::CompleteInput($InputScript, $InputScript.Length, $null) } diff --git a/Tests/Using-Location.Tests.ps1 b/Tests/Using-Location.Tests.ps1 index cd20637..4bc2d97 100644 --- a/Tests/Using-Location.Tests.ps1 +++ b/Tests/Using-Location.Tests.ps1 @@ -3,13 +3,13 @@ BeforeAll { . $PSScriptRoot/../PSCMake/Common/Common.ps1 - $OriginalLocation = Get-Location + $script:OriginalLocation = Get-Location $TestFolder = Join-Path -Path $PSScriptRoot -ChildPath '__test' $null = New-Item -Path $TestFolder -ItemType Directory -Force -ErrorAction SilentlyContinue } AfterAll { - Set-Location $OriginalLocation + Set-Location $script:OriginalLocation } Describe 'Using-Location' { From 01d6efe5b7381110cd75cca93f5962b80f1f6e15 Mon Sep 17 00:00:00 2001 From: Mark Schofield <34426337+MarkSchofield@users.noreply.github.com> Date: Sat, 20 Sep 2025 12:10:42 -0700 Subject: [PATCH 19/37] Canonicalize -Preset,-Configuration; wildcard support --- PSCMake/Common/CMake.ps1 | 16 +++ PSCMake/PSCMake.psm1 | 132 +++++++++++-------- Tests/Build-CMakeBuild.Tests.ps1 | 39 +++++- Tests/BuildConfigurationsCompleter.Tests.ps1 | 4 +- 4 files changed, 131 insertions(+), 60 deletions(-) diff --git a/PSCMake/Common/CMake.ps1 b/PSCMake/Common/CMake.ps1 index 03f3c90..e61f9d6 100644 --- a/PSCMake/Common/CMake.ps1 +++ b/PSCMake/Common/CMake.ps1 @@ -37,6 +37,22 @@ $CMakeCandidates = @( } ) +<# + .Synopsis + Invokes CMake. + + .Description + A function wrapping calls to CMake, allowing the calls to be mocked for testing. +#> +function InvokeCMake { + param( + [string] $CMakePath, + [string[]] $Arguments + ) + Write-Verbose "CMake Arguments: $Arguments" + & $CMakePath @Arguments +} + <# .Synopsis Finds the root of the CMake build - the current or ancestral folder containing a 'CMakePresets.json' file. diff --git a/PSCMake/PSCMake.psm1 b/PSCMake/PSCMake.psm1 index 358529b..5eca240 100644 --- a/PSCMake/PSCMake.psm1 +++ b/PSCMake/PSCMake.psm1 @@ -97,14 +97,14 @@ function BuildTargetsCompleter { $null = $CommandAst $CMakePresetsJson = GetCMakePresets -Silent $PresetNames = GetBuildPresetNames $CMakePresetsJson - $PresetName = $FakeBoundParameters['Presets'] ?? $PresetNames | + $PresetName = $FakeBoundParameters['Preset'] ?? $PresetNames | Select-Object -First 1 $BuildPreset, $ConfigurePreset = ResolvePresets $CMakePresetsJson 'buildPresets' $PresetName $BinaryDirectory = GetBinaryDirectory $CMakePresetsJson $ConfigurePreset $CMakeCodeModel = Get-CMakeBuildCodeModel $BinaryDirectory # TODO: See if the $BuildPreset has a configuration. - $ConfigurationName = $FakeBoundParameters['Configurations'] ?? $CMakeCodeModel.configurations.Name | + $ConfigurationName = $FakeBoundParameters['Configuration'] ?? $CMakeCodeModel.configurations.Name | Select-Object -First 1 $ConfigurationsJson = $CMakeCodeModel.configurations | Where-Object -Property 'name' -EQ $ConfigurationName @@ -166,7 +166,7 @@ function ExecutableTargetsCompleter { <# .Synopsis - An argument-completer for `Configure-CMakeBuild`'s `-Presets` parameter. + An argument-completer for `Configure-CMakeBuild`'s `-Preset` parameter. #> function ConfigurePresetsCompleter { param( @@ -209,9 +209,8 @@ function ConfigureCMake { '--log-level=VERBOSE' } ) - Write-Verbose "CMake Arguments: $CMakeArguments" - & $CMake @CMakeArguments + InvokeCMake $CMake $CMakeArguments if ($LASTEXITCODE -ne 0) { Write-Error "Configuration failed. Command line: '$($CMake.Source)' $($CMakeArguments -join ' ')" } @@ -224,41 +223,46 @@ function ConfigureCMake { .Description Configures the specified 'configurePresets' entries from a CMakePresets.json file in the current-or-higher folder. - .Parameter Presets - The configure preset names to use. + .Parameter Preset + The configure preset name to use. Multiple presets can be specified. .Parameter Fresh A switch specifying whether a 'fresh' configuration is performed - removing any existing cache. .Example # Configure the 'windows-x64' and 'windows-x86' CMake builds. - Configure-CMakeBuild -Presets windows-x64,windows-x86 + Configure-CMakeBuild -Preset windows-x64,windows-x86 #> function Configure-CMakeBuild { [CmdletBinding()] param( + [Alias('Presets')] + [SupportsWildcards()] [Parameter()] - [string[]] $Presets, + [string[]] $Preset, [Parameter()] [switch] $Fresh ) $CMakeRoot = FindCMakeRoot $CMakePresetsJson = GetCMakePresets - $PresetNames = GetConfigurePresetNames $CMakePresetsJson - if (-not $Presets) { - $Presets = $PresetNames | Select-Object -First 1 - Write-Information "No preset specified, defaulting to: $Presets" + $ConfigurePresetNames = GetConfigurePresetNames $CMakePresetsJson + $ConfigurePresetNames = if (-not $Preset) { + $ConfigurePresetNames | Select-Object -First 1 + } else { + foreach ($CandidatePreset in $Preset) { + $ConfigurePresetNames | Where-Object { $_ -like $CandidatePreset } + } } $CMake = GetCMake Using-Location $CMakeRoot { - foreach ($Preset in $Presets) { - Write-Output "Preset : $Preset" + foreach ($ConfigurePresetName in $ConfigurePresetNames) { + Write-Output "Preset : $ConfigurePresetName" - $ConfigurePreset = $CMakePresetsJson.configurePresets | Where-Object { $_.name -eq $Preset } + $ConfigurePreset = $CMakePresetsJson.configurePresets | Where-Object { $_.name -eq $ConfigurePresetName } if (-not $ConfigurePreset) { - Write-Error "Unable to find configuration preset '$Preset' in $script:CMakePresetsPath" + Write-Error "Unable to find configuration preset '$ConfigurePresetName' in $script:CMakePresetsPath" } ConfigureCMake -CMake $CMake $CMakePresetsJson $ConfigurePreset -Fresh:$Fresh @@ -273,11 +277,11 @@ function Configure-CMakeBuild { .Description Builds the specified 'buildPresets' entries from a CMakePresets.json file in the current-or-higher folder. - .Parameter Presets + .Parameter Preset - .Parameter Configurations + .Parameter Configuration - .Parameter Targets + .Parameter Target One or more .Parameter Configure @@ -291,26 +295,31 @@ function Configure-CMakeBuild { .Example # Build the 'windows-x64' and 'windows-x86' CMake builds. - Build-CMakeBuild -Presets windows-x64,windows-x86 + Build-CMakeBuild -Preset windows-x64,windows-x86 # Build the 'windows-x64' and 'windows-x86' CMake builds, with the 'Release' configuration. - Build-CMakeBuild -Presets windows-x64,windows-x86 -Configurations Release + Build-CMakeBuild -Preset windows-x64,windows-x86 -Configuration Release # Build the 'HelperLibrary' target, for the 'windows-x64' and 'windows-x86' CMake builds, with the 'Release' # configuration. - Build-CMakeBuild -Presets windows-x64,windows-x86 -Configurations Release -Targets HelperLibrary + Build-CMakeBuild -Preset windows-x64,windows-x86 -Configuration Release -Target HelperLibrary #> function Build-CMakeBuild { [CmdletBinding()] param( + [Alias('Presets')] + [SupportsWildcards()] [Parameter(Position = 0)] - [string[]] $Presets, + [string[]] $Preset, + [Alias('Configurations')] + [SupportsWildcards()] [Parameter(Position = 1)] - [string[]] $Configurations = @($null), + [string[]] $Configuration, + [Alias('Targets')] [Parameter(Position = 2)] - [string[]] $Targets, + [string[]] $Target, [Parameter()] [switch] $Configure, @@ -323,27 +332,30 @@ function Build-CMakeBuild { ) $CMakeRoot = FindCMakeRoot $CMakePresetsJson = GetCMakePresets - $PresetNames = GetBuildPresetNames $CMakePresetsJson - - if (-not $Presets) { - if (-not $PresetNames) { + $BuildPresetNames = GetBuildPresetNames $CMakePresetsJson + $BuildPresetNames = if (-not $Preset) { + if (-not $BuildPresetNames) { Write-Error "No Presets values specified, and one could not be inferred." } - $Presets = $PresetNames | Select-Object -First 1 + $BuildPresetNames | Select-Object -First 1 + } else { + foreach ($CandidatePreset in $Preset) { + $BuildPresetNames | Where-Object { $_ -like $CandidatePreset } + } } # If; # * no targets were specified, and # * the current location is different from the cmake root # Then we're a scoped build! - $ScopedBuild = (-not $Targets) -and ($CMakeRoot -ne ((Get-Location).Path)) + $ScopedBuild = (-not $Target) -and ($CMakeRoot -ne ((Get-Location).Path)) $ScopeLocation = (Get-Location).Path $CMake = GetCMake Using-Location $CMakeRoot { - foreach ($Preset in $Presets) { - Write-Output "Preset : $Preset" + foreach ($BuildPresetName in $BuildPresetNames) { + Write-Output "Preset : $BuildPresetName" - $BuildPreset, $ConfigurePreset = ResolvePresets $CMakePresetsJson 'buildPresets' $Preset + $BuildPreset, $ConfigurePreset = ResolvePresets $CMakePresetsJson 'buildPresets' $BuildPresetName $BinaryDirectory = GetBinaryDirectory $CMakePresetsJson $ConfigurePreset $CMakeCacheFile = Join-Path -Path $BinaryDirectory -ChildPath 'CMakeCache.txt' @@ -361,33 +373,39 @@ function Build-CMakeBuild { $CodeModel = Get-CMakeBuildCodeModel $BinaryDirectory - foreach ($Configuration in $Configurations) { - Write-Output "Configuration : $Configuration" + [string[]] $ConfigurationNames = @($null) + if ($Configuration) { + $ConfigurationNames = foreach ($CandidateConfigurationName in $Configuration) { + $CodeModel.configurations.name | Where-Object { $_ -like $CandidateConfigurationName } + } + } + + foreach ($ConfigurationName in $ConfigurationNames) { + Write-Output "Configuration : $ConfigurationName" - if ($ScopedBuild) { - $TargetTuples = GetScopedTargets $CodeModel $Configuration $ScopeLocation - $Targets = if ($TargetTuples) { + $TargetNames = if ($ScopedBuild) { + $TargetTuples = GetScopedTargets $CodeModel $ConfigurationName $ScopeLocation + if ($TargetTuples) { $TargetTuples.name - } else { - @() } - Write-Output "Scoped Targets : $Targets" + } else { + $Target } $CMakeArguments = @( '--build' - '--preset', $Preset + '--preset', $BuildPresetName + if ($ConfigurationName) { + '--config', $ConfigurationName + } + if ($TargetNames) { + '--target' + $TargetNames + } ) - if ($Targets) { - $CMakeArguments += '--target' - $CMakeArguments += $Targets - } - - Write-Verbose "CMake Arguments: $CMakeArguments" - $StartTime = [datetime]::Now - & $CMake @CMakeArguments (($Configuration)?('--config', $Configuration):$null) + InvokeCMake $CMake $CMakeArguments if ($LASTEXITCODE -ne 0) { Write-Error "Build failed. Command line: '$($CMake.Source)' $($CMakeArguments -join ' ')" } @@ -498,7 +516,7 @@ function Invoke-CMakeOutput { # $CodeModel = Get-CMakeBuildCodeModel $BinaryDirectory if (-not $CodeModel) { - Configure-CMakeBuild -Presets $Preset + Configure-CMakeBuild -Preset $Preset $CodeModel = Get-CMakeBuildCodeModel $BinaryDirectory } @@ -547,11 +565,11 @@ Register-ArgumentCompleter -CommandName Invoke-CMakeOutput -ParameterName Preset Register-ArgumentCompleter -CommandName Invoke-CMakeOutput -ParameterName Configuration -ScriptBlock $function:BuildConfigurationsCompleter Register-ArgumentCompleter -CommandName Invoke-CMakeOutput -ParameterName Target -ScriptBlock $function:ExecutableTargetsCompleter -Register-ArgumentCompleter -CommandName Build-CMakeBuild -ParameterName Presets -ScriptBlock $function:BuildPresetsCompleter -Register-ArgumentCompleter -CommandName Build-CMakeBuild -ParameterName Configurations -ScriptBlock $function:BuildConfigurationsCompleter -Register-ArgumentCompleter -CommandName Build-CMakeBuild -ParameterName Targets -ScriptBlock $function:BuildTargetsCompleter +Register-ArgumentCompleter -CommandName Build-CMakeBuild -ParameterName Preset -ScriptBlock $function:BuildPresetsCompleter +Register-ArgumentCompleter -CommandName Build-CMakeBuild -ParameterName Configuration -ScriptBlock $function:BuildConfigurationsCompleter +Register-ArgumentCompleter -CommandName Build-CMakeBuild -ParameterName Target -ScriptBlock $function:BuildTargetsCompleter -Register-ArgumentCompleter -CommandName Configure-CMakeBuild -ParameterName Presets -ScriptBlock $function:ConfigurePresetsCompleter +Register-ArgumentCompleter -CommandName Configure-CMakeBuild -ParameterName Preset -ScriptBlock $function:ConfigurePresetsCompleter Register-ArgumentCompleter -CommandName Write-CMakeBuild -ParameterName Preset -ScriptBlock $function:BuildPresetsCompleter Register-ArgumentCompleter -CommandName Write-CMakeBuild -ParameterName Configuration -ScriptBlock $function:BuildConfigurationsCompleter diff --git a/Tests/Build-CMakeBuild.Tests.ps1 b/Tests/Build-CMakeBuild.Tests.ps1 index e577244..491bb89 100644 --- a/Tests/Build-CMakeBuild.Tests.ps1 +++ b/Tests/Build-CMakeBuild.Tests.ps1 @@ -7,18 +7,55 @@ BeforeAll { . $PSScriptRoot/TestUtilities.ps1 . $PSScriptRoot/ReferenceBuild.ps1 + # Configure the reference build so that there is reference content to work with. $Properties = PrepareReferenceBuild - $CMake = "$env:ProgramFiles/CMake/bin/cmake.exe" & $CMake @Properties Import-Module -Force $PSScriptRoot/../PSCMake/PSCMake.psd1 -DisableNameChecking + + # Mock subsequent calls to invoke CMake so that we don't actually try to build anything. + $script:CMakeCalls = @() + Mock -ModuleName PSCMake InvokeCMake { + param( + [string] $CMakePath, + [string[]] $Arguments + ) + $script:CMakeCalls += , $Arguments + } } Describe 'Build-CMakeBuild' { + BeforeEach { + $script:CMakeCalls = @() + } + It 'Builds with no parameters' { Using-Location "$PSScriptRoot/ReferenceBuild" { Build-CMakeBuild } + + $script:CMakeCalls | Should -HaveCount 1 + $script:CMakeCalls[0] | Should -Be @('--build', '--preset', 'windows-x64') + } + + It 'Builds with wildcard presets' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + Build-CMakeBuild -Preset '*-x64' + } + + $CMakeCalls | Should -HaveCount 1 + $script:CMakeCalls[0] | Should -Be @('--build', '--preset', 'windows-x64') + } + + It 'Builds with wildcard configurations' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + Build-CMakeBuild -Preset 'windows-x64' -Configuration * + } + + $CMakeCalls | Should -HaveCount 3 + $script:CMakeCalls[0] | Should -Be @('--build', '--preset', 'windows-x64', '--config', 'Debug') + $script:CMakeCalls[1] | Should -Be @('--build', '--preset', 'windows-x64', '--config', 'Release') + $script:CMakeCalls[2] | Should -Be @('--build', '--preset', 'windows-x64', '--config', 'RelWithDebInfo') } } diff --git a/Tests/BuildConfigurationsCompleter.Tests.ps1 b/Tests/BuildConfigurationsCompleter.Tests.ps1 index a5c7320..a2663d8 100644 --- a/Tests/BuildConfigurationsCompleter.Tests.ps1 +++ b/Tests/BuildConfigurationsCompleter.Tests.ps1 @@ -11,7 +11,7 @@ BeforeAll { Describe 'BuildConfigurationsCompleter' { It 'Returns the default configurations when no preset is specified' { - $Completions = Get-CommandCompletion "Build-CMakeBuild -Configurations " + $Completions = Get-CommandCompletion "Build-CMakeBuild -Configuration " $Completions.CompletionMatches.Count | Should -Be 4 $Completions.CompletionMatches[0].CompletionText | Should -Be 'Release' $Completions.CompletionMatches[1].CompletionText | Should -Be 'Debug' @@ -20,7 +20,7 @@ Describe 'BuildConfigurationsCompleter' { } It 'Returns the default configurations when no preset is specified, filtered by the word to complete' { - $Completions = Get-CommandCompletion "Build-CMakeBuild -Configurations D" + $Completions = Get-CommandCompletion "Build-CMakeBuild -Configuration D" $Completions.CompletionMatches.Count | Should -Be 1 $Completions.CompletionMatches[0].CompletionText | Should -Be 'Debug' } From cc40cf35fb4221ecc1061f433a11fefa22620208 Mon Sep 17 00:00:00 2001 From: Mark Schofield <34426337+MarkSchofield@users.noreply.github.com> Date: Sun, 21 Sep 2025 23:47:00 -0700 Subject: [PATCH 20/37] 'Build-CMakeBuild' should re-run configuration if the code model cannot be loaded (#64) --- PSCMake/PSCMake.psm1 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/PSCMake/PSCMake.psm1 b/PSCMake/PSCMake.psm1 index 5eca240..14a684c 100644 --- a/PSCMake/PSCMake.psm1 +++ b/PSCMake/PSCMake.psm1 @@ -364,15 +364,17 @@ function Build-CMakeBuild { # 2) '-fresh' was specified # 3) "$BinaryDirectory/CMakeCache.txt" doesn't exist # 4) The "Get-CMakeBuildCodeModelDirectory" folder doesn't exist + # 5) "Get-CMakeBuildCodeModel" returns $null if ($Configure -or $Fresh -or (-not (Test-Path -Path $CMakeCacheFile -PathType Leaf)) -or - (-not (Test-Path -Path (Get-CMakeBuildCodeModelDirectory $BinaryDirectory) -PathType Container))) { + (-not (Test-Path -Path (Get-CMakeBuildCodeModelDirectory $BinaryDirectory) -PathType Container)) -or + (-not ($CodeModel = Get-CMakeBuildCodeModel $BinaryDirectory)) + ) { ConfigureCMake -CMake $CMake $CMakePresetsJson $ConfigurePreset -Fresh:$Fresh + $CodeModel = Get-CMakeBuildCodeModel $BinaryDirectory } - $CodeModel = Get-CMakeBuildCodeModel $BinaryDirectory - [string[]] $ConfigurationNames = @($null) if ($Configuration) { $ConfigurationNames = foreach ($CandidateConfigurationName in $Configuration) { From 55089a2d752be79b6ed4a00efaf738fbd639508a Mon Sep 17 00:00:00 2001 From: Mark Schofield <34426337+MarkSchofield@users.noreply.github.com> Date: Sun, 28 Sep 2025 17:11:59 -0700 Subject: [PATCH 21/37] '-Preset' values should be checked for equality before wildcard match (#67) --- PSCMake/PSCMake.psm1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PSCMake/PSCMake.psm1 b/PSCMake/PSCMake.psm1 index 14a684c..bc707df 100644 --- a/PSCMake/PSCMake.psm1 +++ b/PSCMake/PSCMake.psm1 @@ -251,7 +251,7 @@ function Configure-CMakeBuild { $ConfigurePresetNames | Select-Object -First 1 } else { foreach ($CandidatePreset in $Preset) { - $ConfigurePresetNames | Where-Object { $_ -like $CandidatePreset } + $ConfigurePresetNames | Where-Object { ($_ -eq $CandidatePreset) -or ($_ -like $CandidatePreset) } } } @@ -340,7 +340,7 @@ function Build-CMakeBuild { $BuildPresetNames | Select-Object -First 1 } else { foreach ($CandidatePreset in $Preset) { - $BuildPresetNames | Where-Object { $_ -like $CandidatePreset } + $BuildPresetNames | Where-Object { ($_ -eq $CandidatePreset) -or ($_ -like $CandidatePreset) } } } @@ -378,7 +378,7 @@ function Build-CMakeBuild { [string[]] $ConfigurationNames = @($null) if ($Configuration) { $ConfigurationNames = foreach ($CandidateConfigurationName in $Configuration) { - $CodeModel.configurations.name | Where-Object { $_ -like $CandidateConfigurationName } + $CodeModel.configurations.name | Where-Object { ($_ -eq $CandidateConfigurationName) -or ($_ -like $CandidateConfigurationName) } } } From f60e5293a0d55417c5926d55c46a5f4d4e60bb93 Mon Sep 17 00:00:00 2001 From: Mark Schofield Date: Sun, 28 Sep 2025 15:17:18 -0700 Subject: [PATCH 22/37] Consolidate 'ancestor search' from 'ResolvePresetProperty' and 'EvaluatePresetCondition' --- .vscode/settings.json | 3 +- PSCMake/Common/CMake.ps1 | 74 +++++++++++++++++---------- Tests/ResolvePresetProperty.Tests.ps1 | 4 +- Tests/SearchAncestors.Tests.ps1 | 70 +++++++++++++++++++++++++ 4 files changed, 120 insertions(+), 31 deletions(-) create mode 100644 Tests/SearchAncestors.Tests.ps1 diff --git a/.vscode/settings.json b/.vscode/settings.json index a83440e..9f7a7f3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline", - "powershell.codeFormatting.newLineAfterCloseBrace": false + "powershell.codeFormatting.newLineAfterCloseBrace": false, + "editor.renderWhitespace": "all" } diff --git a/PSCMake/Common/CMake.ps1 b/PSCMake/Common/CMake.ps1 index e61f9d6..5367ddd 100644 --- a/PSCMake/Common/CMake.ps1 +++ b/PSCMake/Common/CMake.ps1 @@ -133,10 +133,12 @@ function GetConfigurePresetNames { $Presets = $CMakePresetsJson.configurePresets # Filter presets that have '"hidden":true' - $Presets = $Presets | Where-Object { -not (Get-MemberValue -InputObject $_ -Name 'hidden' -Or $false) } + $Presets = $Presets | + Where-Object { -not (Get-MemberValue -InputObject $_ -Name 'hidden' -Or $false) } # Filter presets that have (or their ancestors have) conditions that evaluate to $false - $Presets = $Presets | Where-Object { EvaluatePresetCondition $_ $CMakePresetsJson.configurePresets } + $Presets = $Presets | + Where-Object { EvaluatePresetCondition $_ $CMakePresetsJson.configurePresets } $Presets.name } @@ -185,48 +187,64 @@ function ResolvePresets { $PresetJson, $ConfigurePresetJson } -function ResolvePresetProperty { +<# + .Synopsis + Searches the specified preset and its ancestors, invoking the specified action for each preset. + + .Parameter Preset + The preset to start searching from. + + .Parameter Presets + The collection of presets to search for 'inherit' references. + + .Description + The action should return $null to continue searching, or a non-$null value to stop searching and return that value. +#> +function SearchAncestors { param( - $CMakePresetsJson, - $ConfigurePreset, - $PropertyName + $Preset, + $Presets, + [scriptblock] $Action ) - - for ($Preset = $ConfigurePreset; $Preset; ) { - $PropertyValue = Get-MemberValue -InputObject $Preset -Name $PropertyName - if ($PropertyValue) { - return $PropertyValue + for (; $Preset; ) { + $Result = & $Action $Preset + if ($null -ne $Result) { + return $Result } - $BasePreset = Get-MemberValue $Preset 'inherits' if (-not $BasePreset) { break } + $Preset = $Presets | Where-Object { $_.name -eq $BasePreset } | Select-Object -First 1 + } +} - $Preset = $CMakePresetsJson.configurePresets | Where-Object { $_.name -eq $BasePreset } | Select-Object -First 1 +function ResolvePresetProperty { + param( + $Preset, + $Presets, + $PropertyName + ) + SearchAncestors $Preset $Presets { + param($CurrentPreset) + Get-MemberValue -InputObject $CurrentPreset -Name $PropertyName } } function EvaluatePresetCondition { param( - $PresetJson, - $PresetsJson + $Preset, + $Presets ) - - $PresetConditionJson = Get-MemberValue $PresetJson 'condition' - if ($PresetConditionJson) { - if (-not (EvaluateCondition $PresetConditionJson $PresetJson)) { + $Result = SearchAncestors $Preset $Presets { + param($CurrentPreset) + $PresetConditionJson = Get-MemberValue $CurrentPreset 'condition' + if (($PresetConditionJson) -and + (-not (EvaluateCondition $PresetConditionJson $CurrentPreset))) { return $false } } - - $BasePresetName = Get-MemberValue $PresetJson 'inherits' - if (-not $BasePresetName) { - return $true - } - - $BasePreset = $PresetsJson | Where-Object { $_.name -eq $BasePresetName } | Select-Object -First 1 - EvaluatePresetCondition $BasePreset $PresetsJson + $Result -ne $false } function EvaluateCondition { @@ -296,7 +314,7 @@ function GetBinaryDirectory { $CMakePresetsJson, $ConfigurePreset ) - $BinaryDirectory = ResolvePresetProperty -CMakePresetsJson $CMakePresetsJson $ConfigurePreset 'binaryDir' + $BinaryDirectory = ResolvePresetProperty $ConfigurePreset $CMakePresetsJson.configurePresets 'binaryDir' # Perform macro-replacement $Result = MacroReplacement $BinaryDirectory $ConfigurePreset diff --git a/Tests/ResolvePresetProperty.Tests.ps1 b/Tests/ResolvePresetProperty.Tests.ps1 index 92be33b..4d82d3c 100644 --- a/Tests/ResolvePresetProperty.Tests.ps1 +++ b/Tests/ResolvePresetProperty.Tests.ps1 @@ -9,7 +9,7 @@ Describe 'ResolvePresetProperty' { $CMakePresetsJson = Get-Content "$PSScriptRoot/ReferencePresets/CMakePresets.Single.json" | ConvertFrom-Json $ConfigurePreset = $CMakePresetsJson.configurePresets[0] - $BinaryDirectory = ResolvePresetProperty $CMakePresetsJson $ConfigurePreset 'binaryDir' + $BinaryDirectory = ResolvePresetProperty $ConfigurePreset $CMakePresetsJson.configurePresets 'binaryDir' $BinaryDirectory | Should -Be '${sourceDir}/__output/${presetName}' } @@ -17,7 +17,7 @@ Describe 'ResolvePresetProperty' { $CMakePresetsJson = Get-Content "$PSScriptRoot/ReferencePresets/CMakePresets.Inherited.json" | ConvertFrom-Json $ConfigurePreset = $CMakePresetsJson.configurePresets | Where-Object { $_.name -eq 'windows-x64' } | Select-Object -First 1 - $BinaryDirectory = ResolvePresetProperty $CMakePresetsJson $ConfigurePreset 'binaryDir' + $BinaryDirectory = ResolvePresetProperty $ConfigurePreset $CMakePresetsJson.configurePresets 'binaryDir' $BinaryDirectory | Should -Be '${sourceDir}/__output/${presetName}' } } diff --git a/Tests/SearchAncestors.Tests.ps1 b/Tests/SearchAncestors.Tests.ps1 new file mode 100644 index 0000000..90ed470 --- /dev/null +++ b/Tests/SearchAncestors.Tests.ps1 @@ -0,0 +1,70 @@ +#Requires -PSEdition Core + +BeforeAll { + $script:Presets = ConvertFrom-Json -InputObject @" +[ + { + "name": "Preset1" + }, + { + "name": "Preset2", + "inherits": "Preset1" + } +] +"@ + $script:Preset1 = $script:Presets | Where-Object { $_.name -eq 'Preset1' } | Select-Object -First 1 + $script:Preset2 = $script:Presets | Where-Object { $_.name -eq 'Preset2' } | Select-Object -First 1 + + . $PSScriptRoot/../PSCMake/Common/CMake.ps1 +} + +Describe 'SearchAncestors' { + It 'Returns $null when passed a $null preset' { + SearchAncestors $null $Presets { } | + Should -Be $null + } + + It 'Searches a single preset' { + $script:VisitedPresets = @() + SearchAncestors $Preset1 $Presets { + param($CurrentPreset) + $script:VisitedPresets += $CurrentPreset.name + } | + Should -Be $null + + $script:VisitedPresets | + Should -Be @('Preset1') + } + + It 'Searches a preset and its parent' { + $script:VisitedPresets = @() + SearchAncestors $Preset2 $Presets { + param($CurrentPreset) + $script:VisitedPresets += $CurrentPreset.name + } | + Should -Be $null + + $script:VisitedPresets | + Should -Be @('Preset2', 'Preset1') + } + + It 'Returns the result of a found preset' { + $Name = SearchAncestors $Preset2 $Presets { + param($CurrentPreset) + $CurrentPreset.name + } + $Name | Should -Be 'Preset2' + } + + It 'Stops searching when a result is found' { + $script:VisitedPresets = @() + $Name = SearchAncestors $Preset2 $Presets { + param($CurrentPreset) + $script:VisitedPresets += $CurrentPreset.name + $CurrentPreset.name + } + $Name | Should -Be 'Preset2' + $script:VisitedPresets | + Should -Be @('Preset2') + } +} From ea65b8d4a5b093762c5e35f7aed3a7f2755ebf82 Mon Sep 17 00:00:00 2001 From: Mark Schofield Date: Sun, 28 Sep 2025 15:42:38 -0700 Subject: [PATCH 23/37] Accommodate multiple 'inherit' values in a given preset --- PSCMake/Common/CMake.ps1 | 20 ++++++++++++++------ Tests/SearchAncestors.Tests.ps1 | 21 +++++++++++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/PSCMake/Common/CMake.ps1 b/PSCMake/Common/CMake.ps1 index 5367ddd..bfc7188 100644 --- a/PSCMake/Common/CMake.ps1 +++ b/PSCMake/Common/CMake.ps1 @@ -199,6 +199,8 @@ function ResolvePresets { .Description The action should return $null to continue searching, or a non-$null value to stop searching and return that value. + + When searching multiple preset 'inherit' values, the presets will be search in order. #> function SearchAncestors { param( @@ -206,16 +208,22 @@ function SearchAncestors { $Presets, [scriptblock] $Action ) - for (; $Preset; ) { + if ($null -eq $Preset) { + return $null + } + [array] $PendingPresets = @($Preset) + for (; ($null -ne $PendingPresets) -and ($PendingPresets.Count -gt 0); ) { + $Preset, $PendingPresets = $PendingPresets $Result = & $Action $Preset if ($null -ne $Result) { return $Result } - $BasePreset = Get-MemberValue $Preset 'inherits' - if (-not $BasePreset) { - break - } - $Preset = $Presets | Where-Object { $_.name -eq $BasePreset } | Select-Object -First 1 + [array] $BasePresets = Get-MemberValue $Preset 'inherits' -Or @() | + ForEach-Object { + $BaseParentName = $_ + $Presets | Where-Object { $_.name -eq $BaseParentName } | Select-Object -First 1 + } + $PendingPresets = $BasePresets + $PendingPresets } } diff --git a/Tests/SearchAncestors.Tests.ps1 b/Tests/SearchAncestors.Tests.ps1 index 90ed470..cf7e2e3 100644 --- a/Tests/SearchAncestors.Tests.ps1 +++ b/Tests/SearchAncestors.Tests.ps1 @@ -9,11 +9,20 @@ BeforeAll { { "name": "Preset2", "inherits": "Preset1" + }, + { + "name": "Preset3" + }, + { + "name": "Preset4", + "inherits": ["Preset2", "Preset3"] } ] "@ $script:Preset1 = $script:Presets | Where-Object { $_.name -eq 'Preset1' } | Select-Object -First 1 $script:Preset2 = $script:Presets | Where-Object { $_.name -eq 'Preset2' } | Select-Object -First 1 + $script:Preset3 = $script:Presets | Where-Object { $_.name -eq 'Preset3' } | Select-Object -First 1 + $script:Preset4 = $script:Presets | Where-Object { $_.name -eq 'Preset4' } | Select-Object -First 1 . $PSScriptRoot/../PSCMake/Common/CMake.ps1 } @@ -67,4 +76,16 @@ Describe 'SearchAncestors' { $script:VisitedPresets | Should -Be @('Preset2') } + + It 'Searches a preset and its multiple parents' { + $script:VisitedPresets = @() + SearchAncestors $Preset4 $Presets { + param($CurrentPreset) + $script:VisitedPresets += $CurrentPreset.name + } | + Should -Be $null + + $script:VisitedPresets | + Should -Be @('Preset4', 'Preset2', 'Preset1', 'Preset3') + } } From b2e7dd7ba3d346383fed0b85f479d509302e7445 Mon Sep 17 00:00:00 2001 From: Mark Schofield Date: Sun, 5 Oct 2025 14:09:48 -0700 Subject: [PATCH 24/37] Update ReferenceBuild with scoped targets and an executable target --- Tests/BuildTargetsCompleter.Tests.ps1 | 20 ++++++++++++------- Tests/ReferenceBuild.ps1 | 9 +++++++++ Tests/ReferenceBuild/CMakeLists.txt | 3 +++ .../SubDirectory/CMakeLists.txt | 7 +++++++ .../SubDirectoryOther/CMakeLists.txt | 7 +++++++ Tests/Write-CMakeBuild.Tests.ps1 | 4 ++++ 6 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 Tests/ReferenceBuild/SubDirectory/CMakeLists.txt create mode 100644 Tests/ReferenceBuild/SubDirectoryOther/CMakeLists.txt diff --git a/Tests/BuildTargetsCompleter.Tests.ps1 b/Tests/BuildTargetsCompleter.Tests.ps1 index 537a8c6..5742a65 100644 --- a/Tests/BuildTargetsCompleter.Tests.ps1 +++ b/Tests/BuildTargetsCompleter.Tests.ps1 @@ -20,13 +20,19 @@ Describe 'BuildTargetsCompleter' { Using-Location "$PSScriptRoot/ReferenceBuild" { $Completions = Get-CommandCompletion "Build-CMakeBuild -Targets " - $Completions.CompletionMatches.Count | Should -Be 6 - $Completions.CompletionMatches[0].CompletionText | Should -Be 'A_Library' - $Completions.CompletionMatches[1].CompletionText | Should -Be 'B_Library' - $Completions.CompletionMatches[2].CompletionText | Should -Be 'C_Library' - $Completions.CompletionMatches[3].CompletionText | Should -Be 'all' - $Completions.CompletionMatches[4].CompletionText | Should -Be 'clean' - $Completions.CompletionMatches[5].CompletionText | Should -Be 'install' + $Completions.CompletionMatches.Count | Should -Be 10 + $Completions.CompletionMatches.CompletionText | Should -Be @( + 'A_Library' + 'B_Library' + 'C_Library' + 'SubDirectoryOther_Executable' + 'SubDirectoryOther_Library' + 'SubDirectory_Executable' + 'SubDirectory_Library' + 'all' + 'clean' + 'install' + ) } } } diff --git a/Tests/ReferenceBuild.ps1 b/Tests/ReferenceBuild.ps1 index 0dd8f5b..476eb7d 100644 --- a/Tests/ReferenceBuild.ps1 +++ b/Tests/ReferenceBuild.ps1 @@ -41,3 +41,12 @@ function PrepareReferenceBuild() { "-DCMAKE_MAKE_PROGRAM=$CMAKE_MAKE_PROGRAM" ) } + +function GetReferenceBuildProperties() { + $BinaryDirectory = "$PSScriptRoot/ReferenceBuild/__output/windows-x64" + + [PSCustomObject]@{ + BinaryDirectory = $BinaryDirectory + CodeModelFile = Get-CMakeBuildCodeModel $BinaryDirectory + } +} diff --git a/Tests/ReferenceBuild/CMakeLists.txt b/Tests/ReferenceBuild/CMakeLists.txt index de4bf11..f737040 100644 --- a/Tests/ReferenceBuild/CMakeLists.txt +++ b/Tests/ReferenceBuild/CMakeLists.txt @@ -16,3 +16,6 @@ add_library(B_Library add_library(C_Library Reference.cpp ) + +add_subdirectory(SubDirectory) +add_subdirectory(SubDirectoryOther) \ No newline at end of file diff --git a/Tests/ReferenceBuild/SubDirectory/CMakeLists.txt b/Tests/ReferenceBuild/SubDirectory/CMakeLists.txt new file mode 100644 index 0000000..12ace25 --- /dev/null +++ b/Tests/ReferenceBuild/SubDirectory/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(SubDirectory_Executable + ../Reference.cpp +) + +add_library(SubDirectory_Library + ../Reference.cpp +) diff --git a/Tests/ReferenceBuild/SubDirectoryOther/CMakeLists.txt b/Tests/ReferenceBuild/SubDirectoryOther/CMakeLists.txt new file mode 100644 index 0000000..473e828 --- /dev/null +++ b/Tests/ReferenceBuild/SubDirectoryOther/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(SubDirectoryOther_Executable + ../Reference.cpp +) + +add_library(SubDirectoryOther_Library + ../Reference.cpp +) diff --git a/Tests/Write-CMakeBuild.Tests.ps1 b/Tests/Write-CMakeBuild.Tests.ps1 index 15444fb..14f730a 100644 --- a/Tests/Write-CMakeBuild.Tests.ps1 +++ b/Tests/Write-CMakeBuild.Tests.ps1 @@ -23,6 +23,10 @@ digraph CodeModel { "A_Library::@6890427a1f51a3e7e1df" [label="A_Library"] "B_Library::@6890427a1f51a3e7e1df" [label="B_Library"] "C_Library::@6890427a1f51a3e7e1df" [label="C_Library"] + "SubDirectoryOther_Executable::@01210d55993b56455dd6" [label="SubDirectoryOther_Executable"] + "SubDirectoryOther_Library::@01210d55993b56455dd6" [label="SubDirectoryOther_Library"] + "SubDirectory_Executable::@c68b9f6dab07fa391196" [label="SubDirectory_Executable"] + "SubDirectory_Library::@c68b9f6dab07fa391196" [label="SubDirectory_Library"] } '@ ((Write-CMakeBuild) -join '') | From bccb8afa38ba3cdc10b4b4d0a6f48cbb133e7cbb Mon Sep 17 00:00:00 2001 From: Mark Schofield Date: Sun, 5 Oct 2025 14:10:00 -0700 Subject: [PATCH 25/37] Add 'GetScopedTargets.Tests.ps1' with expected GetScopedTargets behavior --- Tests/GetScopedTargets.Tests.ps1 | 57 ++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Tests/GetScopedTargets.Tests.ps1 diff --git a/Tests/GetScopedTargets.Tests.ps1 b/Tests/GetScopedTargets.Tests.ps1 new file mode 100644 index 0000000..4c5358e --- /dev/null +++ b/Tests/GetScopedTargets.Tests.ps1 @@ -0,0 +1,57 @@ +#Requires -PSEdition Core + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +BeforeAll { + . $PSScriptRoot/TestUtilities.ps1 + . $PSScriptRoot/ReferenceBuild.ps1 + + $Properties = PrepareReferenceBuild + + $CMake = "$env:ProgramFiles/CMake/bin/cmake.exe" + & $CMake @Properties + + . $PSScriptRoot/../PSCMake/Common/CMake.ps1 + + $ReferenceBuildProperties = GetReferenceBuildProperties + $CodeModel = Get-CMakeBuildCodeModel $ReferenceBuildProperties.BinaryDirectory + $script:SourceLocation = $CodeModel.paths.source +} + +Describe 'GetScopedTargets' { + It 'Returns all targets when the ScopeLocation is the SourceLocation' { + $ScopeLocation = $SourceLocation + $Targets = GetScopedTargets $CodeModel $null $ScopeLocation + $Targets.name | + Should -Be @( + 'A_Library' + 'B_Library' + 'C_Library' + 'SubDirectoryOther_Executable' + 'SubDirectoryOther_Library' + 'SubDirectory_Executable' + 'SubDirectory_Library' + ) + } + + It 'Returns scoped targets when the ScopeLocation is a subdirectory of the SourceLocation' { + $ScopeLocation = Join-Path -Path $SourceLocation -ChildPath 'SubDirectoryOther' + $Targets = GetScopedTargets $CodeModel $null $ScopeLocation + $Targets.name | + Should -Be @( + 'SubDirectoryOther_Executable' + 'SubDirectoryOther_Library' + ) + } + + It 'Returns scoped targets when the ScopeLocation is a subdirectory of the SourceLocation, that is also a prefix of another subdirectory' { + $ScopeLocation = Join-Path -Path $SourceLocation -ChildPath 'SubDirectory' + $Targets = GetScopedTargets $CodeModel $null $ScopeLocation + $Targets.name | + Should -Be @( + 'SubDirectory_Executable' + 'SubDirectory_Library' + ) + } +} From 24c5317e47eb09af36dab99e6d76cf6940cb8906 Mon Sep 17 00:00:00 2001 From: Mark Schofield Date: Sun, 5 Oct 2025 14:10:03 -0700 Subject: [PATCH 26/37] Finding targets by scope should limit to the scoped directory --- PSCMake/Common/CMake.ps1 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/PSCMake/Common/CMake.ps1 b/PSCMake/Common/CMake.ps1 index bfc7188..9e77ca9 100644 --- a/PSCMake/Common/CMake.ps1 +++ b/PSCMake/Common/CMake.ps1 @@ -507,6 +507,10 @@ function GetScopedTargets { $Configuration, $ScopeLocation ) + function CanonicalizeDirectoryPath($Path) { + Resolve-Path -Path (Join-Path -Path $Path -ChildPath '/') + } + $ScopeLocation = CanonicalizeDirectoryPath $ScopeLocation $CodeModelConfiguration = if ($Configuration) { $CodeModel.configurations | Where-Object { $_.name -eq $Configuration } } else { @@ -521,7 +525,8 @@ function GetScopedTargets { } else { Join-Path -Path $SourceDir -ChildPath $Folder } - $Folder.StartsWith($ScopeLocation) + $Folder = CanonicalizeDirectoryPath $Folder + $Folder.Path.StartsWith($ScopeLocation.Path) } } From 7bb74356e177653a0dafec0b694f757bf628b584 Mon Sep 17 00:00:00 2001 From: Mark Schofield Date: Sun, 5 Oct 2025 16:03:30 -0700 Subject: [PATCH 27/37] Improve tests for Configure-CMakeBuild --- PSCMake/PSCMake.psm1 | 9 ++-- Tests/Configure-CMakeBuild.Tests.ps1 | 70 ++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/PSCMake/PSCMake.psm1 b/PSCMake/PSCMake.psm1 index bc707df..4fcb884 100644 --- a/PSCMake/PSCMake.psm1 +++ b/PSCMake/PSCMake.psm1 @@ -251,7 +251,8 @@ function Configure-CMakeBuild { $ConfigurePresetNames | Select-Object -First 1 } else { foreach ($CandidatePreset in $Preset) { - $ConfigurePresetNames | Where-Object { ($_ -eq $CandidatePreset) -or ($_ -like $CandidatePreset) } + $ExpandedPresets = $ConfigurePresetNames | Where-Object { $_ -like $CandidatePreset } + $ExpandedPresets ?? $CandidatePreset } } @@ -340,7 +341,8 @@ function Build-CMakeBuild { $BuildPresetNames | Select-Object -First 1 } else { foreach ($CandidatePreset in $Preset) { - $BuildPresetNames | Where-Object { ($_ -eq $CandidatePreset) -or ($_ -like $CandidatePreset) } + $ExpandedPresets = $BuildPresetNames | Where-Object { $_ -like $CandidatePreset } + $ExpandedPresets ?? $CandidatePreset } } @@ -378,7 +380,8 @@ function Build-CMakeBuild { [string[]] $ConfigurationNames = @($null) if ($Configuration) { $ConfigurationNames = foreach ($CandidateConfigurationName in $Configuration) { - $CodeModel.configurations.name | Where-Object { ($_ -eq $CandidateConfigurationName) -or ($_ -like $CandidateConfigurationName) } + $ExpandedName = $CodeModel.configurations.name | Where-Object { $_ -like $CandidateConfigurationName } + $ExpandedName ?? $CandidateConfigurationName } } diff --git a/Tests/Configure-CMakeBuild.Tests.ps1 b/Tests/Configure-CMakeBuild.Tests.ps1 index c350566..c635c95 100644 --- a/Tests/Configure-CMakeBuild.Tests.ps1 +++ b/Tests/Configure-CMakeBuild.Tests.ps1 @@ -13,12 +13,82 @@ BeforeAll { & $CMake @Properties Import-Module -Force $PSScriptRoot/../PSCMake/PSCMake.psd1 -DisableNameChecking + + # Mock subsequent calls to invoke CMake so that we don't actually try to build anything. + Mock -ModuleName PSCMake InvokeCMake { + param( + [string] $CMakePath, + [string[]] $Arguments + ) + $script:CMakeCalls += , $Arguments + } } Describe 'Configure-CMakeBuild' { + BeforeEach { + $script:CMakeCalls = @() + } + It 'Configures the build with no parameters' { Using-Location "$PSScriptRoot/ReferenceBuild" { Configure-CMakeBuild + + $script:CMakeCalls | Should -HaveCount 1 + $script:CMakeCalls[0] | Should -Be @('--preset', 'windows-x64') + } + } + + It 'Configures with --fresh when specified' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + Configure-CMakeBuild -Fresh + + $script:CMakeCalls | Should -HaveCount 1 + $script:CMakeCalls[0] | Should -Be @('--preset', 'windows-x64', '--fresh') + } + } + + It 'Configures with --verbose when specified' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + Configure-CMakeBuild -Fresh -Verbose + + $script:CMakeCalls | Should -HaveCount 1 + $script:CMakeCalls[0] | Should -Be @('--preset', 'windows-x64', '--fresh', '--log-level=VERBOSE') + } + } + + It 'Configures with a specific preset' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + Configure-CMakeBuild -Preset windows-arm + + $script:CMakeCalls | Should -HaveCount 1 + $script:CMakeCalls[0] | Should -Be @('--preset', 'windows-arm') + } + } + + It 'Configures with a multiple presets' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + Configure-CMakeBuild -Preset windows-arm,windows-x64 + + $script:CMakeCalls | Should -HaveCount 2 + $script:CMakeCalls[0] | Should -Be @('--preset', 'windows-arm') + $script:CMakeCalls[1] | Should -Be @('--preset', 'windows-x64') + } + } + + It 'Configures with a wildcard presets' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + Configure-CMakeBuild -Preset windows-* + + $script:CMakeCalls | Should -HaveCount 2 + $script:CMakeCalls[0] | Should -Be @('--preset', 'windows-x64') + $script:CMakeCalls[1] | Should -Be @('--preset', 'windows-arm') + } + } + + It 'Reports invalid presets' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + { Configure-CMakeBuild -Preset linux-x64 } | + Should -Throw "Unable to find configuration preset 'linux-x64' in $PSScriptRoot\ReferenceBuild\CMakePresets.json" } } } From 1106d1c63e4827f3990d6966f544202c5e87a282 Mon Sep 17 00:00:00 2001 From: Mark Schofield Date: Sun, 5 Oct 2025 16:03:37 -0700 Subject: [PATCH 28/37] Improve tests for Build-CMakeBuild --- Tests/Build-CMakeBuild.Tests.ps1 | 56 ++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/Tests/Build-CMakeBuild.Tests.ps1 b/Tests/Build-CMakeBuild.Tests.ps1 index 491bb89..d886001 100644 --- a/Tests/Build-CMakeBuild.Tests.ps1 +++ b/Tests/Build-CMakeBuild.Tests.ps1 @@ -39,6 +39,53 @@ Describe 'Build-CMakeBuild' { $script:CMakeCalls[0] | Should -Be @('--build', '--preset', 'windows-x64') } + It 'Builds with a single preset' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + Build-CMakeBuild -Preset windows-x64 + } + + $script:CMakeCalls | Should -HaveCount 1 + $script:CMakeCalls[0] | Should -Be @('--build', '--preset', 'windows-x64') + } + + It 'Builds a specified target' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + Build-CMakeBuild -Preset windows-x64 -Target B_Library + } + + $script:CMakeCalls | Should -HaveCount 1 + $script:CMakeCalls[0] | Should -Be @('--build', '--preset', 'windows-x64', '--target', 'B_Library') + } + + It 'Builds a multiple targets' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + Build-CMakeBuild -Preset windows-x64 -Target A_Library,B_Library + } + + $script:CMakeCalls | Should -HaveCount 1 + $script:CMakeCalls[0] | Should -Be @('--build', '--preset', 'windows-x64', '--target', 'A_Library', 'B_Library') + } + + It 'Reruns configuration with -Configure' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + Build-CMakeBuild -Preset windows-x64 -Configure + } + + $script:CMakeCalls | Should -HaveCount 2 + $script:CMakeCalls[0] | Should -Be @('--preset', 'windows-x64') + $script:CMakeCalls[1] | Should -Be @('--build', '--preset', 'windows-x64') + } + + It 'Reruns configuration with -Fresh' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + Build-CMakeBuild -Preset windows-x64 -Fresh + } + + $script:CMakeCalls | Should -HaveCount 2 + $script:CMakeCalls[0] | Should -Be @('--preset', 'windows-x64', '--fresh') + $script:CMakeCalls[1] | Should -Be @('--build', '--preset', 'windows-x64') + } + It 'Builds with wildcard presets' { Using-Location "$PSScriptRoot/ReferenceBuild" { Build-CMakeBuild -Preset '*-x64' @@ -58,4 +105,13 @@ Describe 'Build-CMakeBuild' { $script:CMakeCalls[1] | Should -Be @('--build', '--preset', 'windows-x64', '--config', 'Release') $script:CMakeCalls[2] | Should -Be @('--build', '--preset', 'windows-x64', '--config', 'RelWithDebInfo') } + + It 'Builds scoped targets' { + Using-Location "$PSScriptRoot/ReferenceBuild/SubDirectory" { + Build-CMakeBuild -Preset 'windows-x64' + } + + $CMakeCalls | Should -HaveCount 1 + $script:CMakeCalls[0] | Should -Be @('--build', '--preset', 'windows-x64', '--target', 'SubDirectory_Executable', 'SubDirectory_Library') + } } From d6b8b6a03848230756dd3949b8a7c65436618a3d Mon Sep 17 00:00:00 2001 From: Mark Schofield Date: Sun, 5 Oct 2025 18:18:47 -0700 Subject: [PATCH 29/37] Move 'InvokeCMake' --> 'InvokeExecutable' for more general mocking --- PSCMake/Common/CMake.ps1 | 16 ---------------- PSCMake/PSCMake.psm1 | 20 ++++++++++++++++++-- Tests/Build-CMakeBuild.Tests.ps1 | 2 +- Tests/Configure-CMakeBuild.Tests.ps1 | 3 ++- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/PSCMake/Common/CMake.ps1 b/PSCMake/Common/CMake.ps1 index 9e77ca9..08e259a 100644 --- a/PSCMake/Common/CMake.ps1 +++ b/PSCMake/Common/CMake.ps1 @@ -37,22 +37,6 @@ $CMakeCandidates = @( } ) -<# - .Synopsis - Invokes CMake. - - .Description - A function wrapping calls to CMake, allowing the calls to be mocked for testing. -#> -function InvokeCMake { - param( - [string] $CMakePath, - [string[]] $Arguments - ) - Write-Verbose "CMake Arguments: $Arguments" - & $CMakePath @Arguments -} - <# .Synopsis Finds the root of the CMake build - the current or ancestral folder containing a 'CMakePresets.json' file. diff --git a/PSCMake/PSCMake.psm1 b/PSCMake/PSCMake.psm1 index 4fcb884..dae6f49 100644 --- a/PSCMake/PSCMake.psm1 +++ b/PSCMake/PSCMake.psm1 @@ -30,6 +30,22 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot/Common/Common.ps1 . $PSScriptRoot/Common/Ninja.ps1 +<# + .Synopsis + Invokes an executable. + + .Description + A function wrapping calls to '&', allowing the calls to be mocked for testing. +#> +function InvokeExecutable { + param( + [string] $Path, + [string[]] $Arguments + ) + Write-Verbose "Invoking: $Path $Arguments" + & $Path @Arguments +} + <# .Synopsis An argument-completer for `Build-CMakeBuild`'s `-Preset` parameter. @@ -210,7 +226,7 @@ function ConfigureCMake { } ) - InvokeCMake $CMake $CMakeArguments + InvokeExecutable $CMake $CMakeArguments if ($LASTEXITCODE -ne 0) { Write-Error "Configuration failed. Command line: '$($CMake.Source)' $($CMakeArguments -join ' ')" } @@ -410,7 +426,7 @@ function Build-CMakeBuild { ) $StartTime = [datetime]::Now - InvokeCMake $CMake $CMakeArguments + InvokeExecutable $CMake $CMakeArguments if ($LASTEXITCODE -ne 0) { Write-Error "Build failed. Command line: '$($CMake.Source)' $($CMakeArguments -join ' ')" } diff --git a/Tests/Build-CMakeBuild.Tests.ps1 b/Tests/Build-CMakeBuild.Tests.ps1 index d886001..57b8e49 100644 --- a/Tests/Build-CMakeBuild.Tests.ps1 +++ b/Tests/Build-CMakeBuild.Tests.ps1 @@ -16,7 +16,7 @@ BeforeAll { # Mock subsequent calls to invoke CMake so that we don't actually try to build anything. $script:CMakeCalls = @() - Mock -ModuleName PSCMake InvokeCMake { + Mock -ModuleName PSCMake InvokeExecutable { param( [string] $CMakePath, [string[]] $Arguments diff --git a/Tests/Configure-CMakeBuild.Tests.ps1 b/Tests/Configure-CMakeBuild.Tests.ps1 index c635c95..a74ad90 100644 --- a/Tests/Configure-CMakeBuild.Tests.ps1 +++ b/Tests/Configure-CMakeBuild.Tests.ps1 @@ -15,11 +15,12 @@ BeforeAll { Import-Module -Force $PSScriptRoot/../PSCMake/PSCMake.psd1 -DisableNameChecking # Mock subsequent calls to invoke CMake so that we don't actually try to build anything. - Mock -ModuleName PSCMake InvokeCMake { + Mock -ModuleName PSCMake InvokeExecutable { param( [string] $CMakePath, [string[]] $Arguments ) + $null = $CMakePath $script:CMakeCalls += , $Arguments } } From c3350eebdd319ff48917ec8c30b6c67190a88126 Mon Sep 17 00:00:00 2001 From: Mark Schofield Date: Sun, 5 Oct 2025 18:18:54 -0700 Subject: [PATCH 30/37] Add tests for 'Invoke-CMakeOutput' --- PSCMake/PSCMake.psm1 | 2 +- Tests/Invoke-CMakeOutput.Tests.ps1 | 81 ++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 Tests/Invoke-CMakeOutput.Tests.ps1 diff --git a/PSCMake/PSCMake.psm1 b/PSCMake/PSCMake.psm1 index dae6f49..8f4861a 100644 --- a/PSCMake/PSCMake.psm1 +++ b/PSCMake/PSCMake.psm1 @@ -579,7 +579,7 @@ function Invoke-CMakeOutput { Write-Output "Running: $TargetPath $Arguments" Write-Output '----' - & $TargetPath @Arguments + InvokeExecutable $TargetPath $Arguments } Register-ArgumentCompleter -CommandName Invoke-CMakeOutput -ParameterName Preset -ScriptBlock $function:BuildPresetsCompleter diff --git a/Tests/Invoke-CMakeOutput.Tests.ps1 b/Tests/Invoke-CMakeOutput.Tests.ps1 new file mode 100644 index 0000000..c4c3b22 --- /dev/null +++ b/Tests/Invoke-CMakeOutput.Tests.ps1 @@ -0,0 +1,81 @@ +#Requires -PSEdition Core + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +BeforeAll { + . $PSScriptRoot/TestUtilities.ps1 + . $PSScriptRoot/ReferenceBuild.ps1 + + $Properties = PrepareReferenceBuild + + $CMake = "$env:ProgramFiles/CMake/bin/cmake.exe" + & $CMake @Properties + + Import-Module -Force $PSScriptRoot/../PSCMake/PSCMake.psd1 -DisableNameChecking + + # Mock subsequent calls to invoke CMake so that we don't actually try to build anything. + Mock -ModuleName PSCMake InvokeExecutable { + param( + [string] $Path, + [string[]] $Arguments + ) + $script:ExecutableCalls += [PSCustomObject]@{Path = $Path; Arguments = $Arguments} + } +} + +Describe 'Invoke-CMakeOutput' { + BeforeEach { + $script:ExecutableCalls = @() + } + + It 'Builds then invokes a specified target' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + Invoke-CMakeOutput -Target SubDirectory_Executable + + $script:ExecutableCalls | Should -HaveCount 2 + $script:ExecutableCalls[0].Arguments | Should -Be @('--build', '--preset', 'windows-x64', '--target', 'SubDirectory_Executable') + $script:ExecutableCalls[1].Arguments | Should -BeNullOrEmpty + $script:ExecutableCalls[1].Path | Should -Be @("$PSScriptRoot\ReferenceBuild\__output\windows-x64\SubDirectory\Debug\SubDirectory_Executable.exe") + } + } + + It 'Builds then invokes a specified target - configuration specified' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + Invoke-CMakeOutput -Target SubDirectory_Executable -Configuration Release + + $script:ExecutableCalls | Should -HaveCount 2 + $script:ExecutableCalls[0].Arguments | Should -Be @('--build', '--preset', 'windows-x64', '--config', 'Release', '--target', 'SubDirectory_Executable') + $script:ExecutableCalls[1].Arguments | Should -BeNullOrEmpty + $script:ExecutableCalls[1].Path | Should -Be @("$PSScriptRoot\ReferenceBuild\__output\windows-x64\SubDirectory\Release\SubDirectory_Executable.exe") + } + } + + It 'Passes extra parameters to the invocation' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + Invoke-CMakeOutput -Target SubDirectory_Executable chunky bacon + + $script:ExecutableCalls | Should -HaveCount 2 + $script:ExecutableCalls[0].Arguments | Should -Be @('--build', '--preset', 'windows-x64', '--target', 'SubDirectory_Executable') + $script:ExecutableCalls[1].Arguments | Should -Be @('chunky', 'bacon') + $script:ExecutableCalls[1].Path | Should -Be @("$PSScriptRoot\ReferenceBuild\__output\windows-x64\SubDirectory\Debug\SubDirectory_Executable.exe") + } + } + + It 'Runs a single in-scope executable' { + Using-Location "$PSScriptRoot/ReferenceBuild/SubDirectory" { + Invoke-CMakeOutput + + $script:ExecutableCalls | Should -HaveCount 2 + $script:ExecutableCalls[0].Arguments | Should -Be @('--build', '--preset', 'windows-x64', '--target', 'SubDirectory_Executable') + $script:ExecutableCalls[1].Arguments | Should -BeNullOrEmpty + $script:ExecutableCalls[1].Path | Should -Be @("$PSScriptRoot\ReferenceBuild\__output\windows-x64\SubDirectory\Debug\SubDirectory_Executable.exe") + } + } + + It 'Fails with multiple executable targets in scope' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + { Invoke-CMakeOutput } | Should -Throw "Multiple executable scoped targets match. Specify a target explicitly: SubDirectoryOther_Executable SubDirectory_Executable" + } + } +} From 365442c1ae627abcb00247d399831ae44b359176 Mon Sep 17 00:00:00 2001 From: Mark Schofield Date: Sun, 5 Oct 2025 18:19:01 -0700 Subject: [PATCH 31/37] Add tests for 'ExecutableTargetsCompleter' --- PSCMake/PSCMake.psm1 | 2 +- Tests/ExecutableTargetsCompleter.Tests.ps1 | 25 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 Tests/ExecutableTargetsCompleter.Tests.ps1 diff --git a/PSCMake/PSCMake.psm1 b/PSCMake/PSCMake.psm1 index 8f4861a..331861b 100644 --- a/PSCMake/PSCMake.psm1 +++ b/PSCMake/PSCMake.psm1 @@ -145,7 +145,7 @@ function BuildTargetsCompleter { <# .Synopsis - An argument-completer for `Build-CMakeBuild`'s `-Targets` parameter. + An argument-completer for `Invoke-CMakeOutput`'s `-Target` parameter. #> function ExecutableTargetsCompleter { param( diff --git a/Tests/ExecutableTargetsCompleter.Tests.ps1 b/Tests/ExecutableTargetsCompleter.Tests.ps1 new file mode 100644 index 0000000..1950d66 --- /dev/null +++ b/Tests/ExecutableTargetsCompleter.Tests.ps1 @@ -0,0 +1,25 @@ +#Requires -PSEdition Core + +BeforeAll { + . $PSScriptRoot/TestUtilities.ps1 + . $PSScriptRoot/ReferenceBuild.ps1 + + $Properties = PrepareReferenceBuild + + $CMake = "$env:ProgramFiles/CMake/bin/cmake.exe" + & $CMake @Properties + + Import-Module -Force $PSScriptRoot/../PSCMake/PSCMake.psd1 -DisableNameChecking +} + +Describe 'ExecutableTargetsCompleter' { + It 'Returns the executable targets' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + $Completions = Get-CommandCompletion "Invoke-CMakeOutput -Target " + $Completions.CompletionMatches.CompletionText | Should -Be @( + 'SubDirectoryOther_Executable', + 'SubDirectory_Executable' + ) + } + } +} From c3a31ef60ec17028ae022c1335a7e3b768fd643c Mon Sep 17 00:00:00 2001 From: Mark Schofield <34426337+MarkSchofield@users.noreply.github.com> Date: Sun, 5 Oct 2025 21:14:08 -0700 Subject: [PATCH 32/37] Add PSScriptAnalyzer settings, add Build\RunScriptAnalyzer.ps1 (#72) --- .vscode/PSScriptAnalyzerSettings.psd1 | 13 +++++++++++++ .vscode/settings.json | 8 +++++--- Build/RunScriptAnalyzer.ps1 | 18 ++++++++++++++++++ PSCMake/Common/CMake.ps1 | 6 +++--- Tests/Build-CMakeBuild.Tests.ps1 | 1 + 5 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 .vscode/PSScriptAnalyzerSettings.psd1 create mode 100644 Build/RunScriptAnalyzer.ps1 diff --git a/.vscode/PSScriptAnalyzerSettings.psd1 b/.vscode/PSScriptAnalyzerSettings.psd1 new file mode 100644 index 0000000..355e88a --- /dev/null +++ b/.vscode/PSScriptAnalyzerSettings.psd1 @@ -0,0 +1,13 @@ +@{ + Rules = @{ + PSReviewUnusedParameter = @{ + CommandsToTraverse = @( + 'SearchAncestors' + 'Using-Location' + ) + } + } + IncludeRules = @('*') + ExcludeRules = @('PSUseApprovedVerbs') +} + diff --git a/.vscode/settings.json b/.vscode/settings.json index 9f7a7f3..27a865f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,7 @@ { - "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline", - "powershell.codeFormatting.newLineAfterCloseBrace": false, - "editor.renderWhitespace": "all" + "editor.renderWhitespace": "all", + "powershell.codeFormatting.newLineAfterCloseBrace": false, + "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline", + "powershell.scriptAnalysis.enable": true, + "powershell.scriptAnalysis.settingsPath": ".vscode/PSScriptAnalyzerSettings.psd1" } diff --git a/Build/RunScriptAnalyzer.ps1 b/Build/RunScriptAnalyzer.ps1 new file mode 100644 index 0000000..f0bff31 --- /dev/null +++ b/Build/RunScriptAnalyzer.ps1 @@ -0,0 +1,18 @@ +#Requires -PSEdition Core + +[CmdletBinding()] +param( +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +$WorkingDirectory = $PSScriptRoot +$RepositoryRoot = Resolve-Path -Path (& git -C $WorkingDirectory rev-parse '--show-toplevel') +$SettingsFile = Join-Path -Path $RepositoryRoot -ChildPath '.vscode\PSScriptAnalyzerSettings.psd1' +& git -C $RepositoryRoot ls-files *.ps1 *.psm1 *.psd1 | + ForEach-Object { Get-Item -Path (Join-Path -Path $RepositoryRoot -ChildPath $_) } | + ForEach-Object { Invoke-ScriptAnalyzer -Path $_ -Settings $SettingsFile } | + ForEach-Object { + "$($_.ScriptPath):$($_.Line):$($_.Column) [$($_.RuleName)] $($_.Message)" + } diff --git a/PSCMake/Common/CMake.ps1 b/PSCMake/Common/CMake.ps1 index 08e259a..3f3c5f9 100644 --- a/PSCMake/Common/CMake.ps1 +++ b/PSCMake/Common/CMake.ps1 @@ -217,7 +217,7 @@ function ResolvePresetProperty { $Presets, $PropertyName ) - SearchAncestors $Preset $Presets { + SearchAncestors -Preset $Preset -Presets $Presets { param($CurrentPreset) Get-MemberValue -InputObject $CurrentPreset -Name $PropertyName } @@ -228,7 +228,7 @@ function EvaluatePresetCondition { $Preset, $Presets ) - $Result = SearchAncestors $Preset $Presets { + $Result = SearchAncestors -Preset $Preset -Presets $Presets { param($CurrentPreset) $PresetConditionJson = Get-MemberValue $CurrentPreset 'condition' if (($PresetConditionJson) -and @@ -306,7 +306,7 @@ function GetBinaryDirectory { $CMakePresetsJson, $ConfigurePreset ) - $BinaryDirectory = ResolvePresetProperty $ConfigurePreset $CMakePresetsJson.configurePresets 'binaryDir' + $BinaryDirectory = ResolvePresetProperty -Preset $ConfigurePreset -Presets $CMakePresetsJson.configurePresets -PropertyName 'binaryDir' # Perform macro-replacement $Result = MacroReplacement $BinaryDirectory $ConfigurePreset diff --git a/Tests/Build-CMakeBuild.Tests.ps1 b/Tests/Build-CMakeBuild.Tests.ps1 index 57b8e49..8dcb341 100644 --- a/Tests/Build-CMakeBuild.Tests.ps1 +++ b/Tests/Build-CMakeBuild.Tests.ps1 @@ -21,6 +21,7 @@ BeforeAll { [string] $CMakePath, [string[]] $Arguments ) + $null = $CMakePath $script:CMakeCalls += , $Arguments } } From b4de4cfe9ef053ce07e216f1534fe619d9f25890 Mon Sep 17 00:00:00 2001 From: Mark Schofield Date: Sun, 5 Oct 2025 21:12:54 -0700 Subject: [PATCH 33/37] Use 'Should -HaveCount' not '.Count | Should -Be' for better error messages --- Tests/BuildConfigurationsCompleter.Tests.ps1 | 4 ++-- Tests/BuildPresetsCompleter.Tests.ps1 | 4 ++-- Tests/BuildTargetsCompleter.Tests.ps1 | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/BuildConfigurationsCompleter.Tests.ps1 b/Tests/BuildConfigurationsCompleter.Tests.ps1 index a2663d8..4f7fae0 100644 --- a/Tests/BuildConfigurationsCompleter.Tests.ps1 +++ b/Tests/BuildConfigurationsCompleter.Tests.ps1 @@ -12,7 +12,7 @@ BeforeAll { Describe 'BuildConfigurationsCompleter' { It 'Returns the default configurations when no preset is specified' { $Completions = Get-CommandCompletion "Build-CMakeBuild -Configuration " - $Completions.CompletionMatches.Count | Should -Be 4 + $Completions.CompletionMatches | Should -HaveCount 4 $Completions.CompletionMatches[0].CompletionText | Should -Be 'Release' $Completions.CompletionMatches[1].CompletionText | Should -Be 'Debug' $Completions.CompletionMatches[2].CompletionText | Should -Be 'RelWithDebInfo' @@ -21,7 +21,7 @@ Describe 'BuildConfigurationsCompleter' { It 'Returns the default configurations when no preset is specified, filtered by the word to complete' { $Completions = Get-CommandCompletion "Build-CMakeBuild -Configuration D" - $Completions.CompletionMatches.Count | Should -Be 1 + $Completions.CompletionMatches | Should -HaveCount 1 $Completions.CompletionMatches[0].CompletionText | Should -Be 'Debug' } } diff --git a/Tests/BuildPresetsCompleter.Tests.ps1 b/Tests/BuildPresetsCompleter.Tests.ps1 index 10d24af..5b1909c 100644 --- a/Tests/BuildPresetsCompleter.Tests.ps1 +++ b/Tests/BuildPresetsCompleter.Tests.ps1 @@ -12,14 +12,14 @@ BeforeAll { Describe 'BuildPresetsCompleter' { It 'Returns the presets from the discovered presets file, in the order that they are defined' { $Completions = Get-CommandCompletion "Build-CMakeBuild -Preset " - $Completions.CompletionMatches.Count | Should -Be 2 + $Completions.CompletionMatches | Should -HaveCount 2 $Completions.CompletionMatches[0].CompletionText | Should -Be 'windows-x64' $Completions.CompletionMatches[1].CompletionText | Should -Be 'windows-arm' } It 'Returns the presets from the discovered presets file, filtered by the word to complete' { $Completions = Get-CommandCompletion "Build-CMakeBuild -Preset windows-a" - $Completions.CompletionMatches.Count | Should -Be 1 + $Completions.CompletionMatches | Should -HaveCount 1 $Completions.CompletionMatches[0].CompletionText | Should -Be 'windows-arm' } } diff --git a/Tests/BuildTargetsCompleter.Tests.ps1 b/Tests/BuildTargetsCompleter.Tests.ps1 index 5742a65..779b819 100644 --- a/Tests/BuildTargetsCompleter.Tests.ps1 +++ b/Tests/BuildTargetsCompleter.Tests.ps1 @@ -20,7 +20,7 @@ Describe 'BuildTargetsCompleter' { Using-Location "$PSScriptRoot/ReferenceBuild" { $Completions = Get-CommandCompletion "Build-CMakeBuild -Targets " - $Completions.CompletionMatches.Count | Should -Be 10 + $Completions.CompletionMatches | Should -HaveCount 10 $Completions.CompletionMatches.CompletionText | Should -Be @( 'A_Library' 'B_Library' From cc6a5be8c064494877009096bafad8df9cd239df Mon Sep 17 00:00:00 2001 From: Mark Schofield Date: Sun, 5 Oct 2025 21:13:22 -0700 Subject: [PATCH 34/37] Add tests for 'GetMacroConstants' --- Tests/GetMacroConstants.Tests.ps1 | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Tests/GetMacroConstants.Tests.ps1 diff --git a/Tests/GetMacroConstants.Tests.ps1 b/Tests/GetMacroConstants.Tests.ps1 new file mode 100644 index 0000000..6ddb5cd --- /dev/null +++ b/Tests/GetMacroConstants.Tests.ps1 @@ -0,0 +1,26 @@ +#Requires -PSEdition Core + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +BeforeAll { + . $PSScriptRoot/../PSCMake/Common/CMake.ps1 +} + +Describe 'GetMacroConstants' { + It 'Returns the correct macro constants' { + $HostSystemName = if ($IsWindows) { + 'Windows' + } elseif ($IsMacOS) { + 'Darwin' + } elseif ($IsLinux) { + 'Linux' + } else { + Write-Error "Unsupported `${hostSystemName} value." + } + + $MacroConstants = GetMacroConstants + $MacroConstants['${hostSystemName}'] | Should -Be $HostSystemName + $MacroConstants['$vendor{PSCMake}'] | Should -Be 'true' + } +} From 0ed524af84124ce4717a35d438920eed47cb7461 Mon Sep 17 00:00:00 2001 From: Mark Schofield Date: Sun, 5 Oct 2025 21:13:24 -0700 Subject: [PATCH 35/37] Add Build\RunTests.ps1 --- .github/workflows/ci.yaml | 16 ++-------------- .github/workflows/pull-request.yaml | 14 +------------- .vscode/settings.json | 1 + Build/RunTests.ps1 | 27 +++++++++++++++++++++++++++ 4 files changed, 31 insertions(+), 27 deletions(-) create mode 100644 Build/RunTests.ps1 diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a3eaa62..e000ab4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -24,22 +24,10 @@ jobs: - name: Invoke-Pester shell: pwsh run: | - Import-Module Pester - $Configuration = [PesterConfiguration]@{ - Run = @{ - Path = '*' - Passthru = $true - } - CodeCoverage = @{ - Enabled = $true - OutputFormat = 'JaCoCo' - OutputPath = '${{ github.workspace }}/Pester-Coverage.xml' - } - } - Invoke-Pester -Configuration $Configuration + ${{ github.workspace }}/Build/RunTests.ps1 - uses: codecov/codecov-action@v5 with: - files: '${{ github.workspace }}/Pester-Coverage.xml' + files: '${{ github.workspace }}/__output/coverage.xml' flags: unittests name: codecov-umbrella token: '${{ secrets.CODECOV_TOKEN }}' diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 79fd98b..aef6467 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -15,19 +15,7 @@ jobs: - name: Invoke-Pester shell: pwsh run: | - Import-Module Pester - $Configuration = [PesterConfiguration]@{ - Run = @{ - Path = '*' - Passthru = $true - } - CodeCoverage = @{ - Enabled = $true - OutputFormat = 'JaCoCo' - OutputPath = '${{ github.workspace }}/Pester-Coverage.xml' - } - } - Invoke-Pester -Configuration $Configuration + ${{ github.workspace }}/Build/RunTests.ps1 - name: Build shell: pwsh run: | diff --git a/.vscode/settings.json b/.vscode/settings.json index 27a865f..9a725c2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { + "coverage-gutters.coverageBaseDir": "__output", "editor.renderWhitespace": "all", "powershell.codeFormatting.newLineAfterCloseBrace": false, "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline", diff --git a/Build/RunTests.ps1 b/Build/RunTests.ps1 new file mode 100644 index 0000000..bad0c94 --- /dev/null +++ b/Build/RunTests.ps1 @@ -0,0 +1,27 @@ +#Requires -PSEdition Core + +[CmdletBinding()] +param( +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +Import-Module Pester + +$WorkingDirectory = $PSScriptRoot +$RepositoryRoot = Resolve-Path -Path (& git -C $WorkingDirectory rev-parse '--show-toplevel') +$Configuration = [PesterConfiguration]@{ + Run = @{ + Path = $RepositoryRoot + Passthru = $true + } + CodeCoverage = @{ + Enabled = $true + Path = "$RepositoryRoot/PSCMake" + OutputFormat = 'JaCoCo' + OutputPath = "$RepositoryRoot/__output/coverage.xml" + } +} + +Invoke-Pester -Configuration $Configuration From cb1125cb4c8c3e609eb73663bbf514bf9e375315 Mon Sep 17 00:00:00 2001 From: Mark Schofield <34426337+MarkSchofield@users.noreply.github.com> Date: Sun, 5 Oct 2025 21:29:47 -0700 Subject: [PATCH 36/37] Add tests for 'Write-CMakeBuild' (#74) --- Tests/ReferenceBuild.ps1 | 1 + Tests/TestUtilities.ps1 | 22 ++++++ Tests/Write-CMakeBuild.Tests.ps1 | 19 ++++- Tests/Write-CMakeBuild.dgml | 122 +++++++++++++++++++++++++++++++ Tests/XmlUtilities.ps1 | 32 ++++++++ 5 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 Tests/Write-CMakeBuild.dgml create mode 100644 Tests/XmlUtilities.ps1 diff --git a/Tests/ReferenceBuild.ps1 b/Tests/ReferenceBuild.ps1 index 476eb7d..cb32a20 100644 --- a/Tests/ReferenceBuild.ps1 +++ b/Tests/ReferenceBuild.ps1 @@ -48,5 +48,6 @@ function GetReferenceBuildProperties() { [PSCustomObject]@{ BinaryDirectory = $BinaryDirectory CodeModelFile = Get-CMakeBuildCodeModel $BinaryDirectory + SourceDirectory = (Resolve-Path -Path "$PSScriptRoot/ReferenceBuild/").Path } } diff --git a/Tests/TestUtilities.ps1 b/Tests/TestUtilities.ps1 index 6f25df1..4dc94c2 100644 --- a/Tests/TestUtilities.ps1 +++ b/Tests/TestUtilities.ps1 @@ -6,3 +6,25 @@ $ErrorActionPreference = 'Stop' function Get-CommandCompletion([string] $InputScript) { [System.Management.Automation.CommandCompletion]::CompleteInput($InputScript, $InputScript.Length, $null) } + +function Zip-Object { + $Objects = $args + + if ($Objects.Count -lt 2) { + throw "At least two collections must be specified" + } + + $First = $Objects[0] + $Objects | ForEach-Object { + if ($_.Count -ne $First.Count) { + throw "All collections must have the same number of elements" + } + } + for ($i = 0; $i -lt $First.Count; $i++) { + [array]$Current = @() + $Objects | ForEach-Object { + $Current += $_[$i] + } + (, $Current) + } +} diff --git a/Tests/Write-CMakeBuild.Tests.ps1 b/Tests/Write-CMakeBuild.Tests.ps1 index 14f730a..00058f6 100644 --- a/Tests/Write-CMakeBuild.Tests.ps1 +++ b/Tests/Write-CMakeBuild.Tests.ps1 @@ -5,9 +5,11 @@ $ErrorActionPreference = 'Stop' BeforeAll { . $PSScriptRoot/TestUtilities.ps1 + . $PSScriptRoot/XmlUtilities.ps1 . $PSScriptRoot/ReferenceBuild.ps1 $Properties = PrepareReferenceBuild + $script:BuildProperties = GetReferenceBuildProperties $CMake = "$env:ProgramFiles/CMake/bin/cmake.exe" & $CMake @Properties @@ -17,7 +19,7 @@ BeforeAll { Describe 'Write-CMakeBuild' { It 'Writes the build with no parameters' { - Using-Location "$PSScriptRoot/ReferenceBuild" { + Using-Location $BuildProperties.SourceDirectory { $ExpectedDotFile = @' digraph CodeModel { "A_Library::@6890427a1f51a3e7e1df" [label="A_Library"] @@ -33,4 +35,19 @@ digraph CodeModel { Should -Be ($ExpectedDotFile -replace '\r\n', '') } } + It 'Writes the DGML when specified' { + Using-Location $BuildProperties.SourceDirectory { + [xml]$ExpectedDgml = Get-Content "$PSScriptRoot/Write-CMakeBuild.dgml" + SortChildElements $ExpectedDgml.DirectedGraph.Links { $_.Target } + + [xml]$ActualDgml = Write-CMakeBuild -As Dgml + $ActualDgml.DirectedGraph.Nodes.Node | + Where-Object { Get-MemberValue $_ Definition } | + ForEach-Object { $_.Definition = $_.Definition.Replace($BuildProperties.SourceDirectory, "") } + SortChildElements $ActualDgml.DirectedGraph.Links { $_.Target } + + $ActualDgml.OuterXml | + Should -Be $ExpectedDgml.OuterXml + } + } } diff --git a/Tests/Write-CMakeBuild.dgml b/Tests/Write-CMakeBuild.dgml new file mode 100644 index 0000000..3603306 --- /dev/null +++ b/Tests/Write-CMakeBuild.dgml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/XmlUtilities.ps1 b/Tests/XmlUtilities.ps1 new file mode 100644 index 0000000..6d17017 --- /dev/null +++ b/Tests/XmlUtilities.ps1 @@ -0,0 +1,32 @@ +#Requires -PSEdition Core + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +$TypeAccelerators = [psobject].Assembly.GetType("System.Management.Automation.TypeAccelerators") +$TypeAccelerators::Add("xmlelement", [System.Xml.XmlElement]) + +function RemoveChildElements([xmlelement] $XmlElement) { + $ChildNodes = $XmlElement.ChildNodes + for ($Index = 0; $Index -lt $ChildNodes.Count; ) { + $ChildXmlElement = $ChildNodes.Item($Index) + if ($ChildXmlElement.NodeType -eq 'Element') { + $XmlElement.RemoveChild($ChildXmlElement) + } else { + $Index++ + } + } +} + +function AddChildElements([xmlelement]$XmlElement, [array] $ChildElements) { + $ChildElements | + ForEach-Object { + $null = $XmlElement.AppendChild($_) + } +} + +function SortChildElements([xmlelement] $LinksElement, [scriptblock] $SortExpression) { + $LinkArray = RemoveChildElements $LinksElement + $LinkArray = $LinkArray | Sort-Object $SortExpression + AddChildElements $LinksElement $LinkArray +} From 38b26429e075078da7f8eacbc29f201828dbcc6e Mon Sep 17 00:00:00 2001 From: Mark Schofield <34426337+MarkSchofield@users.noreply.github.com> Date: Mon, 6 Oct 2025 20:58:04 -0700 Subject: [PATCH 37/37] Accommodate '[' and ']' in preset names when finding the code model (#75) --- PSCMake/Common/CMake.ps1 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/PSCMake/Common/CMake.ps1 b/PSCMake/Common/CMake.ps1 index 3f3c5f9..f45faa4 100644 --- a/PSCMake/Common/CMake.ps1 +++ b/PSCMake/Common/CMake.ps1 @@ -448,7 +448,12 @@ function Get-CMakeBuildCodeModel { param( [string] $BinaryDirectory ) - Get-ChildItem -Path (Get-CMakeBuildCodeModelDirectory $BinaryDirectory) -File -Filter 'codemodel-v2-*' -ErrorAction SilentlyContinue | + + # Since BinaryDirectory may contain characters that are valid for the file-system, but are used by PowerShell's + # wildcard syntax (i.e. '[' and ']'), escape the characters before passing to Get-ChildItem. + $EscapedBinaryDirectory = $BinaryDirectory.Replace('[', '`[').Replace(']', '`]') + + Get-ChildItem -Path (Get-CMakeBuildCodeModelDirectory $EscapedBinaryDirectory) -File -Filter 'codemodel-v2-*' -ErrorAction SilentlyContinue | Select-Object -First 1 | Get-Content | ConvertFrom-Json