Skip to content
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

[DNM][WIP] Implement Windows toolchain #419

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9fcd3f2
Implement WindowsToolchain
stevapple Sep 13, 2020
9076ab2
Fix swiftrtPath
stevapple Sep 13, 2020
2c9716b
Fix various problems
stevapple Sep 14, 2020
8b7b0df
Derive executable file name from Toolchain
stevapple Sep 14, 2020
3c0feb3
Fix addPlatformSpecificLinkerArgs for WindowsToolchain
stevapple Sep 14, 2020
633801d
Fix clangLibraryPath for Windows
stevapple Sep 14, 2020
59877f1
Disable profiling support on Windows
stevapple Sep 16, 2020
4b69ec6
Disable unsupported sanitizers on Windows
stevapple Sep 16, 2020
31d2556
Merge branch 'master' into windows-toolchain
stevapple Sep 17, 2020
d8c4f60
Fix cross-compilation problems
stevapple Sep 17, 2020
ed1b8c2
Fix CRT support for Windows
stevapple Sep 17, 2020
4bd5495
Keep update with the latest layout
stevapple Dec 19, 2020
7e82541
Fix subcommand discovery
stevapple Dec 19, 2020
46785ab
Merge branch 'main' into windows-toolchain
stevapple Dec 19, 2020
660a709
Fix build
stevapple Dec 19, 2020
b1e5ef9
Implement functions to depress warnings
stevapple Dec 19, 2020
e270919
Disable LTO support on Windows
stevapple Dec 19, 2020
aa908ad
Improve flags usage
stevapple Dec 20, 2020
a3ca4ab
Fix MSVC Runtime options
stevapple Dec 20, 2020
5d38461
Minor fixes
stevapple Feb 20, 2021
204f061
Merge changes from upstream
stevapple Feb 20, 2021
6909152
Fix PATH
stevapple Feb 20, 2021
aa427ed
Fix some tests on Windows
stevapple Feb 23, 2021
58c6f7a
Fix some tests on Windows
stevapple Feb 23, 2021
9d308f1
Merge 'upstream/main' into windows-toolchain
stevapple Feb 23, 2021
a666df9
Minor fixes
stevapple Feb 23, 2021
d48e3e1
Style fixes
stevapple Feb 24, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
"package": "swift-argument-parser",
"repositoryURL": "https://github.com/apple/swift-argument-parser.git",
"state": {
"branch": null,
"revision": "92646c0cdbaca076c8d3d0207891785b3379cbff",
"version": "0.3.1"
"branch": "main",
"revision": "4273ad222e6c51969e8585541f9da5187ad94e47",
"version": null
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil {
// The 'swift-argument-parser' version declared here must match that
// used by 'swift-package-manager' and 'sourcekit-lsp'. Please coordinate
// dependency version changes here with those projects.
.package(url: "https://github.com/apple/swift-argument-parser.git", .upToNextMinor(from: "0.3.1")),
.package(url: "https://github.com/apple/swift-argument-parser.git", .branch("main")),
stevapple marked this conversation as resolved.
Show resolved Hide resolved
]
} else {
package.dependencies += [
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,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 @@ -71,11 +71,13 @@ add_library(SwiftDriver
Jobs/Toolchain+LinkerSupport.swift
Jobs/VerifyDebugInfoJob.swift
Jobs/VerifyModuleInterfaceJob.swift
Jobs/WindowsToolchain+LinkerSupport.swift
Jobs/WebAssemblyToolchain+LinkerSupport.swift

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

Utilities/DOTJobGraphSerializer.swift
Expand Down
26 changes: 21 additions & 5 deletions Sources/SwiftDriver/Driver/Driver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ public struct Driver {
public static let stderrDiagnosticsHandler: DiagnosticsEngine.DiagnosticsHandler = { diagnostic in
let stream = stderrStream
if !(diagnostic.location is UnknownLocation) {
stream <<< diagnostic.location.description <<< ": "
stream <<< diagnostic.location.description <<< ": "
}

switch diagnostic.message.behavior {
Expand All @@ -293,7 +293,7 @@ public struct Driver {
case .remark:
stream <<< "remark: "
case .ignored:
break
break
}

stream <<< diagnostic.localizedDescription <<< "\n"
Expand Down Expand Up @@ -447,6 +447,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 @@ -741,7 +744,7 @@ extension Driver {
///
/// - Parameter content: response file's content to be tokenized.
private static func tokenizeResponseFile(_ content: String) -> [String] {
#if !os(macOS) && !os(Linux) && !os(Android)
#if !os(macOS) && !os(Linux) && !os(Android) && !os(Windows)
#warning("Response file tokenization unimplemented for platform; behavior may be incorrect")
#endif
return content.split { $0 == "\n" || $0 == "\r\n" }
Expand Down Expand Up @@ -1590,6 +1593,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 @@ -1953,6 +1961,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 @@ -2040,7 +2056,7 @@ extension Triple {
case .wasi:
return WebAssemblyToolchain.self
case .win32:
fatalError("Windows target not supported yet")
return WindowsToolchain.self
default:
diagnosticsEngine.emit(.error_unknown_target(triple))
throw Diagnostics.fatalError
Expand All @@ -2053,7 +2069,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 {
public 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: VirtualPath?,
targetInfo: FrontendTargetInfo) throws -> [String: String] {
// TODO: See whether Windows needs `platformSpecificInterpreterEnvironmentVariables`
return [:]
}
}
10 changes: 8 additions & 2 deletions Sources/SwiftDriver/Jobs/Toolchain+LinkerSupport.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 @@ -50,7 +50,11 @@ extension Toolchain {
result.append(sdkPath.appending(components: "System", "iOSSupport", "usr", "lib", "swift"))
}

result.append(sdkPath.appending(components: "usr", "lib", "swift"))
if (triple.isWindows) {
result.append(sdkPath.appending(components: "usr", "lib", "swift", "windows"))
} else {
Comment on lines +53 to +55
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this used to find the correct path to the runtime, or something else maybe? I know in a few other places we append targetTriple.platformName() only when needed which might help remove the workaround here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I referred to the current layout of Windows toolchain and SDK, which is slightly different from other platforms. I think we need to unify SDK layout for the convenience of cross-compilation (and to eliminate differences across platforms), how do you think @compnerd ?

result.append(sdkPath.appending(components: "usr", "lib", "swift"))
}
}

return result
Expand Down Expand Up @@ -225,3 +229,5 @@ extension DarwinToolchain {
}

}

// TODO: Implement `addArgsToLinkStdlib` for `WindowsToolchain`.
186 changes: 186 additions & 0 deletions Sources/SwiftDriver/Jobs/WindowsToolchain+LinkerSupport.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
//===---------------- 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,
lto: LTOKind?,
sanitizers: Set<Sanitizer>,
targetInfo: FrontendTargetInfo
) throws -> AbsolutePath {
let targetTriple = targetInfo.target.triple

switch linkerOutputType {
case .dynamicLibrary:
commandLine.appendFlags("-Xlinker", "-dll")
stevapple marked this conversation as resolved.
Show resolved Hide resolved
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)")

if let crt = parsedOptions.getLastArgument(.libc) {
switch crt.asSingle {
case "MT": commandLine.appendFlags("-Xlinker", "-defaultlib:libcmt")
case "MTd": commandLine.appendFlags("-Xlinker", "-defaultlib:libcmtd")
case "MD": commandLine.appendFlags("-Xlinker", "-defaultlib:msvcrt")
case "MDd": commandLine.appendFlags("-Xlinker", "-defaultlib:msvcrtd")
default: fatalError("Invalid C runtime value should be filtered")
}
} else {
commandLine.appendFlags("-Xlinker", "-defaultlib: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: targetInfo,
parsedOptions: &parsedOptions,
sdkPath: targetInfo.sdkPath?.path,
isShared: hasRuntimeArgs
)

guard let swiftrtPath = targetInfo.sdkPath?.path
.appending(
components: "usr", "lib", "swift", "windows",
targetTriple.archName,
"swiftrt.obj") else {
throw ToolchainValidationError.sdkNotFound
}
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.appending(component: targetTriple.archName))
}

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

// Explicitly pass the target to the linker
commandLine.appendFlags("-target", targetTriple.triple)
stevapple marked this conversation as resolved.
Show resolved Hide resolved

// 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) })
stevapple marked this conversation as resolved.
Show resolved Hide resolved
return try getToolPath(.staticLinker(lto))
}
}
}
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
@_spi(Testing) 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]()

// An externally provided path from where we should find tools like ld
public let toolDirectory: AbsolutePath?

Expand Down
Loading