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

Fail compilation with an error diagnostic when specified SWIFT_DRIVER_<tool>_EXEC argument does not exist #1621

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
34 changes: 29 additions & 5 deletions Sources/SwiftDriver/Toolchains/Toolchain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import class Foundation.Bundle

import func TSCBasic.getEnvSearchPaths
import func TSCBasic.lookupExecutablePath
import protocol TSCBasic.DiagnosticData
import class TSCBasic.DiagnosticsEngine
import protocol TSCBasic.FileSystem
import struct TSCBasic.AbsolutePath
Expand Down Expand Up @@ -215,29 +216,41 @@ extension Toolchain {
/// looks in the `executableDir`, `xcrunFind` or in the `searchPaths`.
/// - Parameter executable: executable to look for [i.e. `swift`]. Executable suffix (eg. `.exe`) should be omitted.
func lookup(executable: String) throws -> AbsolutePath {
// 1. Check the `SWIFT_DRIVER_<TOOLNAME>_EXEC` override.
if let overrideString = envVar(forExecutable: executable),
let path = try? AbsolutePath(validating: overrideString) {
if !fallbackToExecutableDefaultPath && !fileSystem.isExecutableFile(path) {
throw ToolchainError.notAValidExecutablePath(path.pathString)
}
return path
// 2. If `-tools-directory` is set, check there.
} else if let toolDir = toolDirectory,
let path = lookupExecutablePath(filename: executableName(executable), currentWorkingDirectory: nil, searchPaths: [toolDir]) {
// Looking for tools from the tools directory.
return path
// 3. Perform lookup relative to the driver's executable
} else if let path = lookupExecutablePath(filename: executableName(executable), currentWorkingDirectory: nil, searchPaths: [try executableDir]) {
return path
// 4. Attempt lookup with `xcrun --find`.
} else if let path = try? xcrunFind(executable: executableName(executable)) {
return path
// 5. If querying not the compiler frontend itself and the above attempts failed,
// attempt to resolve adjacent to the compiler frontend.
} else if !["swift-frontend", "swift"].contains(executable),
let parentDirectory = try? getToolPath(.swiftCompiler).parentDirectory,
try parentDirectory != executableDir,
let path = lookupExecutablePath(filename: executableName(executable), searchPaths: [parentDirectory]) {
// If the driver library's client and the frontend are in different directories,
// try looking for tools next to the frontend.
return path
// 6. Perform lookup in the toolchain search paths (e.g. $PATH)
} else if let path = lookupExecutablePath(filename: executableName(executable), searchPaths: searchPaths) {
return path
// 7. Attempt lookup of `swift` for the compiler frontned
// FIXME: we should remove this now
} else if executable == "swift-frontend" {
// Temporary shim: fall back to looking for "swift" before failing.
return try lookup(executable: "swift")
// 8. For testing purposes, attempt lookup in the system "default" paths
} else if fallbackToExecutableDefaultPath {
if self is WindowsToolchain {
return try getToolPath(.swiftCompiler)
Expand All @@ -247,7 +260,7 @@ extension Toolchain {
return try AbsolutePath(validating: "/usr/bin/" + executable)
}
} else {
throw ToolchainError.unableToFind(tool: executable)
throw ToolchainError.unableToFind(executable)
}
}

Expand Down Expand Up @@ -302,7 +315,7 @@ extension Toolchain {
private func xcrunFind(executable: String) throws -> AbsolutePath {
let xcrun = "xcrun"
guard lookupExecutablePath(filename: xcrun, searchPaths: searchPaths) != nil else {
throw ToolchainError.unableToFind(tool: xcrun)
throw ToolchainError.unableToFind(xcrun)
}

let path = try executor.checkNonZeroExit(
Expand Down Expand Up @@ -368,6 +381,17 @@ extension Toolchain {
}
}

@_spi(Testing) public enum ToolchainError: Swift.Error {
case unableToFind(tool: String)
@_spi(Testing) public enum ToolchainError: Swift.Error, Equatable, DiagnosticData {
case unableToFind(String)
case notAValidExecutablePath(String)

public var description: String {
switch self {
case .unableToFind(let tool):
return "unable to locate tool: '\(tool)'"
case .notAValidExecutablePath(let path):
return "not a valid executable: \(path)"

}
}
}
9 changes: 8 additions & 1 deletion Tests/SwiftDriverTests/SwiftDriverTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ final class SwiftDriverTests: XCTestCase {
// so there is no swift-help in the toolchain yet. Set the environment variable
// as if we had found it for the purposes of testing build planning.
var env = ProcessEnv.vars
env["SWIFT_DRIVER_TESTS_ENABLE_EXEC_PATH_FALLBACK"] = "1"
env["SWIFT_DRIVER_SWIFT_HELP_EXEC"] = "/tmp/.test-swift-help"
return env
}
Expand Down Expand Up @@ -2387,6 +2388,7 @@ final class SwiftDriverTests: XCTestCase {

func testWebAssemblyUnsupportedFeatures() throws {
var env = ProcessEnv.vars
env["SWIFT_DRIVER_TESTS_ENABLE_EXEC_PATH_FALLBACK"] = "1"
env["SWIFT_DRIVER_SWIFT_AUTOLINK_EXTRACT_EXEC"] = "/garbage/swift-autolink-extract"
do {
var driver = try Driver(args: ["swift", "-target", "wasm32-unknown-wasi", "foo.swift"], env: env)
Expand Down Expand Up @@ -3140,7 +3142,7 @@ final class SwiftDriverTests: XCTestCase {
"-emit-library", "-driver-filelist-threshold=0"
])

var jobs = try driver.planBuild()
let jobs = try driver.planBuild()
XCTAssertEqual(jobs.count, 4)
XCTAssertEqual(getFileListElements(for: "-filelist", job: jobs[2]),
[.temporary(try .init(validating: "hello-1.o"))])
Expand Down Expand Up @@ -4433,6 +4435,7 @@ final class SwiftDriverTests: XCTestCase {
// Drop SWIFT_DRIVER_CLANG_EXEC from the environment so it doesn't
// interfere with tool lookup.
var env = ProcessEnv.vars
env["SWIFT_DRIVER_TESTS_ENABLE_EXEC_PATH_FALLBACK"] = "1"
env.removeValue(forKey: "SWIFT_DRIVER_CLANG_EXEC")

var driver = try Driver(args: ["swiftc",
Expand Down Expand Up @@ -4837,6 +4840,7 @@ final class SwiftDriverTests: XCTestCase {
// As per Unix conventions, /var/empty is expected to exist and be empty.
// This gives us a non-existent path that we can use for libtool which
// allows us to run this this on non-Darwin platforms.
env["SWIFT_DRIVER_TESTS_ENABLE_EXEC_PATH_FALLBACK"] = "1"
env["SWIFT_DRIVER_LIBTOOL_EXEC"] = "/var/empty/libtool"

// No dSYM generation (-g -emit-library -static)
Expand Down Expand Up @@ -6696,6 +6700,7 @@ final class SwiftDriverTests: XCTestCase {

func testEmbeddedSwiftOptions() throws {
var env = ProcessEnv.vars
env["SWIFT_DRIVER_TESTS_ENABLE_EXEC_PATH_FALLBACK"] = "1"
env["SWIFT_DRIVER_SWIFT_AUTOLINK_EXTRACT_EXEC"] = "/garbage/swift-autolink-extract"

do {
Expand Down Expand Up @@ -6813,6 +6818,7 @@ final class SwiftDriverTests: XCTestCase {
// better override.
var env = ProcessEnv.vars
let swiftHelp: AbsolutePath = try AbsolutePath(validating: "/usr/bin/nonexistent-swift-help")
env["SWIFT_DRIVER_TESTS_ENABLE_EXEC_PATH_FALLBACK"] = "1"
env["SWIFT_DRIVER_SWIFT_HELP_EXEC"] = swiftHelp.pathString
env["SWIFT_DRIVER_CLANG_EXEC"] = "/usr/bin/clang"
var driver = try Driver(
Expand All @@ -6826,6 +6832,7 @@ final class SwiftDriverTests: XCTestCase {
func testSwiftClangOverride() throws {
var env = ProcessEnv.vars
let swiftClang = try AbsolutePath(validating: "/A/Path/swift-clang")
env["SWIFT_DRIVER_TESTS_ENABLE_EXEC_PATH_FALLBACK"] = "1"
env["SWIFT_DRIVER_CLANG_EXEC"] = swiftClang.pathString

var driver = try Driver(
Expand Down