Skip to content

Commit 3c0feb3

Browse files
committed
Fix addPlatformSpecificLinkerArgs for WindowsToolchain
1 parent 8b7b0df commit 3c0feb3

File tree

3 files changed

+145
-186
lines changed

3 files changed

+145
-186
lines changed

Sources/SwiftDriver/Jobs/Toolchain+LinkerSupport.swift

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,6 @@ extension Toolchain {
5454
for triple: Triple,
5555
parsedOptions: inout ParsedOptions
5656
) throws -> AbsolutePath {
57-
/// Use the one in `VCTools` if exists on Windows.
58-
if triple.isWindows,
59-
let vctools = env["VCToolsInstallDir"],
60-
let root = try? AbsolutePath(validating: vctools) {
61-
let archName: String = {
62-
switch triple.arch {
63-
case .aarch64: return "arm64"
64-
case .arm: return "arm"
65-
case .x86: return "x86"
66-
case nil, .x86_64: return "x64"
67-
default: fatalError("unknown arch \(triple.archName) on Windows")
68-
}
69-
}()
70-
return root.appending(components: "lib", archName)
71-
}
7257
return try computeResourceDirPath(for: triple,
7358
parsedOptions: &parsedOptions,
7459
isShared: true)

Sources/SwiftDriver/Jobs/WindowsToolchain+LinkerSupport.swift

Lines changed: 133 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -25,185 +25,158 @@ extension WindowsToolchain {
2525
targetInfo: FrontendTargetInfo
2626
) throws -> AbsolutePath {
2727
let targetTriple = targetInfo.target.triple
28+
2829
switch linkerOutputType {
2930
case .dynamicLibrary:
30-
commandLine.appendFlags("-parse-as-library", "-emit-library")
31+
commandLine.appendFlags("-Xlinker", "-dll")
32+
fallthrough
3133
case .executable:
3234
if !targetTriple.triple.isEmpty {
3335
commandLine.appendFlag("-target")
3436
commandLine.appendFlag(targetTriple.triple)
3537
}
36-
commandLine.appendFlag("-emit-executable")
37-
default:
38-
break
39-
}
40-
41-
switch linkerOutputType {
42-
case .staticLibrary:
43-
commandLine.append(.joinedOptionAndPath("-out:", outputFile))
44-
commandLine.append(contentsOf: inputs.map { .path($0.file) })
45-
if commandLine.contains(.flag("-use-ld=lld")) {
46-
return try lookup(executable: "llvm-lib.exe")
47-
}
48-
return try getToolPath(.staticLinker)
49-
// TODO: Check for `-use-ld=lld`.
50-
default:
51-
// Configure the toolchain.
52-
//
53-
// By default use the system `clang` to perform the link. We use `clang` for
54-
// the driver here because we do not wish to select a particular C++ runtime.
55-
// Furthermore, until C++ interop is enabled, we cannot have a dependency on
56-
// C++ code from pure Swift code. If linked libraries are C++ based, they
57-
// should properly link C++. In the case of static linking, the user can
58-
// explicitly specify the C++ runtime to link against. This is particularly
59-
// important for platforms like android where as it is a Linux platform, the
60-
// default C++ runtime is `libstdc++` which is unsupported on the target but
61-
// as the builds are usually cross-compiled from Linux, libstdc++ is going to
62-
// be present. This results in linking the wrong version of libstdc++
63-
// generating invalid binaries. It is also possible to use different C++
64-
// runtimes than the default C++ runtime for the platform (e.g. libc++ on
65-
// Windows rather than msvcprt). When C++ interop is enabled, we will need to
66-
// surface this via a driver flag. For now, opt for the simpler approach of
67-
// just using `clang` and avoid a dependency on the C++ runtime.
68-
var clangPath = try getToolPath(.clang)
69-
if let toolsDirPath = parsedOptions.getLastArgument(.toolsDirectory) {
70-
// FIXME: What if this isn't an absolute path?
71-
let toolsDir = try AbsolutePath(validating: toolsDirPath.asSingle)
72-
73-
// If there is a clang in the toolchain folder, use that instead.
74-
if let tool = lookupExecutablePath(filename: "clang.exe", searchPaths: [toolsDir]) {
75-
clangPath = tool
76-
}
77-
78-
// Look for binutils in the toolchain folder.
79-
commandLine.appendFlag("-B")
80-
commandLine.appendPath(toolsDir)
38+
// Configure the toolchain.
39+
//
40+
// By default use the system `clang` to perform the link. We use `clang` for
41+
// the driver here because we do not wish to select a particular C++ runtime.
42+
// Furthermore, until C++ interop is enabled, we cannot have a dependency on
43+
// C++ code from pure Swift code. If linked libraries are C++ based, they
44+
// should properly link C++. In the case of static linking, the user can
45+
// explicitly specify the C++ runtime to link against. This is particularly
46+
// important for platforms like android where as it is a Linux platform, the
47+
// default C++ runtime is `libstdc++` which is unsupported on the target but
48+
// as the builds are usually cross-compiled from Linux, libstdc++ is going to
49+
// be present. This results in linking the wrong version of libstdc++
50+
// generating invalid binaries. It is also possible to use different C++
51+
// runtimes than the default C++ runtime for the platform (e.g. libc++ on
52+
// Windows rather than msvcprt). When C++ interop is enabled, we will need to
53+
// surface this via a driver flag. For now, opt for the simpler approach of
54+
// just using `clang` and avoid a dependency on the C++ runtime.
55+
var clangPath = try getToolPath(.clang)
56+
if let toolsDirPath = parsedOptions.getLastArgument(.toolsDirectory) {
57+
// FIXME: What if this isn't an absolute path?
58+
let toolsDir = try AbsolutePath(validating: toolsDirPath.asSingle)
59+
60+
// If there is a clang in the toolchain folder, use that instead.
61+
if let tool = lookupExecutablePath(filename: "clang.exe", searchPaths: [toolsDir]) {
62+
clangPath = tool
8163
}
8264

83-
let staticStdlib = parsedOptions.hasFlag(positive: .staticStdlib,
84-
negative: .noStaticStdlib,
85-
default: false)
86-
let staticExecutable = parsedOptions.hasFlag(positive: .staticExecutable,
87-
negative: .noStaticExecutable,
88-
default: false)
89-
let hasRuntimeArgs = !(staticStdlib || staticExecutable)
90-
91-
let runtimePaths = try runtimeLibraryPaths(
92-
for: targetTriple,
93-
parsedOptions: &parsedOptions,
94-
sdkPath: sdkPath,
95-
isShared: hasRuntimeArgs
96-
)
97-
98-
let sharedResourceDirPath = try computeResourceDirPath(
99-
for: targetTriple,
100-
parsedOptions: &parsedOptions,
101-
isShared: true
102-
)
103-
104-
let swiftrtPath = sharedResourceDirPath.appending(
105-
components: "windows", targetTriple.archName, "swiftrt.obj"
106-
)
107-
commandLine.appendPath(swiftrtPath)
108-
109-
let inputFiles: [Job.ArgTemplate] = inputs.compactMap { input in
110-
// Autolink inputs are handled specially
111-
if input.type == .autolink {
112-
return .responseFilePath(input.file)
113-
} else if input.type == .object {
114-
return .path(input.file)
115-
} else {
116-
return nil
117-
}
118-
}
119-
commandLine.append(contentsOf: inputFiles)
120-
121-
let fSystemArgs = parsedOptions.arguments(for: .F, .Fsystem)
122-
for opt in fSystemArgs {
123-
if opt.option == .Fsystem {
124-
commandLine.appendFlag("-iframework")
125-
} else {
126-
commandLine.appendFlag(.F)
127-
}
128-
commandLine.appendPath(try VirtualPath(path: opt.argument.asSingle))
129-
}
130-
131-
// Add the runtime library link paths.
132-
for path in runtimePaths {
133-
commandLine.appendFlag(.L)
134-
commandLine.appendPath(path)
135-
}
65+
// Look for binutils in the toolchain folder.
66+
commandLine.appendFlag("-B")
67+
commandLine.appendPath(toolsDir)
68+
}
13669

137-
// Link the standard library. In two paths, we do this using a .lnk file
138-
// if we're going that route, we'll set `linkFilePath` to the path to that
139-
// file.
140-
var linkFilePath: AbsolutePath? = try computeResourceDirPath(
141-
for: targetTriple,
142-
parsedOptions: &parsedOptions,
143-
isShared: false
144-
)
145-
146-
if staticExecutable {
147-
linkFilePath = linkFilePath?.appending(component: "static-executable-args.lnk")
148-
} else if staticStdlib {
149-
linkFilePath = linkFilePath?.appending(component: "static-stdlib-args.lnk")
70+
let linker: String
71+
if let arg = parsedOptions.getLastArgument(.useLd) {
72+
linker = arg.asSingle
73+
} else {
74+
linker = "link"
75+
}
76+
commandLine.appendFlag("-fuse-ld=\(linker)")
77+
78+
let staticStdlib = parsedOptions.hasFlag(positive: .staticStdlib,
79+
negative: .noStaticStdlib,
80+
default: false)
81+
let staticExecutable = parsedOptions.hasFlag(positive: .staticExecutable,
82+
negative: .noStaticExecutable,
83+
default: false)
84+
let hasRuntimeArgs = !(staticStdlib || staticExecutable)
85+
86+
let runtimePaths = try runtimeLibraryPaths(
87+
for: targetTriple,
88+
parsedOptions: &parsedOptions,
89+
sdkPath: sdkPath,
90+
isShared: hasRuntimeArgs
91+
)
92+
93+
let sharedResourceDirPath = try computeResourceDirPath(
94+
for: targetTriple,
95+
parsedOptions: &parsedOptions,
96+
isShared: true
97+
)
98+
99+
let swiftrtPath = sharedResourceDirPath.appending(
100+
components: targetTriple.archName, "swiftrt.obj"
101+
)
102+
commandLine.appendPath(swiftrtPath)
103+
104+
let inputFiles: [Job.ArgTemplate] = inputs.compactMap { input in
105+
// Autolink inputs are handled specially
106+
if input.type == .autolink {
107+
return .responseFilePath(input.file)
108+
} else if input.type == .object {
109+
return .path(input.file)
150110
} else {
151-
linkFilePath = nil
152-
commandLine.appendFlag("-lswiftCore")
111+
return nil
153112
}
113+
}
114+
commandLine.append(contentsOf: inputFiles)
154115

155-
if let linkFile = linkFilePath {
156-
guard fileSystem.isFile(linkFile) else {
157-
fatalError("\(linkFile.pathString) not found")
158-
}
159-
commandLine.append(.responseFilePath(.absolute(linkFile)))
116+
let fSystemArgs = parsedOptions.arguments(for: .F, .Fsystem)
117+
for opt in fSystemArgs {
118+
if opt.option == .Fsystem {
119+
commandLine.appendFlag("-iframework")
120+
} else {
121+
commandLine.appendFlag(.F)
160122
}
123+
commandLine.appendPath(try VirtualPath(path: opt.argument.asSingle))
124+
}
161125

162-
// Explicitly pass the target to the linker
163-
commandLine.appendFlag("--target=\(targetTriple.triple)")
164-
165-
// Delegate to Clang for sanitizers. It will figure out the correct linker
166-
// options.
167-
if linkerOutputType == .executable && !sanitizers.isEmpty {
168-
let sanitizerNames = sanitizers
169-
.map { $0.rawValue }
170-
.sorted() // Sort so we get a stable, testable order
171-
.joined(separator: ",")
172-
commandLine.appendFlag("-fsanitize=\(sanitizerNames)")
173-
174-
// The TSan runtime depends on the blocks runtime and libdispatch.
175-
if sanitizers.contains(.thread) {
176-
commandLine.appendFlag("-lBlocksRuntime")
177-
commandLine.appendFlag("-ldispatch")
178-
}
179-
}
126+
// Add the runtime library link paths.
127+
for path in runtimePaths {
128+
commandLine.appendFlag(.L)
129+
commandLine.appendPath(path)
130+
}
180131

181-
if parsedOptions.hasArgument(.profileGenerate) {
182-
let libProfile = sharedResourceDirPath
183-
.parentDirectory // remove platform name
184-
.appending(components: "clang", "lib", targetTriple.osName,
185-
"libclangrt_profile-\(targetTriple.archName).a")
186-
commandLine.appendPath(libProfile)
132+
if hasRuntimeArgs {
133+
commandLine.appendFlag("-lswiftCore")
134+
}
187135

188-
// HACK: Hard-coded from llvm::getInstrProfRuntimeHookVarName()
189-
commandLine.appendFlag("-u__llvm_profile_runtime")
136+
// Explicitly pass the target to the linker
137+
commandLine.appendFlags("-target", targetTriple.triple)
138+
139+
// Delegate to Clang for sanitizers. It will figure out the correct linker
140+
// options.
141+
if linkerOutputType == .executable && !sanitizers.isEmpty {
142+
let sanitizerNames = sanitizers
143+
.map { $0.rawValue }
144+
.sorted() // Sort so we get a stable, testable order
145+
.joined(separator: ",")
146+
commandLine.appendFlag("-fsanitize=\(sanitizerNames)")
147+
148+
// The TSan runtime depends on the blocks runtime and libdispatch.
149+
if sanitizers.contains(.thread) {
150+
commandLine.appendFlag("-lBlocksRuntime")
151+
commandLine.appendFlag("-ldispatch")
190152
}
153+
}
154+
155+
if parsedOptions.hasArgument(.profileGenerate) {
156+
let libProfile = try clangLibraryPath(for: targetTriple, parsedOptions: &parsedOptions)
157+
.appending(components: "clang_rt.profile-\(archName(for: targetTriple)).lib")
158+
commandLine.appendPath(libProfile)
159+
}
191160

192-
// Run clang++ in verbose mode if "-v" is set
193-
try commandLine.appendLast(.v, from: &parsedOptions)
194-
195-
// These custom arguments should be right before the object file at the
196-
// end.
197-
try commandLine.append(
198-
contentsOf: parsedOptions.arguments(in: .linkerOption)
199-
)
200-
try commandLine.appendAllArguments(.Xlinker, from: &parsedOptions)
201-
try commandLine.appendAllArguments(.XclangLinker, from: &parsedOptions)
202-
203-
// This should be the last option, for convenience in checking output.
204-
commandLine.appendFlag(.o)
205-
commandLine.appendPath(outputFile)
206-
return clangPath
161+
// Run clang++ in verbose mode if "-v" is set
162+
try commandLine.appendLast(.v, from: &parsedOptions)
163+
164+
// These custom arguments should be right before the object file at the
165+
// end.
166+
try commandLine.append(
167+
contentsOf: parsedOptions.arguments(in: .linkerOption)
168+
)
169+
try commandLine.appendAllArguments(.Xlinker, from: &parsedOptions)
170+
try commandLine.appendAllArguments(.XclangLinker, from: &parsedOptions)
171+
172+
// This should be the last option, for convenience in checking output.
173+
commandLine.appendFlag(.o)
174+
commandLine.appendPath(outputFile)
175+
return clangPath
176+
case .staticLibrary:
177+
commandLine.append(.joinedOptionAndPath("-out:", outputFile))
178+
commandLine.append(contentsOf: inputs.map { .path($0.file) })
179+
return try getToolPath(.staticLinker)
207180
}
208181
}
209182
}

Sources/SwiftDriver/Toolchains/WindowsToolchain.swift

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ public final class WindowsToolchain: Toolchain {
2727
/// Doubles as path cache and point for overriding normal lookup
2828
private var toolPaths = [Tool: AbsolutePath]()
2929

30+
public func archName(for triple: Triple) -> String {
31+
switch triple.arch {
32+
case .aarch64: return "aarch64"
33+
case .arm: return "armv7"
34+
case .x86: return "i386"
35+
case nil, .x86_64: return "x86_64"
36+
default: fatalError("unknown arch \(triple.archName) on Windows")
37+
}
38+
}
39+
3040
public init(env: [String: String], executor: DriverExecutor, fileSystem: FileSystem = localFileSystem) {
3141
self.env = env
3242
self.executor = executor
@@ -37,7 +47,7 @@ public final class WindowsToolchain: Toolchain {
3747
switch type {
3848
case .executable: return "\(moduleName).exe"
3949
case .dynamicLibrary: return "\(moduleName).dll"
40-
case .staticLibrary: return "\(moduleName).lib"
50+
case .staticLibrary: return "lib\(moduleName).lib"
4151
}
4252
}
4353

@@ -92,15 +102,6 @@ public final class WindowsToolchain: Toolchain {
92102
targetTriple: Triple,
93103
isShared: Bool
94104
) throws -> String {
95-
let archName: String = {
96-
switch targetTriple.arch {
97-
case .aarch64: return "aarch64"
98-
case .arm: return "armv7"
99-
case .x86: return "i386"
100-
case nil, .x86_64: return "x86_64"
101-
default: fatalError("unknown arch \(targetTriple.archName) on Windows")
102-
}
103-
}()
104-
return "clang_rt.\(sanitizer.libraryName)-\(archName).lib"
105+
return "clang_rt.\(sanitizer.libraryName)-\(archName(for: targetTriple)).lib"
105106
}
106107
}

0 commit comments

Comments
 (0)