PSCMake.psm1
#---------------------------------------------------------------------------------------------------------------------- # MIT License # # Copyright (c) 2021 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 # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. #---------------------------------------------------------------------------------------------------------------------- #Requires -PSEdition Core Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' . $PSScriptRoot/Common/CMake.ps1 . $PSScriptRoot/Common/Common.ps1 . $PSScriptRoot/Common/Ninja.ps1 <# .Synopsis An argument-completer for `Build-CMakeBuild`'s `-Preset` parameter. #> function BuildPresetsCompleter { param( $CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters ) $CMakePresetsJson = GetCMakePresets -Silent GetBuildPresetNames $CMakePresetsJson | Where-Object { $_ -ilike "$WordToComplete*" } } <# .Synopsis An argument-completer for `Build-CMakeBuild`'s `-Configurations` parameter. #> function BuildConfigurationsCompleter { param( $CommandName, $ParameterName, $WordToComplete, $CommandAst, $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. # * If not, look at the configure preset and see if CMAKE_CONFIGURATION_TYPES is set, and use that array. # * Otherwise default to Release, Debug, RelWithDebInfo, MinSizeRel @( 'Release' 'Debug' 'RelWithDebInfo' 'MinSizeRel' ) | Where-Object { $_ -ilike "$WordToComplete*" } } <# .Synopsis An argument-completer for `Build-CMakeBuild`'s `-Targets` parameter. #> function BuildTargetsCompleter { 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 $TargetNames = $ConfigurationsJson.targets.name $TargetNames | Where-Object { $_ -ilike "$WordToComplete*" } } <# .Synopsis An argument-completer for `Configure-CMakeBuild`'s `-Presets` parameter. #> function ConfigurePresetsCompleter { param( $CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters ) $CMakePresetsJson = GetCMakePresets -Silent GetConfigurePresetNames $CMakePresetsJson | Where-Object { $_ -ilike "$WordToComplete*" } } <# .Synopsis Configures a CMake build. .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. .Example # Configure the 'windows-x64' and 'windows-x86' CMake builds. Configure-CMakeBuild -Presets windows-x64,windows-x86 #> function Configure-CMakeBuild { [CmdletBinding()] param( [Parameter()] [string[]] $Presets ) $CMakeRoot = FindCMakeRoot $CMakePresetsJson = GetCMakePresets $PresetNames = GetConfigurePresetNames $CMakePresetsJson if (-not $Presets) { $Presets = $PresetNames | Select-Object -First 1 Write-Information "No preset specified, defaulting to: $Presets" } $CMake = GetCMake Using-Location $CMakeRoot { foreach ($Preset in $Presets) { Write-Output "Preset : $Presets" $ConfigurePreset = $CMakePresetsJson.configurePresets | Where-Object { $_.name -eq $Preset } if (-not $ConfigurePreset) { Write-Error "Unable to find configuration preset '$Preset' in $script:CMakePresetsPath" } $BinaryDirectory = GetBinaryDirectory $CMakePresetsJson $ConfigurePreset Enable-CMakeBuildQuery $BinaryDirectory $CMakeArguments = @( '--preset', $Preset if ($VerbosePreference) { '--log-level=VERBOSE' } ) Write-Verbose "CMake Arguments: $CMakeArguments" & $CMake @CMakeArguments } } } <# .Synopsis Builds a CMake build. .Description Builds the specified 'buildPresets' entries from a CMakePresets.json file in the current-or-higher folder. .Parameter Presets .Parameter Configurations .Parameter Targets One or more .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. .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 '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()] param( [Parameter(Position = 0)] [string[]] $Presets, [Parameter(Position = 1)] [string[]] $Configurations = @($null), [Parameter(Position = 2)] [string[]] $Targets, [Parameter()] [switch] $Configure, [Parameter()] [switch] $Report ) $CMakeRoot = FindCMakeRoot $CMakePresetsJson = GetCMakePresets $PresetNames = GetBuildPresetNames $CMakePresetsJson if (-not $Presets) { if (-not $PresetNames) { Write-Error "No Presets values specified, and one could not be inferred." } $Presets = $PresetNames | Select-Object -First 1 } # 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)) $ScopeLocation = (Get-Location).Path $CMake = GetCMake Using-Location $CMakeRoot { foreach ($Preset in $Presets) { $BuildPreset, $ConfigurePreset = ResolvePresets $CMakePresetsJson 'buildPresets' $Preset $BinaryDirectory = GetBinaryDirectory $CMakePresetsJson $ConfigurePreset $CMakeCacheFile = Join-Path -Path $BinaryDirectory -ChildPath 'CMakeCache.txt' # Run CMake configure if; # 1) "$BinaryDirectory/CMakeCache.txt" doesn't exist # 2) '-configure' was specified if ((-not (Test-Path -Path $CMakeCacheFile -PathType Leaf)) -or $Configure) { Configure-CMakeBuild -Presets $ConfigurePreset.name } else { Write-Output "Preset : $Presets" } if ($ScopedBuild) { $CodeModel = Get-CMakeBuildCodeModel $BinaryDirectory $CodeModelConfiguration = $CodeModel.configurations | Where-Object { $_.name -eq 'Release' } $TargetTuples = $CodeModelConfiguration.targets | ForEach-Object { [pscustomobject]@{ Name=$_.name Folder=$CodeModelConfiguration.directories[$_.directoryIndex].build } } | Where-Object { $_.Folder -ne '.' } | Where-Object { (Join-Path -Path $CMakeRoot -ChildPath $_.Folder).StartsWith($ScopeLocation) } if ($TargetTuples) { $Targets = $TargetTuples.Name Write-Output "Scoped Targets : $Targets" } } $CMakeArguments = @( '--build' '--preset', $Preset if ($Targets) { '--target', $Targets } ) Write-Verbose "CMake Arguments: $CMakeArguments" foreach ($Configuration in $Configurations) { Write-Output "Configuration : $Configuration" $StartTime = [datetime]::Now & $CMake @CMakeArguments (($Configuration)?('--config', $Configuration):$null) if ($Report) { Report-NinjaBuild (Join-Path $BinaryDirectory '.ninja_log') $StartTime } } } } } function Write-CMakeBuild { param( [Parameter(Position = 0)] [string] $Preset, [Parameter(Position = 1)] [string] $Configuration, [ValidateSet('dot','dgml')] [string] $As = 'dot' ) $CMakePresetsJson = GetCMakePresets $PresetNames = GetBuildPresetNames $CMakePresetsJson if (-not $Preset) { if (-not $PresetNames) { Write-Error "No Preset values specified, and one could not be inferred." } $Preset = $PresetNames | Select-Object -First 1 Write-Information "No preset specified, defaulting to: $Preset" } $BuildPreset, $ConfigurePreset = ResolvePresets $CMakePresetsJson 'buildPresets' $Preset $BinaryDirectory = GetBinaryDirectory $CMakePresetsJson $ConfigurePreset $CodeModel = Get-CMakeBuildCodeModel $BinaryDirectory $CodeModelDirectory = Get-CMakeBuildCodeModelDirectory $BinaryDirectory if (-not $Configuration) { $Configuration = 'Debug' } if ($As -eq 'dot') { WriteDot $Configuration $CodeModel $CodeModelDirectory } elseif ($As -eq 'dgml') { WriteDgml $Configuration $CodeModel $CodeModelDirectory } } 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 Configure-CMakeBuild -ParameterName Presets -ScriptBlock $function:ConfigurePresetsCompleter Register-ArgumentCompleter -CommandName Write-CMakeBuild -ParameterName Preset -ScriptBlock $function:BuildPresetsCompleter Register-ArgumentCompleter -CommandName Write-CMakeBuild -ParameterName Configuration -ScriptBlock $function:BuildConfigurationsCompleter |