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 @@ -58,10 +58,12 @@ add_library(SwiftDriver
Jobs/Toolchain+LinkerSupport.swift
Jobs/VerifyDebugInfoJob.swift
Jobs/VerifyModuleInterfaceJob.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
20 changes: 18 additions & 2 deletions Sources/SwiftDriver/Driver/Driver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,9 @@ public struct Driver {
self.numParallelJobs = Self.determineNumParallelJobs(&parsedOptions, diagnosticsEngine: diagnosticEngine, env: env)

Self.validateWarningControlArgs(&parsedOptions, diagnosticEngine: diagnosticEngine)
Self.validateCRuntimeArgs(&parsedOptions,
diagnosticEngine: diagnosticsEngine,
targetTriple: self.frontendTargetInfo.target.triple)
Self.validateProfilingArgs(&parsedOptions,
fileSystem: fileSystem,
workingDirectory: workingDirectory,
Expand Down Expand Up @@ -1299,6 +1302,11 @@ extension Driver {
sanitizerSupported = false
}

// Currently only ASAN is supported on Windows.
if sanitizer != .address && targetTriple.isWindows {
sanitizerSupported = false
}

if !sanitizerSupported {
diagnosticEngine.emit(
.error_unsupported_opt_for_target(
Expand Down Expand Up @@ -1655,6 +1663,14 @@ extension Driver {
}
}

static func validateCRuntimeArgs(_ parsedOptions: inout ParsedOptions,
diagnosticEngine: DiagnosticsEngine,
targetTriple: Triple) {
if parsedOptions.hasArgument(.libc) && !targetTriple.isWindows {
diagnosticEngine.emit(.error_unsupported_opt_for_target(arg: "-libc", target: targetTriple))
}
}

static func validateProfilingArgs(_ parsedOptions: inout ParsedOptions,
fileSystem: FileSystem,
workingDirectory: AbsolutePath?,
Expand Down Expand Up @@ -1718,7 +1734,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 @@ -1731,7 +1747,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 [:]
}
}
28 changes: 22 additions & 6 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,12 +54,20 @@ extension Toolchain {
for triple: Triple,
parsedOptions: inout ParsedOptions
) throws -> AbsolutePath {
return try computeResourceDirPath(for: triple,
parsedOptions: &parsedOptions,
isShared: true)
.parentDirectory // Remove platform name.
.appending(components: "clang", "lib",
triple.platformName(conflatingDarwin: true)!)
#if os(Windows)
return try getToolPath(.swiftCompiler)
.parentDirectory // remove /swift
.parentDirectory // remove /bin
.appending(components: "lib", "swift", "clang", "lib",
triple.platformName(conflatingDarwin: true)!)
#else
return try computeResourceDirPath(for: triple,
parsedOptions: &parsedOptions,
isShared: true)
.parentDirectory // Remove platform name.
.appending(components: "clang", "lib",
triple.platformName(conflatingDarwin: true)!)
#endif
}

func runtimeLibraryPaths(
Expand Down Expand Up @@ -258,3 +272,5 @@ extension DarwinToolchain {
}

}

// TODO: Implement `addArgsToLinkStdlib` for `WindowsToolchain`.
190 changes: 190 additions & 0 deletions Sources/SwiftDriver/Jobs/WindowsToolchain+LinkerSupport.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
//===---------------- 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:
commandLine.appendFlags("-Xlinker", "-dll")
fallthrough
case .executable:
if !targetTriple.triple.isEmpty {
commandLine.appendFlag("-target")
commandLine.appendFlag(targetTriple.triple)
}
// 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 linker: String
if let arg = parsedOptions.getLastArgument(.useLd) {
linker = arg.asSingle
} else {
linker = "link"
}
commandLine.appendFlag("-fuse-ld=\(linker)")

// FIXME: Do we really need `oldnames`?
commandLine.appendFlags("-autolink-library", "oldnames")
if let crt = parsedOptions.getLastArgument(.libc) {
switch crt.asSingle {
case "MT": commandLine.appendFlags("-autolink-library", "libcmt")
case "MTd": commandLine.appendFlags("-autolink-library", "libcmtd")
case "MD": commandLine.appendFlags("-autolink-library", "msvcrt")
case "MDd": commandLine.appendFlags("-autolink-library", "msvcrtd")
default: fatalError("Invalid C runtime value should be filtered")
}
} else {
commandLine.appendFlags("-autolink-library", "msvcrt")
}

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
)

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

let swiftrtPath = sharedResourceDirPath.appending(
components: 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)
}

if hasRuntimeArgs {
commandLine.appendFlag("-lswiftCore")
}

// Explicitly pass the target to the linker
commandLine.appendFlags("-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")
}
}

// 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
case .staticLibrary:
commandLine.append(.joinedOptionAndPath("-out:", outputFile))
commandLine.append(contentsOf: inputs.map { .path($0.file) })
return try getToolPath(.staticLinker)
}
}
}
6 changes: 3 additions & 3 deletions Sources/SwiftDriver/Toolchains/DarwinToolchain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ import SwiftOptions
public final class DarwinToolchain: Toolchain {
public let env: [String: String]

/// Doubles as path cache and point for overriding normal lookup
private var toolPaths = [Tool: AbsolutePath]()

/// The executor used to run processes used to find tools and retrieve target info.
public let executor: DriverExecutor

/// The file system to use for any file operations.
public let fileSystem: FileSystem

/// Doubles as path cache and point for overriding normal lookup
private var toolPaths = [Tool: AbsolutePath]()

public init(env: [String: String], executor: DriverExecutor, fileSystem: FileSystem = localFileSystem) {
self.env = env
self.executor = executor
Expand Down
Loading