Skip to content

Commit dc2fed2

Browse files
committed
Add support for testable executables on Windows
This uses the /ALTERNATENAME flag to link.exe, which is roughly equivalent to -alias/--defsym. This has been verified to work in a sample project. See https://devblogs.microsoft.com/oldnewthing/20200731-00/ for more info. Closes #6367
1 parent 0b23073 commit dc2fed2

File tree

2 files changed

+22
-2
lines changed

2 files changed

+22
-2
lines changed

Sources/Build/BuildPlan/BuildPlan.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,32 @@ extension BuildParameters {
151151
args = ["-alias", "_\(target.c99name)_main", "_main"]
152152
case .elf:
153153
args = ["--defsym", "main=\(target.c99name)_main"]
154+
case .coff:
155+
// If the user is specifying a custom entry point name that isn't "main", assume they may be setting WinMain or wWinMain
156+
// and don't do any modifications ourselves. In that case the linker will infer the WINDOWS subsystem and call WinMainCRTStartup,
157+
// which will then call the custom entry point. And WinMain/wWinMain != main, so this still won't run into duplicate symbol
158+
// issues when called from a test target, which always uses main.
159+
if let customEntryPointFunctionName = findCustomEntryPointFunctionName(of: target), customEntryPointFunctionName != "main" {
160+
return nil
161+
}
162+
args = ["/ALTERNATENAME:main=\(target.c99name)_main", "/SUBSYSTEM:CONSOLE"]
154163
default:
155164
return nil
156165
}
157166
return args.asSwiftcLinkerFlags()
158167
}
159168

169+
private func findCustomEntryPointFunctionName(of target: ResolvedModule) -> String? {
170+
let flags = createScope(for: target).evaluate(.OTHER_SWIFT_FLAGS)
171+
var it = flags.makeIterator()
172+
while let value = it.next() {
173+
if value == "-Xfrontend" && it.next() == "-entry-point-function-name" && it.next() == "-Xfrontend" {
174+
return it.next()
175+
}
176+
}
177+
return nil
178+
}
179+
160180
/// Returns the scoped view of build settings for a given target.
161181
func createScope(for target: ResolvedModule) -> BuildSettings.Scope {
162182
BuildSettings.Scope(target.underlying.buildSettings, environment: buildEnvironment)

Tests/BuildTests/BuildPlanTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3875,7 +3875,7 @@ class BuildPlanTestCase: BuildSystemProviderTestCase {
38753875
observabilityScope: observability.topScope
38763876
))
38773877
}
3878-
let supportingTriples: [Basics.Triple] = [.x86_64Linux, .x86_64MacOS]
3878+
let supportingTriples: [Basics.Triple] = [.x86_64Linux, .x86_64MacOS, .x86_64Windows]
38793879
for triple in supportingTriples {
38803880
let result = try await createResult(for: triple)
38813881
let exe = try result.moduleBuildDescription(for: "exe").swift().compileArguments()
@@ -3884,7 +3884,7 @@ class BuildPlanTestCase: BuildSystemProviderTestCase {
38843884
XCTAssertMatch(linkExe, [.contains("exe_main")])
38853885
}
38863886

3887-
let unsupportingTriples: [Basics.Triple] = [.wasi, .windows]
3887+
let unsupportingTriples: [Basics.Triple] = [.wasi]
38883888
for triple in unsupportingTriples {
38893889
let result = try await createResult(for: triple)
38903890
let exe = try result.moduleBuildDescription(for: "exe").swift().compileArguments()

0 commit comments

Comments
 (0)