Skip to content
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ The goal of the new Swift driver is to provide a drop-in replacement for the exi
* Platform support
* [x] Teach the `DarwinToolchain` to also handle iOS, tvOS, watchOS
* [x] Fill out the `GenericUnixToolchain` toolchain to get it working
* [ ] Implement a `WindowsToolchain`
* [x] Implement a `WindowsToolchain`
* [x] Implement proper tokenization for response files
* Compilation modes
* [x] Batch mode
Expand Down
4 changes: 3 additions & 1 deletion Sources/SwiftDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This source file is part of the Swift.org open source project
#
# Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See http://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -57,10 +57,12 @@ add_library(SwiftDriver
Jobs/Toolchain+InterpreterSupport.swift
Jobs/Toolchain+LinkerSupport.swift
Jobs/VerifyDebugInfoJob.swift
Jobs/WindowsToolchain+LinkerSupport.swift

Toolchains/DarwinToolchain.swift
Toolchains/GenericUnixToolchain.swift
Toolchains/Toolchain.swift
Toolchains/WindowsToolchain.swift

Utilities/Bits.swift
Utilities/Bitstream.swift
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftDriver/Driver/Driver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1702,7 +1702,7 @@ extension Triple {
case .freeBSD, .haiku:
return GenericUnixToolchain.self
case .win32:
fatalError("Windows target not supported yet")
return WindowsToolchain.self
default:
diagnosticsEngine.emit(.error_unknown_target(triple))
throw Diagnostics.fatalError
Expand All @@ -1715,7 +1715,7 @@ extension Driver {
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
static let defaultToolchainType: Toolchain.Type = DarwinToolchain.self
#elseif os(Windows)
static let defaultToolchainType: Toolchain.Type = { fatalError("Windows target not supported yet") }()
static let defaultToolchainType: Toolchain.Type = WindowsToolchain.self
#else
static let defaultToolchainType: Toolchain.Type = GenericUnixToolchain.self
#endif
Expand Down
8 changes: 4 additions & 4 deletions Sources/SwiftDriver/Jobs/CommandLineArguments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,10 @@ extension Array where Element == Job.ArgTemplate {
var joinedArguments: String {
return self.map {
switch $0 {
case .flag(let string):
return string.spm_shellEscaped()
case .path(let path):
return path.name.spm_shellEscaped()
case .flag(let string):
return string.spm_shellEscaped()
case .path(let path):
return path.name.spm_shellEscaped()
case .responseFilePath(let path):
return "@\(path.name.spm_shellEscaped())"
case let .joinedOptionAndPath(option, path):
Expand Down
11 changes: 11 additions & 0 deletions Sources/SwiftDriver/Jobs/Toolchain+InterpreterSupport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,14 @@ extension GenericUnixToolchain {
return envVars
}
}

extension WindowsToolchain {
public func platformSpecificInterpreterEnvironmentVariables(
env: [String : String],
parsedOptions: inout ParsedOptions,
sdkPath: String?,
targetTriple: Triple) throws -> [String: String] {
// TODO: See whether Windows needs `platformSpecificInterpreterEnvironmentVariables`
return [:]
}
}
23 changes: 23 additions & 0 deletions Sources/SwiftDriver/Jobs/Toolchain+LinkerSupport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ extension Toolchain {
resourceDirBase = sdkPath
.appending(components: "usr", "lib",
isShared ? "swift" : "swift_static")
} else if triple.isWindows,
let SDKROOT = env["SDKROOT"],
let sdkPath = try? AbsolutePath(validating: SDKROOT) {
resourceDirBase = sdkPath
.appending(components: "usr", "lib",
isShared ? "swift" : "swift_static")
} else {
resourceDirBase = try getToolPath(.swiftCompiler)
.parentDirectory // remove /swift
Expand All @@ -48,6 +54,21 @@ extension Toolchain {
for triple: Triple,
parsedOptions: inout ParsedOptions
) throws -> AbsolutePath {
/// Use the one in `VCTools` if exists on Windows.
if triple.isWindows,
let vctools = env["VCToolsInstallDir"],
let root = try? AbsolutePath(validating: vctools) {
let archName: String = {
switch triple.arch {
case .aarch64, .aarch64_32: return "arm64"
case .arm: return "arm"
case .x86: return "x86"
case nil, .x86_64: return "x64"
default: fatalError("unknown arch \(triple.archName) on Windows")
}
}()
return root.appending(components: "lib", archName)
}
return try computeResourceDirPath(for: triple,
parsedOptions: &parsedOptions,
isShared: true)
Expand Down Expand Up @@ -258,3 +279,5 @@ extension DarwinToolchain {
}

}

// TODO: See whether Windows needs `addArgsToLinkStdlib`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it will definitely needs arguments for that. At the very least, you need to ensure that the ucrt link matches the stdlib.

219 changes: 219 additions & 0 deletions Sources/SwiftDriver/Jobs/WindowsToolchain+LinkerSupport.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
//===---------------- WindowsToolchain+LinkerSupport.swift ----------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import TSCBasic
import SwiftOptions

extension WindowsToolchain {
public func addPlatformSpecificLinkerArgs(
to commandLine: inout [Job.ArgTemplate],
parsedOptions: inout ParsedOptions,
linkerOutputType: LinkOutputType,
inputs: [TypedVirtualPath],
outputFile: VirtualPath,
shouldUseInputFileList: Bool,
sdkPath: String?,
sanitizers: Set<Sanitizer>,
targetInfo: FrontendTargetInfo
) throws -> AbsolutePath {
let targetTriple = targetInfo.target.triple
switch linkerOutputType {
case .dynamicLibrary:
// Same options as an executable, just with '-shared'
commandLine.appendFlags("-parse-as-library", "-emit-library")
fallthrough
case .executable:
if !targetTriple.triple.isEmpty {
commandLine.appendFlag("-target")
commandLine.appendFlag(targetTriple.triple)
}
commandLine.appendFlag("-emit-executable")
default:
break
}

switch linkerOutputType {
case .staticLibrary:
commandLine.append(.joinedOptionAndPath("-out:", outputFile))
commandLine.append(contentsOf: inputs.map { .path($0.file) })
return try getToolPath(.staticLinker)
default:
// Configure the toolchain.
//
// By default use the system `clang` to perform the link. We use `clang` for
// the driver here because we do not wish to select a particular C++ runtime.
// Furthermore, until C++ interop is enabled, we cannot have a dependency on
// C++ code from pure Swift code. If linked libraries are C++ based, they
// should properly link C++. In the case of static linking, the user can
// explicitly specify the C++ runtime to link against. This is particularly
// important for platforms like android where as it is a Linux platform, the
// default C++ runtime is `libstdc++` which is unsupported on the target but
// as the builds are usually cross-compiled from Linux, libstdc++ is going to
// be present. This results in linking the wrong version of libstdc++
// generating invalid binaries. It is also possible to use different C++
// runtimes than the default C++ runtime for the platform (e.g. libc++ on
// Windows rather than msvcprt). When C++ interop is enabled, we will need to
// surface this via a driver flag. For now, opt for the simpler approach of
// just using `clang` and avoid a dependency on the C++ runtime.
var clangPath = try getToolPath(.clang)
if let toolsDirPath = parsedOptions.getLastArgument(.toolsDirectory) {
// FIXME: What if this isn't an absolute path?
let toolsDir = try AbsolutePath(validating: toolsDirPath.asSingle)

// If there is a clang in the toolchain folder, use that instead.
if let tool = lookupExecutablePath(filename: "clang.exe", searchPaths: [toolsDir]) {
clangPath = tool
}

// Look for binutils in the toolchain folder.
commandLine.appendFlag("-B")
commandLine.appendPath(toolsDir)
}

let staticStdlib = parsedOptions.hasFlag(positive: .staticStdlib,
negative: .noStaticStdlib,
default: false)
let staticExecutable = parsedOptions.hasFlag(positive: .staticExecutable,
negative: .noStaticExecutable,
default: false)
let hasRuntimeArgs = !(staticStdlib || staticExecutable)

let runtimePaths = try runtimeLibraryPaths(
for: targetTriple,
parsedOptions: &parsedOptions,
sdkPath: sdkPath,
isShared: hasRuntimeArgs
)

if hasRuntimeArgs {
// FIXME: We probably shouldn't be adding an rpath here unless we know
// ahead of time the standard library won't be copied.
for path in runtimePaths {
commandLine.appendFlag(.Xlinker)
commandLine.appendFlag("-rpath")
commandLine.appendFlag(.Xlinker)
commandLine.appendPath(path)
}
}

let sharedResourceDirPath = try computeResourceDirPath(
for: targetTriple,
parsedOptions: &parsedOptions,
isShared: true
)

let swiftrtPath = sharedResourceDirPath.appending(
components: "windows", targetTriple.archName, "swiftrt.obj"
)
commandLine.appendPath(swiftrtPath)

let inputFiles: [Job.ArgTemplate] = inputs.compactMap { input in
// Autolink inputs are handled specially
if input.type == .autolink {
return .responseFilePath(input.file)
} else if input.type == .object {
return .path(input.file)
} else {
return nil
}
}
commandLine.append(contentsOf: inputFiles)

let fSystemArgs = parsedOptions.arguments(for: .F, .Fsystem)
for opt in fSystemArgs {
if opt.option == .Fsystem {
commandLine.appendFlag("-iframework")
} else {
commandLine.appendFlag(.F)
}
commandLine.appendPath(try VirtualPath(path: opt.argument.asSingle))
}

// Add the runtime library link paths.
for path in runtimePaths {
commandLine.appendFlag(.L)
commandLine.appendPath(path)
}

// Link the standard library. In two paths, we do this using a .lnk file
// if we're going that route, we'll set `linkFilePath` to the path to that
// file.
var linkFilePath: AbsolutePath? = try computeResourceDirPath(
for: targetTriple,
parsedOptions: &parsedOptions,
isShared: false
)

if staticExecutable {
linkFilePath = linkFilePath?.appending(component: "static-executable-args.lnk")
} else if staticStdlib {
linkFilePath = linkFilePath?.appending(component: "static-stdlib-args.lnk")
} else {
linkFilePath = nil
commandLine.appendFlag("-lswiftCore")
}

if let linkFile = linkFilePath {
guard fileSystem.isFile(linkFile) else {
fatalError("\(linkFile.pathString) not found")
}
commandLine.append(.responseFilePath(.absolute(linkFile)))
}

// Explicitly pass the target to the linker
commandLine.appendFlag("--target=\(targetTriple.triple)")

// Delegate to Clang for sanitizers. It will figure out the correct linker
// options.
if linkerOutputType == .executable && !sanitizers.isEmpty {
let sanitizerNames = sanitizers
.map { $0.rawValue }
.sorted() // Sort so we get a stable, testable order
.joined(separator: ",")
commandLine.appendFlag("-fsanitize=\(sanitizerNames)")

// The TSan runtime depends on the blocks runtime and libdispatch.
if sanitizers.contains(.thread) {
commandLine.appendFlag("-lBlocksRuntime")
commandLine.appendFlag("-ldispatch")
}
}

if parsedOptions.hasArgument(.profileGenerate) {
let libProfile = sharedResourceDirPath
.parentDirectory // remove platform name
.appending(components: "clang", "lib", targetTriple.osName,
"libclangrt_profile-\(targetTriple.archName).a")
commandLine.appendPath(libProfile)

// HACK: Hard-coded from llvm::getInstrProfRuntimeHookVarName()
commandLine.appendFlag("-u__llvm_profile_runtime")
}

// Run clang++ in verbose mode if "-v" is set
try commandLine.appendLast(.v, from: &parsedOptions)

// These custom arguments should be right before the object file at the
// end.
try commandLine.append(
contentsOf: parsedOptions.arguments(in: .linkerOption)
)
try commandLine.appendAllArguments(.Xlinker, from: &parsedOptions)
try commandLine.appendAllArguments(.XclangLinker, from: &parsedOptions)

// This should be the last option, for convenience in checking output.
commandLine.appendFlag(.o)
commandLine.appendPath(outputFile)
return clangPath
}

}
}
8 changes: 5 additions & 3 deletions Sources/SwiftDriver/Toolchains/Toolchain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -135,7 +135,7 @@ extension Toolchain {
return path
} else if let path = try? xcrunFind(executable: executable) {
return path
} else if !["swift-frontend", "swift"].contains(executable),
} else if !["swift-frontend", "swift", "swift-frontend.exe", "swift.exe"].contains(executable),
let parentDirectory = try? getToolPath(.swiftCompiler).parentDirectory,
parentDirectory != executableDir,
let path = lookupExecutablePath(filename: executable, searchPaths: [parentDirectory]) {
Expand All @@ -144,9 +144,11 @@ extension Toolchain {
return path
} else if let path = lookupExecutablePath(filename: executable, searchPaths: searchPaths) {
return path
// Temporary shim: fall back to looking for "swift" before failing.
} else if executable == "swift-frontend" {
// Temporary shim: fall back to looking for "swift" before failing.
return try lookup(executable: "swift")
} else if executable == "swift-frontend.exe" {
return try lookup(executable: "swift.exe")
} else if fallbackToExecutableDefaultPath {
return AbsolutePath("/usr/bin/" + executable)
} else {
Expand Down
Loading