Skip to content

[WIP] Implement WindowsToolchain #252

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
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