Skip to content

Commit e2442a6

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 e2442a6

File tree

8 files changed

+326
-14
lines changed

8 files changed

+326
-14
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: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -787,7 +787,7 @@ extension Driver {
787787
let execName = try VirtualPath(path: args[0]).basenameWithoutExt
788788

789789
// 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 {
790+
guard ["swift.exe", "swiftc.exe", "swift", "swiftc"].contains(execName), args.count > 1 else {
791791
return (.normal(isRepl: false), args)
792792
}
793793

@@ -797,13 +797,13 @@ extension Driver {
797797

798798
// Check for flags associated with frontend tools.
799799
if firstArg == "-frontend" {
800-
updatedArgs.replaceSubrange(0...1, with: ["swift-frontend"])
800+
updatedArgs.replaceSubrange(0...1, with: ["swift-frontend.exe"])
801801
return (.subcommand("swift-frontend"), updatedArgs)
802802
}
803803

804804
if firstArg == "-modulewrap" {
805-
updatedArgs[0] = "swift-frontend"
806-
return (.subcommand("swift-frontend"), updatedArgs)
805+
updatedArgs[0] = "swift-frontend.exe"
806+
return (.subcommand("swift-frontend.exe"), updatedArgs)
807807
}
808808

809809
// Only 'swift' supports subcommands.
@@ -824,7 +824,7 @@ extension Driver {
824824
return (.normal(isRepl: true), updatedArgs)
825825
}
826826

827-
let subcommand = "swift-\(firstArg)"
827+
let subcommand = "swift-\(firstArg).exe"
828828

829829
updatedArgs.replaceSubrange(0...1, with: [subcommand])
830830

@@ -2222,8 +2222,9 @@ extension Driver {
22222222

22232223
if !fileSystem.exists(path) {
22242224
diagnosticsEngine.emit(.warning_no_such_sdk(sdkPath))
2225-
} else if isSDKTooOld(sdkPath: path, fileSystem: fileSystem,
2226-
diagnosticsEngine: diagnosticsEngine) {
2225+
} else if false, // (targetTriple?.isDarwin ?? false),
2226+
isSDKTooOld(sdkPath: path, fileSystem: fileSystem,
2227+
diagnosticsEngine: diagnosticsEngine) {
22272228
diagnosticsEngine.emit(.error_sdk_too_old(sdkPath))
22282229
return nil
22292230
}
@@ -2510,7 +2511,7 @@ extension Triple {
25102511
case .wasi:
25112512
return WebAssemblyToolchain.self
25122513
case .win32:
2513-
fatalError("Windows target not supported yet")
2514+
return WindowsToolchain.self
25142515
default:
25152516
diagnosticsEngine.emit(.error_unknown_target(triple))
25162517
throw Diagnostics.fatalError
@@ -2523,7 +2524,7 @@ extension Driver {
25232524
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
25242525
static let defaultToolchainType: Toolchain.Type = DarwinToolchain.self
25252526
#elseif os(Windows)
2526-
static let defaultToolchainType: Toolchain.Type = { fatalError("Windows target not supported yet") }()
2527+
static let defaultToolchainType: Toolchain.Type = WindowsToolchain.self
25272528
#else
25282529
static let defaultToolchainType: Toolchain.Type = GenericUnixToolchain.self
25292530
#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: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public protocol Toolchain {
104104

105105
extension Toolchain {
106106
public var searchPaths: [AbsolutePath] {
107-
getEnvSearchPaths(pathString: env["PATH"], currentWorkingDirectory: fileSystem.currentWorkingDirectory)
107+
getEnvSearchPaths(pathString: ProcessEnv.path, currentWorkingDirectory: fileSystem.currentWorkingDirectory)
108108
}
109109

110110
/// Returns the `executablePath`'s directory.
@@ -131,6 +131,9 @@ extension Toolchain {
131131
private func envVarName(for toolName: String) -> String {
132132
let lookupName = toolName
133133
.replacingOccurrences(of: "-", with: "_")
134+
// FIXME(compnerd) we should extract the extension for generating the
135+
// toolname rather than assuming that we can convert the tool name blindly
136+
.replacingOccurrences(of: ".", with: "_")
134137
.uppercased()
135138
return "SWIFT_DRIVER_\(lookupName)_EXEC"
136139
}
@@ -157,7 +160,7 @@ extension Toolchain {
157160
return path
158161
} else if let path = try? xcrunFind(executable: executable) {
159162
return path
160-
} else if !["swift-frontend", "swift"].contains(executable),
163+
} else if !["swift-frontend.exe", "swift.exe", "swift-frontend", "swift"].contains(executable),
161164
let parentDirectory = try? getToolPath(.swiftCompiler).parentDirectory,
162165
parentDirectory != executableDir,
163166
let path = lookupExecutablePath(filename: executable, searchPaths: [parentDirectory]) {
@@ -166,10 +169,15 @@ extension Toolchain {
166169
return path
167170
} else if let path = lookupExecutablePath(filename: executable, searchPaths: searchPaths) {
168171
return path
169-
} else if executable == "swift-frontend" {
172+
} else if ["swift-frontend.exe", "swift-frontend"].contains(executable) {
170173
// Temporary shim: fall back to looking for "swift" before failing.
171-
return try lookup(executable: "swift")
174+
175+
// FIXME(compnerd) this should fallback to `swift`, but only on
176+
// non-windows hosts. To do this we need access to the Driver as we can
177+
// check !Driver.hostTriple.isWindows
178+
return try lookup(executable: "swift.exe")
172179
} else if fallbackToExecutableDefaultPath {
180+
// TODO(compnerd) correct this for Windows - %SystemDrive%\Developer\Toolchains\....xctoolchain\usr\bin
173181
return AbsolutePath("/usr/bin/" + executable)
174182
} else {
175183
throw ToolchainError.unableToFind(tool: executable)

0 commit comments

Comments
 (0)