Skip to content

Commit 36eee08

Browse files
committed
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.
1 parent 01873c9 commit 36eee08

File tree

2 files changed

+79
-17
lines changed

2 files changed

+79
-17
lines changed

stdlib/public/SwiftShims/swift/shims/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ else()
127127
set(clang_headers_location "${LLVM_LIBRARY_OUTPUT_INTDIR}/clang/${CLANG_VERSION_MAJOR}")
128128
endif()
129129

130+
# Normalize the path.
131+
cmake_path(CONVERT "${clang_headers_location}" TO_CMAKE_PATH_LIST clang_headers_location NORMALIZE)
132+
130133
add_custom_command_target(unused_var
131134
COMMAND
132135
"${CMAKE_COMMAND}" "-E" "make_directory" "${SWIFTLIB_DIR}"

utils/build.ps1

Lines changed: 76 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,15 @@ function Get-CMake {
511511
throw "CMake not found on Path nor in the Visual Studio Installation. Please Install CMake to continue."
512512
}
513513

514+
$cmake = Get-CMake
515+
$CMakeVersionString = & $cmake --version | Select-String -Pattern 'cmake version ([\d\.]+)' | ForEach-Object { $_.Matches[0].Groups[1].Value }
516+
$CMakeVersion = [Version]$CMakeVersionString
517+
# Starting with CMake 3.30, CMake propagates linker flags to Swift.
518+
$CMakePassesSwiftLinkerFlags = $CMakeVersion -ge [version]'3.30'
519+
# CMP0181 enables support for the `LINKER:flag1,flag2,...` syntax in
520+
# `CMAKE_[EXE|SHARED|MODULE]_LINKER_FLAGS[_<CONFIG>]` variables.
521+
$CMakeSupportsCMP0181 = $CMakeVersion -ge [version]'4.0'
522+
514523
function Get-Ninja {
515524
try {
516525
return (Get-Command "Ninja.exe" -ErrorAction Stop).Source
@@ -522,7 +531,6 @@ function Get-Ninja {
522531
throw "Ninja not found on Path nor in the Visual Studio Installation. Please Install Ninja to continue."
523532
}
524533

525-
$cmake = Get-CMake
526534
$ninja = Get-Ninja
527535

528536
$NugetRoot = "$BinaryCache\nuget"
@@ -1420,9 +1428,32 @@ function Build-CMakeProject {
14201428
$UseCXX = $UseBuiltCompilers.Contains("CXX") -or $UseMSVCCompilers.Contains("CXX") -or $UsePinnedCompilers.Contains("CXX")
14211429
$UseSwift = $UseBuiltCompilers.Contains("Swift") -or $UsePinnedCompilers.Contains("Swift")
14221430

1431+
# We need to manually prefix linker flags with `-Xlinker` if we are using
1432+
# the GNU driver or if Swift is used as the linker driver.
1433+
# This is not necessary with CMake 4.0+ as CMP0181 simplifies the handling
1434+
# of linker arguments.
1435+
$PrefixLinkerFlags = if ($Platform.OS -eq [OS]::Android) {
1436+
# We pass the linker location to the driver, not to the linker.
1437+
$false
1438+
} elseif ($CMakeSupportsCMP0181) {
1439+
# Not necessary if CMP0181 is supported.
1440+
$false
1441+
} elseif ($UseGnuDriver) {
1442+
# Always necessary with the GNU driver.
1443+
$true
1444+
} else {
1445+
# Only necessary with Swift projects, when CMake is not passing the linker flags.
1446+
$UseSwift -and $CMakePassesSwiftLinkerFlags
1447+
}
1448+
14231449
# Add additional defines (unless already present)
14241450
$Defines = $Defines.Clone()
14251451

1452+
# Always enable CMP0181 if available.
1453+
if ($CMakeSupportsCMP0181) {
1454+
Add-KeyValueIfNew $Defines CMAKE_POLICY_DEFAULT_CMP0181 NEW
1455+
}
1456+
14261457
Add-KeyValueIfNew $Defines CMAKE_BUILD_TYPE Release
14271458

14281459
# Avoid specifying `CMAKE_SYSTEM_NAME` and `CMAKE_SYSTEM_PROCESSOR` on
@@ -1593,23 +1624,31 @@ function Build-CMakeProject {
15931624
@("-gnone")
15941625
}
15951626

1596-
# Disable EnC as that introduces padding in the conformance tables
1597-
$SwiftFlags += @("-Xlinker", "/INCREMENTAL:NO")
1598-
# Swift requires COMDAT folding and de-duplication
1599-
$SwiftFlags += @("-Xlinker", "/OPT:REF", "-Xlinker", "/OPT:ICF")
1627+
if (-not $CMakePassesSwiftLinkerFlags) {
1628+
# Disable EnC as that introduces padding in the conformance tables
1629+
$SwiftFlags += @("-Xlinker", "/INCREMENTAL:NO")
1630+
# Swift requires COMDAT folding and de-duplication
1631+
$SwiftFlags += @("-Xlinker", "/OPT:REF", "-Xlinker", "/OPT:ICF")
1632+
}
16001633

16011634
Add-FlagsDefine $Defines CMAKE_Swift_FLAGS $SwiftFlags
16021635
# Workaround CMake 3.26+ enabling `-wmo` by default on release builds
16031636
Add-FlagsDefine $Defines CMAKE_Swift_FLAGS_RELEASE "-O"
16041637
Add-FlagsDefine $Defines CMAKE_Swift_FLAGS_RELWITHDEBINFO "-O"
1605-
}
16061638

1607-
$LinkerFlags = if ($UseGNUDriver) {
1608-
@("-Xlinker", "/INCREMENTAL:NO", "-Xlinker", "/OPT:REF", "-Xlinker", "/OPT:ICF")
1609-
} else {
1610-
@("/INCREMENTAL:NO", "/OPT:REF", "/OPT:ICF")
1639+
if ($CMakePassesSwiftLinkerFlags) {
1640+
# CMake 3.30+ passes all linker flags to Swift as the linker driver,
1641+
# including those from the internal CMake modules files, without
1642+
# a `-Xlinker` prefix. This causes build failures as Swift cannot
1643+
# parse linker flags.
1644+
# Overwrite the release linker flags to be empty to avoid this.
1645+
Add-KeyValueIfNew $Defines CMAKE_EXE_LINKER_FLAGS_RELEASE ""
1646+
Add-KeyValueIfNew $Defines CMAKE_SHARED_LINKER_FLAGS_RELEASE ""
1647+
}
16111648
}
16121649

1650+
$LinkerFlags = @("/INCREMENTAL:NO", "/OPT:REF", "/OPT:ICF")
1651+
16131652
if ($DebugInfo) {
16141653
if ($UseASM -or $UseC -or $UseCXX) {
16151654
# Prefer `/Z7` over `/ZI`
@@ -1619,10 +1658,14 @@ function Build-CMakeProject {
16191658
Add-KeyValueIfNew $Defines CMAKE_MSVC_DEBUG_INFORMATION_FORMAT Embedded
16201659
Add-KeyValueIfNew $Defines CMAKE_POLICY_DEFAULT_CMP0141 NEW
16211660

1622-
$LinkerFlags += if ($UseGNUDriver) {
1623-
@("-Xlinker", "/DEBUG")
1624-
} else {
1625-
@("/DEBUG")
1661+
$LinkerFlags += @("/DEBUG")
1662+
1663+
# The linker flags are shared across every language, and `/IGNORE:longsections` is an
1664+
# `lld-link.exe` argument, not `link.exe`, so this can only be enabled when we use
1665+
# `lld-link.exe` for linking.
1666+
# TODO: Investigate supporting fission with PE/COFF, this should avoid this warning.
1667+
if ($SwiftDebugFormat -eq "dwarf" -and -not ($UseMSVCCompilers.Contains("C") -or $UseMSVCCompilers.Contains("CXX"))) {
1668+
$LinkerFlags += @("/IGNORE:longsections")
16261669
}
16271670

16281671
# The linker flags are shared across every language, and `/IGNORE:longsections` is an
@@ -1772,11 +1815,27 @@ function Build-CMakeProject {
17721815
# Single token value, no need to quote spaces, the splat operator does the right thing.
17731816
$Value = $Define.Value.Replace("\", "/")
17741817
} else {
1818+
# Linker flags are escaped differently, depending on the CMake version.
1819+
$IsLinkerFlag = $Define.Key -match "_LINKER_FLAGS" -and ($Platform.OS -ne [OS]::Android)
1820+
17751821
# Flags array, multiple tokens, quoting needed for tokens containing spaces
1776-
$Value = ""
1822+
$Value = if ($IsLinkerFlag) {
1823+
if ($CMakeSupportsCMP0181) { "LINKER:" } elseif ($PrefixLinkerFlags) { "-Xlinker " } else { "" }
1824+
} else {
1825+
""
1826+
}
1827+
$Separator = if ($IsLinkerFlag) {
1828+
if ($CMakeSupportsCMP0181) { "," } elseif ($PrefixLinkerFlags) { " -Xlinker " } else { " " }
1829+
} else {
1830+
" "
1831+
}
1832+
1833+
$FirstArg = $true
17771834
foreach ($Arg in $Define.Value) {
1778-
if ($Value.Length -gt 0) {
1779-
$Value += " "
1835+
if ($FirstArg) {
1836+
$FirstArg = $false
1837+
} else {
1838+
$Value += $Separator
17801839
}
17811840

17821841
$ArgWithForwardSlashes = $Arg.Replace("\", "/")

0 commit comments

Comments
 (0)