From 36eee08f063dedf0bd3d7e6912bc8ac19d22a8cf Mon Sep 17 00:00:00 2001 From: Fabrice de Gans Date: Fri, 29 Aug 2025 09:52:14 -0700 Subject: [PATCH 1/2] utils: Support multiple CMake versions in build.ps1 CMake 3.30 passes linker flags to the Swift invocation when it is used as the linker driver. However, Swift cannot parse the linker arguments, so they need to be passed with the `-Xlinker` prefix. This cannot be done uncondtionally as the same arguments are also used with the MSVC toolchain. CMake 4.0 introduces the CMP0181 policy, which allows CMake to parse the `LINKER:` syntax on the linker flags passed on the command line, which simplifies handling of flags. Note that building for Android still requires CMake 3.29 for the time being as we pass a linker driver argument to the CMake linker flags to override the linker used. --- .../SwiftShims/swift/shims/CMakeLists.txt | 3 + utils/build.ps1 | 93 +++++++++++++++---- 2 files changed, 79 insertions(+), 17 deletions(-) diff --git a/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt b/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt index d664cddcd614a..0de28bc414bb4 100644 --- a/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt +++ b/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt @@ -127,6 +127,9 @@ else() set(clang_headers_location "${LLVM_LIBRARY_OUTPUT_INTDIR}/clang/${CLANG_VERSION_MAJOR}") endif() +# Normalize the path. +cmake_path(CONVERT "${clang_headers_location}" TO_CMAKE_PATH_LIST clang_headers_location NORMALIZE) + add_custom_command_target(unused_var COMMAND "${CMAKE_COMMAND}" "-E" "make_directory" "${SWIFTLIB_DIR}" diff --git a/utils/build.ps1 b/utils/build.ps1 index 76951ed1392a5..fb0b2257751b5 100644 --- a/utils/build.ps1 +++ b/utils/build.ps1 @@ -511,6 +511,15 @@ function Get-CMake { throw "CMake not found on Path nor in the Visual Studio Installation. Please Install CMake to continue." } +$cmake = Get-CMake +$CMakeVersionString = & $cmake --version | Select-String -Pattern 'cmake version ([\d\.]+)' | ForEach-Object { $_.Matches[0].Groups[1].Value } +$CMakeVersion = [Version]$CMakeVersionString +# Starting with CMake 3.30, CMake propagates linker flags to Swift. +$CMakePassesSwiftLinkerFlags = $CMakeVersion -ge [version]'3.30' +# CMP0181 enables support for the `LINKER:flag1,flag2,...` syntax in +# `CMAKE_[EXE|SHARED|MODULE]_LINKER_FLAGS[_]` variables. +$CMakeSupportsCMP0181 = $CMakeVersion -ge [version]'4.0' + function Get-Ninja { try { return (Get-Command "Ninja.exe" -ErrorAction Stop).Source @@ -522,7 +531,6 @@ function Get-Ninja { throw "Ninja not found on Path nor in the Visual Studio Installation. Please Install Ninja to continue." } -$cmake = Get-CMake $ninja = Get-Ninja $NugetRoot = "$BinaryCache\nuget" @@ -1420,9 +1428,32 @@ function Build-CMakeProject { $UseCXX = $UseBuiltCompilers.Contains("CXX") -or $UseMSVCCompilers.Contains("CXX") -or $UsePinnedCompilers.Contains("CXX") $UseSwift = $UseBuiltCompilers.Contains("Swift") -or $UsePinnedCompilers.Contains("Swift") + # We need to manually prefix linker flags with `-Xlinker` if we are using + # the GNU driver or if Swift is used as the linker driver. + # This is not necessary with CMake 4.0+ as CMP0181 simplifies the handling + # of linker arguments. + $PrefixLinkerFlags = if ($Platform.OS -eq [OS]::Android) { + # We pass the linker location to the driver, not to the linker. + $false + } elseif ($CMakeSupportsCMP0181) { + # Not necessary if CMP0181 is supported. + $false + } elseif ($UseGnuDriver) { + # Always necessary with the GNU driver. + $true + } else { + # Only necessary with Swift projects, when CMake is not passing the linker flags. + $UseSwift -and $CMakePassesSwiftLinkerFlags + } + # Add additional defines (unless already present) $Defines = $Defines.Clone() + # Always enable CMP0181 if available. + if ($CMakeSupportsCMP0181) { + Add-KeyValueIfNew $Defines CMAKE_POLICY_DEFAULT_CMP0181 NEW + } + Add-KeyValueIfNew $Defines CMAKE_BUILD_TYPE Release # Avoid specifying `CMAKE_SYSTEM_NAME` and `CMAKE_SYSTEM_PROCESSOR` on @@ -1593,23 +1624,31 @@ function Build-CMakeProject { @("-gnone") } - # Disable EnC as that introduces padding in the conformance tables - $SwiftFlags += @("-Xlinker", "/INCREMENTAL:NO") - # Swift requires COMDAT folding and de-duplication - $SwiftFlags += @("-Xlinker", "/OPT:REF", "-Xlinker", "/OPT:ICF") + if (-not $CMakePassesSwiftLinkerFlags) { + # Disable EnC as that introduces padding in the conformance tables + $SwiftFlags += @("-Xlinker", "/INCREMENTAL:NO") + # Swift requires COMDAT folding and de-duplication + $SwiftFlags += @("-Xlinker", "/OPT:REF", "-Xlinker", "/OPT:ICF") + } Add-FlagsDefine $Defines CMAKE_Swift_FLAGS $SwiftFlags # Workaround CMake 3.26+ enabling `-wmo` by default on release builds Add-FlagsDefine $Defines CMAKE_Swift_FLAGS_RELEASE "-O" Add-FlagsDefine $Defines CMAKE_Swift_FLAGS_RELWITHDEBINFO "-O" - } - $LinkerFlags = if ($UseGNUDriver) { - @("-Xlinker", "/INCREMENTAL:NO", "-Xlinker", "/OPT:REF", "-Xlinker", "/OPT:ICF") - } else { - @("/INCREMENTAL:NO", "/OPT:REF", "/OPT:ICF") + if ($CMakePassesSwiftLinkerFlags) { + # CMake 3.30+ passes all linker flags to Swift as the linker driver, + # including those from the internal CMake modules files, without + # a `-Xlinker` prefix. This causes build failures as Swift cannot + # parse linker flags. + # Overwrite the release linker flags to be empty to avoid this. + Add-KeyValueIfNew $Defines CMAKE_EXE_LINKER_FLAGS_RELEASE "" + Add-KeyValueIfNew $Defines CMAKE_SHARED_LINKER_FLAGS_RELEASE "" + } } + $LinkerFlags = @("/INCREMENTAL:NO", "/OPT:REF", "/OPT:ICF") + if ($DebugInfo) { if ($UseASM -or $UseC -or $UseCXX) { # Prefer `/Z7` over `/ZI` @@ -1619,10 +1658,14 @@ function Build-CMakeProject { Add-KeyValueIfNew $Defines CMAKE_MSVC_DEBUG_INFORMATION_FORMAT Embedded Add-KeyValueIfNew $Defines CMAKE_POLICY_DEFAULT_CMP0141 NEW - $LinkerFlags += if ($UseGNUDriver) { - @("-Xlinker", "/DEBUG") - } else { - @("/DEBUG") + $LinkerFlags += @("/DEBUG") + + # The linker flags are shared across every language, and `/IGNORE:longsections` is an + # `lld-link.exe` argument, not `link.exe`, so this can only be enabled when we use + # `lld-link.exe` for linking. + # TODO: Investigate supporting fission with PE/COFF, this should avoid this warning. + if ($SwiftDebugFormat -eq "dwarf" -and -not ($UseMSVCCompilers.Contains("C") -or $UseMSVCCompilers.Contains("CXX"))) { + $LinkerFlags += @("/IGNORE:longsections") } # The linker flags are shared across every language, and `/IGNORE:longsections` is an @@ -1772,11 +1815,27 @@ function Build-CMakeProject { # Single token value, no need to quote spaces, the splat operator does the right thing. $Value = $Define.Value.Replace("\", "/") } else { + # Linker flags are escaped differently, depending on the CMake version. + $IsLinkerFlag = $Define.Key -match "_LINKER_FLAGS" -and ($Platform.OS -ne [OS]::Android) + # Flags array, multiple tokens, quoting needed for tokens containing spaces - $Value = "" + $Value = if ($IsLinkerFlag) { + if ($CMakeSupportsCMP0181) { "LINKER:" } elseif ($PrefixLinkerFlags) { "-Xlinker " } else { "" } + } else { + "" + } + $Separator = if ($IsLinkerFlag) { + if ($CMakeSupportsCMP0181) { "," } elseif ($PrefixLinkerFlags) { " -Xlinker " } else { " " } + } else { + " " + } + + $FirstArg = $true foreach ($Arg in $Define.Value) { - if ($Value.Length -gt 0) { - $Value += " " + if ($FirstArg) { + $FirstArg = $false + } else { + $Value += $Separator } $ArgWithForwardSlashes = $Arg.Replace("\", "/") From 9dbf81a3407f95c47aaa11d9c459ff2bbf861bbc Mon Sep 17 00:00:00 2001 From: Fabrice de Gans Date: Tue, 16 Sep 2025 13:30:10 -0400 Subject: [PATCH 2/2] Join flags arguments --- utils/build.ps1 | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/utils/build.ps1 b/utils/build.ps1 index fb0b2257751b5..5d86579fd1914 100644 --- a/utils/build.ps1 +++ b/utils/build.ps1 @@ -1815,10 +1815,21 @@ function Build-CMakeProject { # Single token value, no need to quote spaces, the splat operator does the right thing. $Value = $Define.Value.Replace("\", "/") } else { - # Linker flags are escaped differently, depending on the CMake version. - $IsLinkerFlag = $Define.Key -match "_LINKER_FLAGS" -and ($Platform.OS -ne [OS]::Android) - # Flags array, multiple tokens, quoting needed for tokens containing spaces + $EscapedArgs = $Define.Value | ForEach-Object { + $Arg = $_.Replace("\", "/") + if ($Arg.Contains(" ")) { + # Escape the quote so it makes it through. PowerShell 5 and Core + # handle quotes differently, so we need to check the version. + $quote = if ($PSEdition -eq "Core") { '"' } else { '\"' } + "$quote$Arg$quote" + } else { + $Arg + } + } + + # Linker flags are handled differently depending on the CMake version. + $IsLinkerFlag = $Define.Key -match "_LINKER_FLAGS" -and ($Platform.OS -ne [OS]::Android) $Value = if ($IsLinkerFlag) { if ($CMakeSupportsCMP0181) { "LINKER:" } elseif ($PrefixLinkerFlags) { "-Xlinker " } else { "" } } else { @@ -1830,26 +1841,8 @@ function Build-CMakeProject { " " } - $FirstArg = $true - foreach ($Arg in $Define.Value) { - if ($FirstArg) { - $FirstArg = $false - } else { - $Value += $Separator - } - - $ArgWithForwardSlashes = $Arg.Replace("\", "/") - if ($ArgWithForwardSlashes.Contains(" ")) { - # Escape the quote so it makes it through. PowerShell 5 and Core - # handle quotes differently, so we need to check the version. - $quote = if ($PSEdition -eq "Core") { '"' } else { '\"' } - $Value += "$quote$ArgWithForwardSlashes$quote" - } else { - $Value += $ArgWithForwardSlashes - } - } + $Value += $EscapedArgs -join $Separator } - $cmakeGenerateArgs += @("-D", "$($Define.Key)=$Value") }