Common/Includes.ps1
|
#---------------------------------------------------------------------------------------------------------------------- # MIT License # # 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 # 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' <# .Synopsis Reads the toolchains-v1 File API reply for the given binary directory. #> function Get-CMakeBuildToolchains { param( [string] $BinaryDirectory ) Get-ChildItem -LiteralPath (Get-CMakeBuildCodeModelDirectory $BinaryDirectory) -File -Filter 'toolchains-v1-*' -ErrorAction SilentlyContinue | Select-Object -First 1 | Get-Content | ConvertFrom-Json } <# .Synopsis Resolves the compiler path, ID, and base argument list for a given source file. .Description Looks up the compile group for $SourceFilePath in the code model, determines the compiler ID (preferring CMAKE_<LANG>_COMPILER_FRONTEND_VARIANT when present), and builds the fragment/include/define arguments. Returns a PSCustomObject with CompilerId, CompilerPath, CompilerArgs, and BuildDir, or $null if the source file was not found in any target. #> function GetCompilerInvocationForSource { param( $CodeModel, $Toolchains, [string] $BinaryDirectory, [string] $Configuration, [string] $SourceFilePath ) $CompileInfo = GetCompileInfoForSource $CodeModel $BinaryDirectory $Configuration $SourceFilePath if (-not $CompileInfo) { return $null } $Toolchain = $Toolchains.toolchains | Where-Object { $_.language -eq $CompileInfo.Language } | Select-Object -First 1 if (-not $Toolchain) { Write-Error "No toolchain found for language '$($CompileInfo.Language)'." } # Prefer CMAKE_<LANG>_COMPILER_FRONTEND_VARIANT when set; it reflects the actual flag syntax # (e.g. clang-cl reports 'MSVC' here even though the compiler id is 'Clang'). $Language = $CompileInfo.Language $CompilerFrontendId = GetCacheValue $BinaryDirectory "CMAKE_$($Language)_COMPILER_FRONTEND_VARIANT" $CompilerId = if ($CompilerFrontendId) { $CompilerFrontendId } else { Get-MemberValue $Toolchain.compiler 'id' } if ($CompilerId -notin @('MSVC', 'Clang', 'AppleClang')) { Write-Error "Compiler '$CompilerId' is not supported by PSCMake. Supported compilers: MSVC, Clang, AppleClang." } $CompilerArgs = @() $CompilerArgs += $CompileInfo.Fragments | ForEach-Object { $_.fragment -split '\s+' } | Where-Object { $_ } $CompilerArgs += if ($CompilerId -eq 'MSVC') { $CompileInfo.Includes | ForEach-Object { "/I"; $_.path } } else { $CompileInfo.Includes | ForEach-Object { "-I"; $_.path } } $CompilerArgs += if ($CompilerId -eq 'MSVC') { $CompileInfo.Defines | ForEach-Object { "/D$($_.define)" } } else { $CompileInfo.Defines | ForEach-Object { "-D$($_.define)" } } [PSCustomObject]@{ CompilerId = $CompilerId CompilerPath = $Toolchain.compiler.path CompilerArgs = $CompilerArgs BuildDir = $CompileInfo.BuildDir } } <# .Synopsis Searches all targets in the given code model configuration for the named source file and returns its compile group information. .Outputs A PSCustomObject with Language, Fragments, Includes, Defines, and BuildDir properties, or $null if not found. #> function GetCompileInfoForSource { param( $CodeModel, [string] $BinaryDirectory, [string] $Configuration, [string] $SourceFilePath ) $ReplyDir = Get-CMakeBuildCodeModelDirectory $BinaryDirectory $SourceDir = $CodeModel.paths.source $CodeModelConfig = if ($Configuration) { $CodeModel.configurations | Where-Object { $_.name -eq $Configuration } } else { $CodeModel.configurations[0] } foreach ($TargetRef in $CodeModelConfig.targets) { $TargetJson = Get-Content (Join-Path -Path $ReplyDir -ChildPath $TargetRef.jsonFile) | ConvertFrom-Json foreach ($Source in (Get-MemberValue $TargetJson 'sources' -Or @())) { $CompileGroupIndex = Get-MemberValue $Source 'compileGroupIndex' if ($null -eq $CompileGroupIndex) { continue } $RawSourcePath = if ([System.IO.Path]::IsPathRooted($Source.path)) { $Source.path } else { Join-Path -Path $SourceDir -ChildPath $Source.path } $FullSourcePath = [System.IO.Path]::GetFullPath($RawSourcePath) if ($FullSourcePath -ieq $SourceFilePath) { $CompileGroup = $TargetJson.compileGroups[$CompileGroupIndex] $RawBuildDir = Join-Path -Path $CodeModel.paths.build -ChildPath $TargetJson.paths.build return [PSCustomObject]@{ Language = $CompileGroup.language Fragments = Get-MemberValue $CompileGroup 'compileCommandFragments' -Or @() Includes = Get-MemberValue $CompileGroup 'includes' -Or @() Defines = Get-MemberValue $CompileGroup 'defines' -Or @() BuildDir = [System.IO.Path]::GetFullPath($RawBuildDir) } } } } return $null } <# .Synopsis Parses MSVC /showIncludes output lines into a flat list of include nodes with Depth and Path properties. MSVC format: "Note: including file: <N spaces><path>" where N spaces = include depth (1 = directly included). #> function ParseMSVCIncludes { param( [string[]] $Output ) foreach ($Line in $Output) { if ($Line -match '^Note: including file:( +)(.+)$') { [PSCustomObject]@{ PSTypeName = 'PSCMake.IncludeNode' Depth = $Matches[1].Length Path = $Matches[2].TrimEnd() } } } } <# .Synopsis Parses Clang -H output lines into a flat list of include nodes with Depth and Path properties. Clang format: "<N dots> <path>" where N dots = include depth (1 = directly included). #> function ParseClangIncludes { param( [string[]] $Output ) foreach ($Line in $Output) { if ($Line -match '^(\.+) (.+)$') { [PSCustomObject]@{ PSTypeName = 'PSCMake.IncludeNode' Depth = $Matches[1].Length Path = $Matches[2].TrimEnd() } } } } |