Skip to content

SwiftDriver: initial (incomplete) windows port #878

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

Merged
merged 1 commit into from
Oct 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions Sources/SwiftDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,14 @@ add_library(SwiftDriver
Jobs/VerifyDebugInfoJob.swift
Jobs/VerifyModuleInterfaceJob.swift
Jobs/WebAssemblyToolchain+LinkerSupport.swift
Jobs/WindowsToolchain+LinkerSupport.swift
Jobs/PrebuiltModulesJob.swift

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

Utilities/DOTJobGraphSerializer.swift
Utilities/DateAdditions.swift
Expand Down
26 changes: 14 additions & 12 deletions Sources/SwiftDriver/Driver/Driver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -787,7 +787,7 @@ extension Driver {
let execName = try VirtualPath(path: args[0]).basenameWithoutExt

// If we are not run as 'swift' or 'swiftc' or there are no program arguments, always invoke as normal.
guard execName == "swift" || execName == "swiftc", args.count > 1 else {
guard [executableName("swift"), executableName("swiftc")].contains(execName), args.count > 1 else {
return (.normal(isRepl: false), args)
}

Expand All @@ -797,13 +797,13 @@ extension Driver {

// Check for flags associated with frontend tools.
if firstArg == "-frontend" {
updatedArgs.replaceSubrange(0...1, with: ["swift-frontend"])
return (.subcommand("swift-frontend"), updatedArgs)
updatedArgs.replaceSubrange(0...1, with: [executableName("swift-frontend")])
return (.subcommand(executableName("swift-frontend")), updatedArgs)
}

if firstArg == "-modulewrap" {
updatedArgs[0] = "swift-frontend"
return (.subcommand("swift-frontend"), updatedArgs)
updatedArgs[0] = executableName("swift-frontend")
return (.subcommand(executableName("swift-frontend")), updatedArgs)
}

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

let subcommand = "swift-\(firstArg)"
let subcommand = executableName("swift-\(firstArg)")

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

Expand Down Expand Up @@ -2222,10 +2222,12 @@ extension Driver {

if !fileSystem.exists(path) {
diagnosticsEngine.emit(.warning_no_such_sdk(sdkPath))
} else if isSDKTooOld(sdkPath: path, fileSystem: fileSystem,
diagnosticsEngine: diagnosticsEngine) {
diagnosticsEngine.emit(.error_sdk_too_old(sdkPath))
return nil
} else if !(targetTriple?.isWindows ?? (defaultToolchainType == WindowsToolchain.self)) {
if isSDKTooOld(sdkPath: path, fileSystem: fileSystem,
diagnosticsEngine: diagnosticsEngine) {
diagnosticsEngine.emit(.error_sdk_too_old(sdkPath))
return nil
}
}

return .absolute(path)
Expand Down Expand Up @@ -2510,7 +2512,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 @@ -2523,7 +2525,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
24 changes: 24 additions & 0 deletions Sources/SwiftDriver/Driver/WindowsExtensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//===------------- WindowsExtensions.swift - Windows Extensions -----------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 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 Foundation

internal func executableName(_ name: String) -> String {
#if os(Windows)
if name.suffix(from: name.index(name.endIndex, offsetBy: -4)) == ".exe" {
return name
}
return "\(name).exe"
#else
return name
#endif
}
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,14 @@ public extension Driver {
@_spi(Testing) public extension Driver {
static func getScanLibPath(of toolchain: Toolchain, hostTriple: Triple,
env: [String: String]) throws -> AbsolutePath {
if hostTriple.isWindows {
// no matter if we are in a build tree or an installed tree, the layout is
// always: `bin/_InternalSwiftScan.dll`
return try getRootPath(of: toolchain, env: env)
.appending(component: "bin")
.appending(component: "_InternalSwiftScan.dll")
}

let sharedLibExt: String
if hostTriple.isMacOSX {
sharedLibExt = ".dylib"
Expand Down
13 changes: 13 additions & 0 deletions Sources/SwiftDriver/Jobs/Toolchain+InterpreterSupport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,16 @@ extension GenericUnixToolchain {
return envVars
}
}

extension WindowsToolchain {
public func platformSpecificInterpreterEnvironmentVariables(
env: [String : String],
parsedOptions: inout ParsedOptions,
sdkPath: VirtualPath.Handle?,
targetInfo: FrontendTargetInfo) throws -> [String: String] {

// TODO(compnerd): setting up `Path` is meaningless currently as the lldb
// support required for the interpreter mode fails to load the dependencies.
return [:]
}
}
130 changes: 130 additions & 0 deletions Sources/SwiftDriver/Jobs/WindowsToolchain+LinkerSupport.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
//===--------------- WindowsToolchain+LinkerSupport.swift -----------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 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

if !targetTriple.triple.isEmpty {
commandLine.appendFlag("-target")
commandLine.appendFlag(targetTriple.triple)
}

switch linkerOutputType {
case .staticLibrary:
break
case .dynamicLibrary:
commandLine.appendFlag("-shared")
case .executable:
break
}

// Select the linker to use.
if let arg = parsedOptions.getLastArgument(.useLd) {
commandLine.appendFlag("-fuse-ld=\(arg.asSingle)")
} else if lto != nil {
commandLine.appendFlag("-fuse-ld=lld")
}

switch lto {
case .some(.llvmThin):
commandLine.appendFlag("-flto=thin")
case .some(.llvmFull):
commandLine.appendFlag("-flto=full")
case .none:
break
}

// FIXME(compnerd): render `-Xlinker /DEBUG` or `-Xlinker /DEBUG:DWARF` with
// DWARF + lld

// Rely on `-libc` to correctly identify the MSVC Runtime Library. We use
// `-nostartfiles` as that limits the difference to just the
// `-defaultlib:libcmt` which is passed unconditionally with the `clang`
// driver rather than the `clang-cl` driver.
commandLine.appendFlag("-nostartfiles")

// TODO(compnerd) investigate the proper way to port this logic over from
// the C++ driver.

// Since Windows has separate libraries per architecture, link against the
// architecture specific version of the static library.
commandLine.appendFlag(.L)
commandLine.appendPath(VirtualPath.lookup(targetInfo.runtimeLibraryImportPaths.last!.path))

// FIXME(compnerd) figure out how to ensure that the SDK relative path is
// the last one
commandLine.appendPath(VirtualPath.lookup(targetInfo.runtimeLibraryImportPaths.last!.path)
.appending(component: "swiftrt.obj"))

commandLine.append(contentsOf: inputs.compactMap { (input: TypedVirtualPath) -> Job.ArgTemplate? in
switch input.type {
case .object, .llvmBitcode:
return .path(input.file)
default:
return nil
}
})

for framework in parsedOptions.arguments(for: .F, .Fsystem) {
commandLine.appendFlag(framework.option == .Fsystem ? "-iframework" : "-F")
try commandLine.appendPath(VirtualPath(path: framework.argument.asSingle))
}

if let sdkPath = targetInfo.sdkPath?.path {
commandLine.appendFlag("-I")
commandLine.appendPath(VirtualPath.lookup(sdkPath))
}

if let stdlib = parsedOptions.getLastArgument(.experimentalCxxStdlib) {
commandLine.appendFlag("-stdlib=\(stdlib.asSingle)")
}

// FIXME(compnerd) render asan/ubsan runtime link for executables

if parsedOptions.contains(.profileGenerate) {
commandLine.appendFlag("-Xlinker")
// FIXME(compnerd) wrap llvm::getInstrProfRuntimeHookVarName()
commandLine.appendFlag("-include:__llvm_profile_runtime")
commandLine.appendFlag("-lclang_rt.profile")
}

for option in parsedOptions.arguments(for: .Xlinker) {
commandLine.appendFlag(.Xlinker)
commandLine.appendFlag(option.argument.asSingle)
}
// TODO(compnerd) is there a separate equivalent to OPT_linker_option_group?
try commandLine.appendAllArguments(.XclangLinker, from: &parsedOptions)

if parsedOptions.contains(.v) {
commandLine.appendFlag("-v")
}

commandLine.appendFlag("-o")
commandLine.appendPath(outputFile)

// TODO(compnerd) handle static libraries
return try getToolPath(.clang)
}
}
34 changes: 29 additions & 5 deletions Sources/SwiftDriver/Toolchains/Toolchain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,14 @@ public protocol Toolchain {

extension Toolchain {
public var searchPaths: [AbsolutePath] {
getEnvSearchPaths(pathString: env["PATH"], currentWorkingDirectory: fileSystem.currentWorkingDirectory)
// Conditionalize this on the build time host because cross-compiling from
// a non-Windows host, we would use a Windows toolchain, but would want to
// use the platform variable for the path.
#if os(Windows)
return getEnvSearchPaths(pathString: env["Path"], currentWorkingDirectory: fileSystem.currentWorkingDirectory)
#else
return getEnvSearchPaths(pathString: env["PATH"], currentWorkingDirectory: fileSystem.currentWorkingDirectory)
#endif
}

/// Returns the `executablePath`'s directory.
Expand All @@ -131,6 +138,9 @@ extension Toolchain {
private func envVarName(for toolName: String) -> String {
let lookupName = toolName
.replacingOccurrences(of: "-", with: "_")
// FIXME(compnerd) we should extract the extension for generating the
// toolname rather than assuming that we can convert the tool name blindly
.replacingOccurrences(of: ".", with: "_")
Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I guess it would be nice if the environment variables were consistent across platforms. So windows doesn't always have an extra _EXE in its variables.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I think that we will need to resolve this, but I think that doing that subsequently is better than trying to address everything immediately.

.uppercased()
return "SWIFT_DRIVER_\(lookupName)_EXEC"
}
Expand All @@ -157,7 +167,7 @@ extension Toolchain {
return path
} else if let path = try? xcrunFind(executable: executable) {
return path
} else if !["swift-frontend", "swift"].contains(executable),
} else if ![executableName("swift-frontend"), executableName("swift")].contains(executable),
let parentDirectory = try? getToolPath(.swiftCompiler).parentDirectory,
parentDirectory != executableDir,
let path = lookupExecutablePath(filename: executable, searchPaths: [parentDirectory]) {
Expand All @@ -166,11 +176,25 @@ extension Toolchain {
return path
} else if let path = lookupExecutablePath(filename: executable, searchPaths: searchPaths) {
return path
} else if executable == "swift-frontend" {
} else if executable == executableName("swift-frontend") {
// Temporary shim: fall back to looking for "swift" before failing.
return try lookup(executable: "swift")
return try lookup(executable: executableName("swift"))
} else if fallbackToExecutableDefaultPath {
return AbsolutePath("/usr/bin/" + executable)
if self is WindowsToolchain {
if let DEVELOPER_DIR = env["DEVELOPER_DIR"] {
return AbsolutePath(DEVELOPER_DIR)
.appending(component: "Toolchains")
.appending(component: "unknown-Asserts-development.xctoolchain")
.appending(component: "usr")
.appending(component: "bin")
.appending(component: executable)
}
return try getToolPath(.swiftCompiler)
.parentDirectory
.appending(component: executable)
} else {
return AbsolutePath("/usr/bin/" + executable)
}
} else {
throw ToolchainError.unableToFind(tool: executable)
}
Expand Down
Loading