Skip to content

Commit 95d7f4e

Browse files
authored
Merge pull request #26 from JhonnyBillM/SWIFT-DRIVER-OVERRIDES-YOU-AND-ME
[Toolchains] - Generalize lookup routines as a Toolchain extension.
2 parents fda643c + fc7f5d2 commit 95d7f4e

File tree

6 files changed

+120
-62
lines changed

6 files changed

+120
-62
lines changed

Sources/SwiftDriver/Toolchains/DarwinToolchain.swift

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,47 +11,30 @@
1111
//===----------------------------------------------------------------------===//
1212
import TSCBasic
1313

14-
fileprivate func envVarName(forExecutable toolName: String) -> String {
15-
return "SWIFT_DRIVER_\(toolName.uppercased())_EXEC"
16-
}
17-
1814
/// Toolchain for Darwin-based platforms, such as macOS and iOS.
1915
///
2016
/// FIXME: This class is not thread-safe.
2117
public final class DarwinToolchain: Toolchain {
2218
public let env: [String: String]
23-
24-
func xcrunFind(exec: String) throws -> AbsolutePath {
25-
if let overrideString = env[envVarName(forExecutable: exec)] {
26-
return try AbsolutePath(validating: overrideString)
27-
}
28-
29-
#if os(macOS)
30-
let path = try Process.checkNonZeroExit(
31-
arguments: ["xcrun", "-sdk", "macosx", "--find", exec],
32-
environment: env
33-
).spm_chomp()
34-
return AbsolutePath(path)
35-
#else
36-
// This is a hack so our tests work on linux. We need a better way for looking up tools in general.
37-
return AbsolutePath("/usr/bin/" + exec)
38-
#endif
19+
20+
public init(env: [String: String]) {
21+
self.env = env
3922
}
40-
23+
4124
/// Retrieve the absolute path for a given tool.
4225
public func getToolPath(_ tool: Tool) throws -> AbsolutePath {
4326
switch tool {
4427
case .swiftCompiler:
45-
return try xcrunFind(exec: "swift")
28+
return try lookup(exec: "swift")
4629

4730
case .dynamicLinker:
48-
return try xcrunFind(exec: "ld")
31+
return try lookup(exec: "ld")
4932

5033
case .staticLinker:
51-
return try xcrunFind(exec: "libtool")
34+
return try lookup(exec: "libtool")
5235

5336
case .dsymutil:
54-
return try xcrunFind(exec: "dsymutil")
37+
return try lookup(exec: "dsymutil")
5538

5639
case .clang:
5740
let result = try Process.checkNonZeroExit(
@@ -60,13 +43,13 @@ public final class DarwinToolchain: Toolchain {
6043
).spm_chomp()
6144
return AbsolutePath(result)
6245
case .swiftAutolinkExtract:
63-
return try xcrunFind(exec: "swift-autolink-extract")
46+
return try lookup(exec: "swift-autolink-extract")
6447
}
6548
}
6649

6750
/// Swift compiler path.
6851
public lazy var swiftCompiler: Result<AbsolutePath, Swift.Error> = Result {
69-
try xcrunFind(exec: "swift")
52+
try lookup(exec: "swift")
7053
}
7154

7255
/// SDK path.
@@ -88,11 +71,6 @@ public final class DarwinToolchain: Toolchain {
8871
return swiftCompiler.map{ $0.appending(RelativePath("../../lib/swift/macosx")) }
8972
}
9073

91-
92-
public init(env: [String: String]) {
93-
self.env = env
94-
}
95-
9674
public func makeLinkerOutputFilename(moduleName: String, type: LinkOutputType) -> String {
9775
switch type {
9876
case .executable: return moduleName

Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,10 @@ import TSCBasic
1313

1414
/// Toolchain for Unix-like systems.
1515
public final class GenericUnixToolchain: Toolchain {
16-
enum Error: Swift.Error {
17-
case unableToFind(tool: String)
18-
}
19-
2016
public let env: [String: String]
2117

22-
private let searchPaths: [AbsolutePath]
23-
2418
public init(env: [String: String]) {
2519
self.env = env
26-
self.searchPaths = getEnvSearchPaths(pathString: env["PATH"], currentWorkingDirectory: localFileSystem.currentWorkingDirectory)
27-
}
28-
29-
private func lookup(exec: String) throws -> AbsolutePath {
30-
if let path = lookupExecutablePath(filename: exec, searchPaths: searchPaths) {
31-
return path
32-
}
33-
34-
// If we happen to be on a macOS host, some tools might not be in our
35-
// PATH, so we'll just use xcrun to find them too.
36-
#if os(macOS)
37-
return try DarwinToolchain(env: self.env).xcrunFind(exec: exec)
38-
#else
39-
throw Error.unableToFind(tool: exec)
40-
#endif
41-
4220
}
4321

4422
public func makeLinkerOutputFilename(moduleName: String, type: LinkOutputType) -> String {

Sources/SwiftDriver/Toolchains/Toolchain.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
12+
import Foundation
1213
import TSCBasic
1314

1415
public enum Tool {
@@ -27,6 +28,8 @@ public protocol Toolchain {
2728

2829
var env: [String: String] { get }
2930

31+
var searchPaths: [AbsolutePath] { get }
32+
3033
/// Retrieve the absolute path to a particular tool.
3134
func getToolPath(_ tool: Tool) throws -> AbsolutePath
3235

@@ -59,6 +62,10 @@ public protocol Toolchain {
5962
}
6063

6164
extension Toolchain {
65+
public var searchPaths: [AbsolutePath] {
66+
getEnvSearchPaths(pathString: env["PATH"], currentWorkingDirectory: localFileSystem.currentWorkingDirectory)
67+
}
68+
6269
public func swiftCompilerVersion() throws -> String {
6370
try Process.checkNonZeroExit(
6471
args: getToolPath(.swiftCompiler).pathString, "-version",
@@ -74,4 +81,57 @@ extension Toolchain {
7481
).spm_chomp()
7582
return Triple(triple)
7683
}
84+
85+
/// Returns the `executablePath`'s directory.
86+
public var executableDir: AbsolutePath {
87+
guard let path = Bundle.main.executablePath else {
88+
fatalError("Could not find executable path.")
89+
}
90+
return AbsolutePath(path).parentDirectory
91+
}
92+
93+
/// Looks for `SWIFT_DRIVER_TOOLNAME_EXEC` in the `env` property.
94+
/// - Returns: Environment variable value, if any.
95+
func envVar(forExecutable toolName: String) -> String? {
96+
return env[envVarName(for: toolName)]
97+
}
98+
99+
/// - Returns: String in the form of: `SWIFT_DRIVER_TOOLNAME_EXEC`
100+
private func envVarName(for toolName: String) -> String {
101+
return "SWIFT_DRIVER_\(toolName.uppercased())_EXEC"
102+
}
103+
104+
/// Looks for the executable in the `SWIFT_DRIVER_TOOLNAME_EXEC` enviroment variable, if found nothing,
105+
/// looks in the `executableDir`, `xcrunFind` or in the `searchPaths`.
106+
/// - Parameter exec: executable to look for [i.e. `swift`].
107+
func lookup(exec: String) throws -> AbsolutePath {
108+
if let overrideString = envVar(forExecutable: exec) {
109+
return try AbsolutePath(validating: overrideString)
110+
} else if let path = lookupExecutablePath(filename: exec, searchPaths: [executableDir]) {
111+
return path
112+
} else if let path = try? xcrunFind(exec: exec) {
113+
return path
114+
} else if let path = lookupExecutablePath(filename: exec, searchPaths: searchPaths) {
115+
return path
116+
} else {
117+
// This is a hack so our tests work on linux. We need a better way for looking up tools in general.
118+
return AbsolutePath("/usr/bin/" + exec)
119+
}
120+
}
121+
122+
private func xcrunFind(exec: String) throws -> AbsolutePath {
123+
#if os(macOS)
124+
let path = try Process.checkNonZeroExit(
125+
arguments: ["xcrun", "-sdk", "macosx", "--find", exec],
126+
environment: env
127+
).spm_chomp()
128+
return AbsolutePath(path)
129+
#else
130+
throw ToolchainError.unableToFind(tool: exec)
131+
#endif
132+
}
133+
}
134+
135+
fileprivate enum ToolchainError: Swift.Error {
136+
case unableToFind(tool: String)
77137
}

Tests/SwiftDriverTests/JobExecutorTests.swift

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -185,15 +185,25 @@ final class JobExecutorTests: XCTestCase {
185185

186186
func testSwiftDriverExecOverride() throws {
187187
var env = ProcessEnv.vars
188+
let envVarName = "SWIFT_DRIVER_SWIFT_EXEC"
189+
let dummyPath = "/some/garbage/path/fnord"
188190

189-
env.removeValue(forKey: "SWIFT_DRIVER_SWIFT_EXEC")
190-
let normalToolchain = DarwinToolchain(env: env)
191-
let normalSwiftPath = try normalToolchain.getToolPath(.swiftCompiler)
191+
// DarwinToolchain
192+
env.removeValue(forKey: envVarName)
193+
let normalSwiftPath = try DarwinToolchain(env: env).getToolPath(.swiftCompiler)
192194
XCTAssertEqual(normalSwiftPath.basenameWithoutExt, "swift")
193195

194-
env["SWIFT_DRIVER_SWIFT_EXEC"] = "/some/garbage/path/fnord"
195-
let overriddenToolchain = DarwinToolchain(env: env)
196-
let overriddenSwiftPath = try overriddenToolchain.getToolPath(.swiftCompiler)
197-
XCTAssertEqual(overriddenSwiftPath, AbsolutePath("/some/garbage/path/fnord"))
196+
env[envVarName] = dummyPath
197+
let overriddenSwiftPath = try DarwinToolchain(env: env).getToolPath(.swiftCompiler)
198+
XCTAssertEqual(overriddenSwiftPath, AbsolutePath(dummyPath))
199+
200+
// GenericUnixToolchain
201+
env.removeValue(forKey: envVarName)
202+
let unixSwiftPath = try GenericUnixToolchain(env: env).getToolPath(.swiftCompiler)
203+
XCTAssertEqual(unixSwiftPath.basenameWithoutExt, "swift")
204+
205+
env[envVarName] = dummyPath
206+
let unixOverriddenSwiftPath = try GenericUnixToolchain(env: env).getToolPath(.swiftCompiler)
207+
XCTAssertEqual(unixOverriddenSwiftPath, AbsolutePath(dummyPath))
198208
}
199209
}

Tests/SwiftDriverTests/SwiftDriverTests.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ final class SwiftDriverTests: XCTestCase {
582582
XCTAssertFalse(cmd.contains(.flag("-dylib")))
583583
XCTAssertFalse(cmd.contains(.flag("-shared")))
584584
}
585-
585+
586586
do {
587587
// linux target
588588
var driver = try Driver(args: commonArgs + ["-emit-library", "-target", "x86_64-unknown-linux"])
@@ -989,6 +989,21 @@ final class SwiftDriverTests: XCTestCase {
989989
assertString(swiftVersion, contains: "Swift version ")
990990
#endif
991991
}
992+
993+
func testToolchainClangPath() {
994+
// TODO: remove this conditional check once DarwinToolchain does not requires xcrun to look for clang.
995+
var toolchain: Toolchain
996+
#if os(macOS)
997+
toolchain = DarwinToolchain(env: ProcessEnv.vars)
998+
#else
999+
toolchain = GenericUnixToolchain(env: ProcessEnv.vars)
1000+
#endif
1001+
1002+
XCTAssertEqual(
1003+
try? toolchain.getToolPath(.swiftCompiler).parentDirectory,
1004+
try? toolchain.getToolPath(.clang).parentDirectory
1005+
)
1006+
}
9921007
}
9931008

9941009
func assertString(

Tests/SwiftDriverTests/XCTestManifests.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,17 @@ extension PrefixTrieTests {
5454
]
5555
}
5656

57+
extension StringAdditionsTests {
58+
// DO NOT MODIFY: This is autogenerated, use:
59+
// `swift test --generate-linuxmain`
60+
// to regenerate.
61+
static let __allTests__StringAdditionsTests = [
62+
("testBasicIdentifiers", testBasicIdentifiers),
63+
("testSwiftKeywordsAsIdentifiers", testSwiftKeywordsAsIdentifiers),
64+
("testUnicodeCharacters", testUnicodeCharacters),
65+
]
66+
}
67+
5768
extension SwiftDriverTests {
5869
// DO NOT MODIFY: This is autogenerated, use:
5970
// `swift test --generate-linuxmain`
@@ -70,16 +81,21 @@ extension SwiftDriverTests {
7081
("testMergeModulesOnly", testMergeModulesOnly),
7182
("testModuleNameFallbacks", testModuleNameFallbacks),
7283
("testModuleSettings", testModuleSettings),
84+
("testMultithreading", testMultithreading),
85+
("testMultithreadingDiagnostics", testMultithreadingDiagnostics),
7386
("testOutputFileMapLoading", testOutputFileMapLoading),
7487
("testOutputFileMapStoring", testOutputFileMapStoring),
7588
("testParseErrors", testParseErrors),
7689
("testParsing", testParsing),
7790
("testPrimaryOutputKinds", testPrimaryOutputKinds),
91+
("testPrimaryOutputKindsDiagnostics", testPrimaryOutputKindsDiagnostics),
7892
("testRegressions", testRegressions),
7993
("testResponseFileExpansion", testResponseFileExpansion),
94+
("testResponseFileTokenization", testResponseFileTokenization),
8095
("testSanitizerArgs", testSanitizerArgs),
8196
("testStandardCompileJobs", testStandardCompileJobs),
8297
("testTargetTriple", testTargetTriple),
98+
("testToolchainClangPath", testToolchainClangPath),
8399
("testToolchainUtilities", testToolchainUtilities),
84100
]
85101
}
@@ -109,6 +125,7 @@ public func __allTests() -> [XCTestCaseEntry] {
109125
testCase(JobExecutorTests.__allTests__JobExecutorTests),
110126
testCase(ParsableMessageTests.__allTests__ParsableMessageTests),
111127
testCase(PrefixTrieTests.__allTests__PrefixTrieTests),
128+
testCase(StringAdditionsTests.__allTests__StringAdditionsTests),
112129
testCase(SwiftDriverTests.__allTests__SwiftDriverTests),
113130
testCase(TripleTests.__allTests__TripleTests),
114131
]

0 commit comments

Comments
 (0)