Skip to content

Commit 9e9ce5f

Browse files
committed
Implement WindowsToolchain
1 parent e9b27e8 commit 9e9ce5f

File tree

7 files changed

+358
-8
lines changed

7 files changed

+358
-8
lines changed

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,7 +1702,7 @@ extension Triple {
17021702
case .freeBSD, .haiku:
17031703
return GenericUnixToolchain.self
17041704
case .win32:
1705-
fatalError("Windows target not supported yet")
1705+
return WindowsToolchain.self
17061706
default:
17071707
diagnosticsEngine.emit(.error_unknown_target(triple))
17081708
throw Diagnostics.fatalError
@@ -1715,7 +1715,7 @@ extension Driver {
17151715
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
17161716
static let defaultToolchainType: Toolchain.Type = DarwinToolchain.self
17171717
#elseif os(Windows)
1718-
static let defaultToolchainType: Toolchain.Type = { fatalError("Windows target not supported yet") }()
1718+
static let defaultToolchainType: Toolchain.Type = WindowsToolchain.self
17191719
#else
17201720
static let defaultToolchainType: Toolchain.Type = GenericUnixToolchain.self
17211721
#endif

Sources/SwiftDriver/Jobs/CommandLineArguments.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,10 @@ extension Array where Element == Job.ArgTemplate {
165165
var joinedArguments: String {
166166
return self.map {
167167
switch $0 {
168-
case .flag(let string):
169-
return string.spm_shellEscaped()
170-
case .path(let path):
171-
return path.name.spm_shellEscaped()
168+
case .flag(let string):
169+
return string.spm_shellEscaped()
170+
case .path(let path):
171+
return path.name.spm_shellEscaped()
172172
case .responseFilePath(let path):
173173
return "@\(path.name.spm_shellEscaped())"
174174
case let .joinedOptionAndPath(option, path):

Sources/SwiftDriver/Jobs/Toolchain+InterpreterSupport.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,14 @@ extension GenericUnixToolchain {
8181
return envVars
8282
}
8383
}
84+
85+
extension WindowsToolchain {
86+
public func platformSpecificInterpreterEnvironmentVariables(
87+
env: [String : String],
88+
parsedOptions: inout ParsedOptions,
89+
sdkPath: String?,
90+
targetTriple: Triple) throws -> [String: String] {
91+
// TODO: See whether Windows needs `platformSpecificInterpreterEnvironmentVariables`
92+
return [:]
93+
}
94+
}

Sources/SwiftDriver/Jobs/Toolchain+LinkerSupport.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ extension Toolchain {
3030
resourceDirBase = sdkPath
3131
.appending(components: "usr", "lib",
3232
isShared ? "swift" : "swift_static")
33+
} else if triple.isWindows,
34+
let SDKROOT = env["SDKROOT"],
35+
let sdkPath = try? AbsolutePath(validating: SDKROOT) {
36+
resourceDirBase = sdkPath
37+
.appending(components: "usr", "lib",
38+
isShared ? "swift" : "swift_static")
3339
} else {
3440
resourceDirBase = try getToolPath(.swiftCompiler)
3541
.parentDirectory // remove /swift
@@ -48,6 +54,21 @@ extension Toolchain {
4854
for triple: Triple,
4955
parsedOptions: inout ParsedOptions
5056
) throws -> AbsolutePath {
57+
/// Find
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, .aarch64_32: 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+
}
5172
return try computeResourceDirPath(for: triple,
5273
parsedOptions: &parsedOptions,
5374
isShared: true)
@@ -258,3 +279,5 @@ extension DarwinToolchain {
258279
}
259280

260281
}
282+
283+
// TODO: See whether Windows needs `addArgsToLinkStdlib`.
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
//===--------------- GenericUnixToolchain+LinkerSupport.swift -------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
import TSCBasic
13+
import SwiftOptions
14+
15+
extension WindowsToolchain {
16+
public func addPlatformSpecificLinkerArgs(
17+
to commandLine: inout [Job.ArgTemplate],
18+
parsedOptions: inout ParsedOptions,
19+
linkerOutputType: LinkOutputType,
20+
inputs: [TypedVirtualPath],
21+
outputFile: VirtualPath,
22+
shouldUseInputFileList: Bool,
23+
sdkPath: String?,
24+
sanitizers: Set<Sanitizer>,
25+
targetInfo: FrontendTargetInfo
26+
) throws -> AbsolutePath {
27+
let targetTriple = targetInfo.target.triple
28+
switch linkerOutputType {
29+
case .dynamicLibrary:
30+
// Same options as an executable, just with '-shared'
31+
commandLine.appendFlags("-parse-as-library", "-emit-library")
32+
fallthrough
33+
case .executable:
34+
if !targetTriple.triple.isEmpty {
35+
commandLine.appendFlag("-target")
36+
commandLine.appendFlag(targetTriple.triple)
37+
}
38+
commandLine.appendFlag("-emit-executable")
39+
default:
40+
break
41+
}
42+
43+
switch linkerOutputType {
44+
case .staticLibrary:
45+
commandLine.append(.joinedOptionAndPath("-out:", outputFile))
46+
commandLine.append(contentsOf: inputs.map { .path($0.file) })
47+
return try getToolPath(.staticLinker)
48+
default:
49+
// Configure the toolchain.
50+
//
51+
// By default use the system `clang` to perform the link. We use `clang` for
52+
// the driver here because we do not wish to select a particular C++ runtime.
53+
// Furthermore, until C++ interop is enabled, we cannot have a dependency on
54+
// C++ code from pure Swift code. If linked libraries are C++ based, they
55+
// should properly link C++. In the case of static linking, the user can
56+
// explicitly specify the C++ runtime to link against. This is particularly
57+
// important for platforms like android where as it is a Linux platform, the
58+
// default C++ runtime is `libstdc++` which is unsupported on the target but
59+
// as the builds are usually cross-compiled from Linux, libstdc++ is going to
60+
// be present. This results in linking the wrong version of libstdc++
61+
// generating invalid binaries. It is also possible to use different C++
62+
// runtimes than the default C++ runtime for the platform (e.g. libc++ on
63+
// Windows rather than msvcprt). When C++ interop is enabled, we will need to
64+
// surface this via a driver flag. For now, opt for the simpler approach of
65+
// just using `clang` and avoid a dependency on the C++ runtime.
66+
var clangPath = try getToolPath(.clang)
67+
if let toolsDirPath = parsedOptions.getLastArgument(.toolsDirectory) {
68+
// FIXME: What if this isn't an absolute path?
69+
let toolsDir = try AbsolutePath(validating: toolsDirPath.asSingle)
70+
71+
// If there is a clang in the toolchain folder, use that instead.
72+
if let tool = lookupExecutablePath(filename: "clang.exe", searchPaths: [toolsDir]) {
73+
clangPath = tool
74+
}
75+
76+
// Look for binutils in the toolchain folder.
77+
commandLine.appendFlag("-B")
78+
commandLine.appendPath(toolsDir)
79+
}
80+
81+
let staticStdlib = parsedOptions.hasFlag(positive: .staticStdlib,
82+
negative: .noStaticStdlib,
83+
default: false)
84+
let staticExecutable = parsedOptions.hasFlag(positive: .staticExecutable,
85+
negative: .noStaticExecutable,
86+
default: false)
87+
let hasRuntimeArgs = !(staticStdlib || staticExecutable)
88+
89+
let runtimePaths = try runtimeLibraryPaths(
90+
for: targetTriple,
91+
parsedOptions: &parsedOptions,
92+
sdkPath: sdkPath,
93+
isShared: hasRuntimeArgs
94+
)
95+
96+
if hasRuntimeArgs && targetTriple.environment != .android {
97+
// FIXME: We probably shouldn't be adding an rpath here unless we know
98+
// ahead of time the standard library won't be copied.
99+
for path in runtimePaths {
100+
commandLine.appendFlag(.Xlinker)
101+
commandLine.appendFlag("-rpath")
102+
commandLine.appendFlag(.Xlinker)
103+
commandLine.appendPath(path)
104+
}
105+
}
106+
107+
let sharedResourceDirPath = try computeResourceDirPath(
108+
for: targetTriple,
109+
parsedOptions: &parsedOptions,
110+
isShared: true
111+
)
112+
113+
let swiftrtPath = sharedResourceDirPath
114+
.appending(
115+
components: "x86_64", "swiftrt.o"
116+
)
117+
commandLine.appendPath(swiftrtPath)
118+
119+
let inputFiles: [Job.ArgTemplate] = inputs.compactMap { input in
120+
// Autolink inputs are handled specially
121+
if input.type == .autolink {
122+
return .responseFilePath(input.file)
123+
} else if input.type == .object {
124+
return .path(input.file)
125+
} else {
126+
return nil
127+
}
128+
}
129+
commandLine.append(contentsOf: inputFiles)
130+
131+
let fSystemArgs = parsedOptions.arguments(for: .F, .Fsystem)
132+
for opt in fSystemArgs {
133+
if opt.option == .Fsystem {
134+
commandLine.appendFlag("-iframework")
135+
} else {
136+
commandLine.appendFlag(.F)
137+
}
138+
commandLine.appendPath(try VirtualPath(path: opt.argument.asSingle))
139+
}
140+
141+
// Add the runtime library link paths.
142+
for path in runtimePaths {
143+
commandLine.appendFlag(.L)
144+
commandLine.appendPath(path)
145+
}
146+
147+
// Link the standard library. In two paths, we do this using a .lnk file
148+
// if we're going that route, we'll set `linkFilePath` to the path to that
149+
// file.
150+
var linkFilePath: AbsolutePath? = try computeResourceDirPath(
151+
for: targetTriple,
152+
parsedOptions: &parsedOptions,
153+
isShared: false
154+
)
155+
156+
if staticExecutable {
157+
linkFilePath = linkFilePath?.appending(component: "static-executable-args.lnk")
158+
} else if staticStdlib {
159+
linkFilePath = linkFilePath?.appending(component: "static-stdlib-args.lnk")
160+
} else {
161+
linkFilePath = nil
162+
commandLine.appendFlag("-lswiftCore")
163+
}
164+
165+
if let linkFile = linkFilePath {
166+
guard fileSystem.isFile(linkFile) else {
167+
fatalError("\(linkFile.pathString) not found")
168+
}
169+
commandLine.append(.responseFilePath(.absolute(linkFile)))
170+
}
171+
172+
// Explicitly pass the target to the linker
173+
commandLine.appendFlag("--target=\(targetTriple.triple)")
174+
175+
// Delegate to Clang for sanitizers. It will figure out the correct linker
176+
// options.
177+
if linkerOutputType == .executable && !sanitizers.isEmpty {
178+
let sanitizerNames = sanitizers
179+
.map { $0.rawValue }
180+
.sorted() // Sort so we get a stable, testable order
181+
.joined(separator: ",")
182+
commandLine.appendFlag("-fsanitize=\(sanitizerNames)")
183+
184+
// The TSan runtime depends on the blocks runtime and libdispatch.
185+
if sanitizers.contains(.thread) {
186+
commandLine.appendFlag("-lBlocksRuntime")
187+
commandLine.appendFlag("-ldispatch")
188+
}
189+
}
190+
191+
if parsedOptions.hasArgument(.profileGenerate) {
192+
let libProfile = sharedResourceDirPath
193+
.parentDirectory // remove platform name
194+
.appending(components: "clang", "lib", targetTriple.osName,
195+
"libclangrt_profile-\(targetTriple.archName).a")
196+
commandLine.appendPath(libProfile)
197+
198+
// HACK: Hard-coded from llvm::getInstrProfRuntimeHookVarName()
199+
commandLine.appendFlag("-u__llvm_profile_runtime")
200+
}
201+
202+
// Run clang++ in verbose mode if "-v" is set
203+
try commandLine.appendLast(.v, from: &parsedOptions)
204+
205+
// These custom arguments should be right before the object file at the
206+
// end.
207+
try commandLine.append(
208+
contentsOf: parsedOptions.arguments(in: .linkerOption)
209+
)
210+
try commandLine.appendAllArguments(.Xlinker, from: &parsedOptions)
211+
try commandLine.appendAllArguments(.XclangLinker, from: &parsedOptions)
212+
213+
// This should be the last option, for convenience in checking output.
214+
commandLine.appendFlag(.o)
215+
commandLine.appendPath(outputFile)
216+
return clangPath
217+
}
218+
219+
}
220+
}

Sources/SwiftDriver/Toolchains/Toolchain.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ extension Toolchain {
135135
return path
136136
} else if let path = try? xcrunFind(executable: executable) {
137137
return path
138-
} else if !["swift-frontend", "swift"].contains(executable),
138+
} else if !["swift-frontend", "swift", "swift-frontend.exe", "swift.exe"].contains(executable),
139139
let parentDirectory = try? getToolPath(.swiftCompiler).parentDirectory,
140140
parentDirectory != executableDir,
141141
let path = lookupExecutablePath(filename: executable, searchPaths: [parentDirectory]) {
@@ -144,9 +144,11 @@ extension Toolchain {
144144
return path
145145
} else if let path = lookupExecutablePath(filename: executable, searchPaths: searchPaths) {
146146
return path
147+
// Temporary shim: fall back to looking for "swift" before failing.
147148
} else if executable == "swift-frontend" {
148-
// Temporary shim: fall back to looking for "swift" before failing.
149149
return try lookup(executable: "swift")
150+
} else if executable == "swift-frontend.exe" {
151+
return try lookup(executable: "swift.exe")
150152
} else if fallbackToExecutableDefaultPath {
151153
return AbsolutePath("/usr/bin/" + executable)
152154
} else {

0 commit comments

Comments
 (0)