Skip to content

Commit e794943

Browse files
committed
SwiftDriver: initial (incomplete) windows port
This is sufficient to build on Windows, and the resulting driver is able to somewhat function. The tests are still not usable due to assumptions about path which do not hold and the insufficiency of the path representation in tools-support-core. This is likely best approached by replacing the use of `Path` from t-s-c with the representation in system. However, this allows some attempt to validate the swift-driver which is increasingly becoming required for s-p-m, and thus unblocks further work.
1 parent dc27691 commit e794943

File tree

8 files changed

+349
-17
lines changed

8 files changed

+349
-17
lines changed

Sources/SwiftDriver/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,14 @@ add_library(SwiftDriver
8686
Jobs/VerifyDebugInfoJob.swift
8787
Jobs/VerifyModuleInterfaceJob.swift
8888
Jobs/WebAssemblyToolchain+LinkerSupport.swift
89+
Jobs/WindowsToolchain+LinkerSupport.swift
8990
Jobs/PrebuiltModulesJob.swift
9091

9192
Toolchains/DarwinToolchain.swift
9293
Toolchains/GenericUnixToolchain.swift
9394
Toolchains/Toolchain.swift
9495
Toolchains/WebAssemblyToolchain.swift
96+
Toolchains/WindowsToolchain.swift
9597

9698
Utilities/DOTJobGraphSerializer.swift
9799
Utilities/DateAdditions.swift

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ import TSCUtility
1414
import Foundation
1515
import SwiftOptions
1616

17+
private func executable(_ name: String) -> String {
18+
#if os(Windows)
19+
if name.suffix(from: name.index(name.endIndex, offsetBy: -4)) == ".exe" {
20+
return name
21+
}
22+
return "\(name).exe"
23+
#else
24+
return name
25+
#endif
26+
}
27+
1728
/// The Swift driver.
1829
public struct Driver {
1930
public enum Error: Swift.Error, Equatable, DiagnosticData {
@@ -787,7 +798,7 @@ extension Driver {
787798
let execName = try VirtualPath(path: args[0]).basenameWithoutExt
788799

789800
// If we are not run as 'swift' or 'swiftc' or there are no program arguments, always invoke as normal.
790-
guard execName == "swift" || execName == "swiftc", args.count > 1 else {
801+
guard execName == executable("swift") || execName == executable("swiftc"), args.count > 1 else {
791802
return (.normal(isRepl: false), args)
792803
}
793804

@@ -797,13 +808,13 @@ extension Driver {
797808

798809
// Check for flags associated with frontend tools.
799810
if firstArg == "-frontend" {
800-
updatedArgs.replaceSubrange(0...1, with: ["swift-frontend"])
801-
return (.subcommand("swift-frontend"), updatedArgs)
811+
updatedArgs.replaceSubrange(0...1, with: [executable("swift-frontend")])
812+
return (.subcommand(executable("swift-frontend")), updatedArgs)
802813
}
803814

804815
if firstArg == "-modulewrap" {
805-
updatedArgs[0] = "swift-frontend"
806-
return (.subcommand("swift-frontend"), updatedArgs)
816+
updatedArgs[0] = executable("swift-frontend")
817+
return (.subcommand(executable("swift-frontend")), updatedArgs)
807818
}
808819

809820
// Only 'swift' supports subcommands.
@@ -824,7 +835,7 @@ extension Driver {
824835
return (.normal(isRepl: true), updatedArgs)
825836
}
826837

827-
let subcommand = "swift-\(firstArg)"
838+
let subcommand = executable("swift-\(firstArg)")
828839

829840
updatedArgs.replaceSubrange(0...1, with: [subcommand])
830841

@@ -2222,10 +2233,12 @@ extension Driver {
22222233

22232234
if !fileSystem.exists(path) {
22242235
diagnosticsEngine.emit(.warning_no_such_sdk(sdkPath))
2225-
} else if isSDKTooOld(sdkPath: path, fileSystem: fileSystem,
2226-
diagnosticsEngine: diagnosticsEngine) {
2227-
diagnosticsEngine.emit(.error_sdk_too_old(sdkPath))
2228-
return nil
2236+
} else if !(targetTriple?.isWindows ?? (defaultToolchainType == WindowsToolchain.self)) {
2237+
if isSDKTooOld(sdkPath: path, fileSystem: fileSystem,
2238+
diagnosticsEngine: diagnosticsEngine) {
2239+
diagnosticsEngine.emit(.error_sdk_too_old(sdkPath))
2240+
return nil
2241+
}
22292242
}
22302243

22312244
return .absolute(path)
@@ -2510,7 +2523,7 @@ extension Triple {
25102523
case .wasi:
25112524
return WebAssemblyToolchain.self
25122525
case .win32:
2513-
fatalError("Windows target not supported yet")
2526+
return WindowsToolchain.self
25142527
default:
25152528
diagnosticsEngine.emit(.error_unknown_target(triple))
25162529
throw Diagnostics.fatalError
@@ -2523,7 +2536,7 @@ extension Driver {
25232536
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
25242537
static let defaultToolchainType: Toolchain.Type = DarwinToolchain.self
25252538
#elseif os(Windows)
2526-
static let defaultToolchainType: Toolchain.Type = { fatalError("Windows target not supported yet") }()
2539+
static let defaultToolchainType: Toolchain.Type = WindowsToolchain.self
25272540
#else
25282541
static let defaultToolchainType: Toolchain.Type = GenericUnixToolchain.self
25292542
#endif

Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,14 @@ public extension Driver {
314314
@_spi(Testing) public extension Driver {
315315
static func getScanLibPath(of toolchain: Toolchain, hostTriple: Triple,
316316
env: [String: String]) throws -> AbsolutePath {
317+
if hostTriple.isWindows {
318+
// no matter if we are in a build tree or an installed tree, the layout is
319+
// always: `bin/_InternalSwiftScan.dll`
320+
return try getRootPath(of: toolchain, env: env)
321+
.appending(component: "bin")
322+
.appending(component: "_InternalSwiftScan.dll")
323+
}
324+
317325
let sharedLibExt: String
318326
if hostTriple.isMacOSX {
319327
sharedLibExt = ".dylib"

Sources/SwiftDriver/Jobs/Toolchain+InterpreterSupport.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,16 @@ extension GenericUnixToolchain {
7373
return envVars
7474
}
7575
}
76+
77+
extension WindowsToolchain {
78+
public func platformSpecificInterpreterEnvironmentVariables(
79+
env: [String : String],
80+
parsedOptions: inout ParsedOptions,
81+
sdkPath: VirtualPath.Handle?,
82+
targetInfo: FrontendTargetInfo) throws -> [String: String] {
83+
84+
// TODO(compnerd): setting up `Path` is meaningless currently as the lldb
85+
// support required for the interpreter mode fails to load the dependencies.
86+
return [:]
87+
}
88+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
//===--------------- WindowsToolchain+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+
13+
import TSCBasic
14+
import SwiftOptions
15+
16+
extension WindowsToolchain {
17+
public func addPlatformSpecificLinkerArgs(to commandLine: inout [Job.ArgTemplate],
18+
parsedOptions: inout ParsedOptions,
19+
linkerOutputType: LinkOutputType,
20+
inputs: [TypedVirtualPath],
21+
outputFile: VirtualPath,
22+
shouldUseInputFileList: Bool,
23+
lto: LTOKind?,
24+
sanitizers: Set<Sanitizer>,
25+
targetInfo: FrontendTargetInfo)
26+
throws -> AbsolutePath {
27+
let targetTriple = targetInfo.target.triple
28+
29+
if !targetTriple.triple.isEmpty {
30+
commandLine.appendFlag("-target")
31+
commandLine.appendFlag(targetTriple.triple)
32+
}
33+
34+
switch linkerOutputType {
35+
case .staticLibrary:
36+
break
37+
case .dynamicLibrary:
38+
commandLine.appendFlag("-shared")
39+
case .executable:
40+
break
41+
}
42+
43+
// Select the linker to use.
44+
if let arg = parsedOptions.getLastArgument(.useLd) {
45+
commandLine.appendFlag("-fuse-ld=\(arg.asSingle)")
46+
} else if lto != nil {
47+
commandLine.appendFlag("-fuse-ld=lld")
48+
}
49+
50+
switch lto {
51+
case .some(.llvmThin):
52+
commandLine.appendFlag("-flto=thin")
53+
case .some(.llvmFull):
54+
commandLine.appendFlag("-flto=full")
55+
case .none:
56+
break
57+
}
58+
59+
// FIXME(compnerd): render `-Xlinker /DEBUG` or `-Xlinker /DEBUG:DWARF` with
60+
// DWARF + lld
61+
62+
// Rely on `-libc` to correctly identify the MSVC Runtime Library. We use
63+
// `-nostartfiles` as that limits the difference to just the
64+
// `-defaultlib:libcmt` which is passed unconditionally with the `clang`
65+
// driver rather than the `clang-cl` driver.
66+
commandLine.appendFlag("-nostartfiles")
67+
68+
// TODO(compnerd) investigate the proper way to port this logic over from
69+
// the C++ driver.
70+
71+
// Since Windows has separate libraries per architecture, link against the
72+
// architecture specific version of the static library.
73+
commandLine.appendFlag(.L)
74+
commandLine.appendPath(VirtualPath.lookup(targetInfo.runtimeLibraryImportPaths.last!.path))
75+
76+
// FIXME(compnerd) figure out how to ensure that the SDK relative path is
77+
// the last one
78+
commandLine.appendPath(VirtualPath.lookup(targetInfo.runtimeLibraryImportPaths.last!.path)
79+
.appending(component: "swiftrt.obj"))
80+
81+
commandLine.append(contentsOf: inputs.compactMap { (input: TypedVirtualPath) -> Job.ArgTemplate? in
82+
switch input.type {
83+
case .object, .llvmBitcode:
84+
return .path(input.file)
85+
default:
86+
return nil
87+
}
88+
})
89+
90+
for framework in parsedOptions.arguments(for: .F, .Fsystem) {
91+
commandLine.appendFlag(framework.option == .Fsystem ? "-iframework" : "-F")
92+
try commandLine.appendPath(VirtualPath(path: framework.argument.asSingle))
93+
}
94+
95+
if let sdkPath = targetInfo.sdkPath?.path {
96+
commandLine.appendFlag("-I")
97+
commandLine.appendPath(VirtualPath.lookup(sdkPath))
98+
}
99+
100+
if let stdlib = parsedOptions.getLastArgument(.experimentalCxxStdlib) {
101+
commandLine.appendFlag("-stdlib=\(stdlib.asSingle)")
102+
}
103+
104+
// FIXME(compnerd) render asan/ubsan runtime link for executables
105+
106+
if parsedOptions.contains(.profileGenerate) {
107+
commandLine.appendFlag("-Xlinker")
108+
// FIXME(compnerd) wrap llvm::getInstrProfRuntimeHookVarName()
109+
commandLine.appendFlag("-include:__llvm_profile_runtime")
110+
commandLine.appendFlag("-lclang_rt.profile")
111+
}
112+
113+
for option in parsedOptions.arguments(for: .Xlinker) {
114+
commandLine.appendFlag(.Xlinker)
115+
commandLine.appendFlag(option.argument.asSingle)
116+
}
117+
// TODO(compnerd) is there a separate equivalent to OPT_linker_option_group?
118+
try commandLine.appendAllArguments(.XclangLinker, from: &parsedOptions)
119+
120+
if parsedOptions.contains(.v) {
121+
commandLine.appendFlag("-v")
122+
}
123+
124+
commandLine.appendFlag("-o")
125+
commandLine.appendPath(outputFile)
126+
127+
// TODO(compnerd) handle static libraries
128+
return try getToolPath(.clang)
129+
}
130+
}

Sources/SwiftDriver/Toolchains/Toolchain.swift

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ import Foundation
1414
import TSCBasic
1515
import SwiftOptions
1616

17+
private func tool(_ name: String) -> String {
18+
#if os(Windows)
19+
if name.suffix(from: name.index(name.endIndex, offsetBy: -4)) == ".exe" {
20+
return name
21+
}
22+
return "\(name).exe"
23+
#else
24+
return name
25+
#endif
26+
}
27+
1728
public enum Tool: Hashable {
1829
case swiftCompiler
1930
case staticLinker(LTOKind?)
@@ -104,7 +115,7 @@ public protocol Toolchain {
104115

105116
extension Toolchain {
106117
public var searchPaths: [AbsolutePath] {
107-
getEnvSearchPaths(pathString: env["PATH"], currentWorkingDirectory: fileSystem.currentWorkingDirectory)
118+
getEnvSearchPaths(pathString: ProcessEnv.path, currentWorkingDirectory: fileSystem.currentWorkingDirectory)
108119
}
109120

110121
/// Returns the `executablePath`'s directory.
@@ -131,6 +142,9 @@ extension Toolchain {
131142
private func envVarName(for toolName: String) -> String {
132143
let lookupName = toolName
133144
.replacingOccurrences(of: "-", with: "_")
145+
// FIXME(compnerd) we should extract the extension for generating the
146+
// toolname rather than assuming that we can convert the tool name blindly
147+
.replacingOccurrences(of: ".", with: "_")
134148
.uppercased()
135149
return "SWIFT_DRIVER_\(lookupName)_EXEC"
136150
}
@@ -157,7 +171,7 @@ extension Toolchain {
157171
return path
158172
} else if let path = try? xcrunFind(executable: executable) {
159173
return path
160-
} else if !["swift-frontend", "swift"].contains(executable),
174+
} else if ![tool("swift-frontend"), tool("swift")].contains(executable),
161175
let parentDirectory = try? getToolPath(.swiftCompiler).parentDirectory,
162176
parentDirectory != executableDir,
163177
let path = lookupExecutablePath(filename: executable, searchPaths: [parentDirectory]) {
@@ -166,10 +180,12 @@ extension Toolchain {
166180
return path
167181
} else if let path = lookupExecutablePath(filename: executable, searchPaths: searchPaths) {
168182
return path
169-
} else if executable == "swift-frontend" {
183+
} else if executable == tool("swift-frontend") {
170184
// Temporary shim: fall back to looking for "swift" before failing.
171-
return try lookup(executable: "swift")
185+
return try lookup(executable: tool("swift"))
172186
} else if fallbackToExecutableDefaultPath {
187+
// TODO(compnerd) correct this for Windows - %SystemDrive%\Developer\Toolchains\....xctoolchain\usr\bin
188+
// To do this we need access to the Driver as we can check Driver.hostTriple.isWindows
173189
return AbsolutePath("/usr/bin/" + executable)
174190
} else {
175191
throw ToolchainError.unableToFind(tool: executable)

0 commit comments

Comments
 (0)