Skip to content

Commit 64a4183

Browse files
authored
Merge pull request #588 from abertelrud/error-improvements
Improved error messages for when swiftc, clang, or sysroot can't be found
2 parents ed959ff + 2736dd9 commit 64a4183

9 files changed

+109
-56
lines changed

Sources/Build/Command.compile(ClangModule).swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ struct ClangModuleBuildMetadata {
114114
}
115115

116116
extension Command {
117-
static func compile(clangModule module: ClangModule, externalModules: Set<Module>, configuration conf: Configuration, prefix: AbsolutePath, CC: String, otherArgs: [String]) throws -> [Command] {
117+
static func compile(clangModule module: ClangModule, externalModules: Set<Module>, configuration conf: Configuration, prefix: AbsolutePath, otherArgs: [String], compilerExec: AbsolutePath) throws -> [Command] {
118118

119119
let buildMeta = ClangModuleBuildMetadata(module: module, prefix: prefix, otherArgs: otherArgs)
120120

@@ -138,7 +138,7 @@ extension Command {
138138
let clang = ClangTool(desc: "Compile \(module.name) \(path.filename.asString)",
139139
inputs: buildMeta.inputs + [path.source.asString],
140140
outputs: [path.object.asString],
141-
args: [CC] + args,
141+
args: [compilerExec.asString] + args,
142142
deps: path.deps.asString)
143143

144144
let command = Command(node: path.object.asString, tool: clang)

Sources/Build/Command.compile(SwiftModule).swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import PackageLoading
1414
import Utility
1515

1616
extension Command {
17-
static func compile(swiftModule module: SwiftModule, configuration conf: Configuration, prefix: AbsolutePath, otherArgs: [String], SWIFT_EXEC: String) throws -> Command {
17+
static func compile(swiftModule module: SwiftModule, configuration conf: Configuration, prefix: AbsolutePath, otherArgs: [String], compilerExec: AbsolutePath) throws -> Command {
1818
let otherArgs = otherArgs + module.XccFlags(prefix) + (try module.pkgConfigSwiftcArgs()) + module.moduleCacheArgs(prefix: prefix)
1919
var args = ["-j\(SwiftcTool.numThreads)", "-D", "SWIFT_PACKAGE"]
2020

@@ -29,7 +29,7 @@ extension Command {
2929
args += ["-F", try platformFrameworksPath().asString]
3030
#endif
3131

32-
let tool = SwiftcTool(module: module, prefix: prefix, otherArgs: args + otherArgs, executable: SWIFT_EXEC, conf: conf)
32+
let tool = SwiftcTool(module: module, prefix: prefix, otherArgs: args + otherArgs, executable: compilerExec.asString, conf: conf)
3333
return Command(node: module.targetName, tool: tool)
3434
}
3535
}

Sources/Build/Command.link(ClangModule).swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import PackageLoading
1414
import Utility
1515

1616
extension Command {
17-
static func linkClangModule(_ product: Product, configuration conf: Configuration, prefix: AbsolutePath, otherArgs: [String], CC: String) throws -> Command {
17+
static func linkClangModule(_ product: Product, configuration conf: Configuration, prefix: AbsolutePath, otherArgs: [String], linkerExec: AbsolutePath) throws -> Command {
1818
precondition(product.containsOnlyClangModules)
1919

2020
let clangModules = product.modules.flatMap { $0 as? ClangModule }
@@ -54,7 +54,7 @@ extension Command {
5454
let shell = ShellTool(description: "Linking \(product.name)",
5555
inputs: objects.map{ $0.asString } + inputs,
5656
outputs: [productPath.asString, product.targetName],
57-
args: [CC] + args)
57+
args: [linkerExec.asString] + args)
5858

5959
return Command(node: product.targetName, tool: shell)
6060
}

Sources/Build/Command.link(SwiftModule).swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,21 @@ import Utility
1616
//FIXME messy :/
1717

1818
extension Command {
19-
static func linkSwiftModule(_ product: Product, configuration conf: Configuration, prefix: AbsolutePath, otherArgs: [String], SWIFT_EXEC: String) throws -> Command {
19+
static func linkSwiftModule(_ product: Product, configuration conf: Configuration, prefix: AbsolutePath, otherArgs: [String], linkerExec: AbsolutePath) throws -> Command {
2020

2121
// Get the unique set of all input modules.
2222
//
2323
// FIXME: This needs to handle C language targets.
2424
let buildables = OrderedSet(product.modules.flatMap{ [$0] + $0.recursiveDependencies }.flatMap{ $0 as? SwiftModule }).contents
2525

26-
var objects = buildables.flatMap { SwiftcTool(module: $0, prefix: prefix, otherArgs: [], executable: SWIFT_EXEC, conf: conf).objects }
26+
var objects = buildables.flatMap { SwiftcTool(module: $0, prefix: prefix, otherArgs: [], executable: linkerExec.asString, conf: conf).objects }
2727

2828
let outpath = prefix.appending(product.outname)
2929

3030
var args: [String]
3131
switch product.type {
3232
case .Library(.Dynamic), .Executable, .Test:
33-
args = [SWIFT_EXEC] + otherArgs
33+
args = [linkerExec.asString] + otherArgs
3434

3535
if conf == .debug {
3636
args += ["-g"]

Sources/Build/describe().swift

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,13 @@ public func describe(_ prefix: AbsolutePath, _ conf: Configuration, _ graph: Pac
3232
try makeDirectories(prefix)
3333
let swiftcArgs = flags.cCompilerFlags.flatMap{ ["-Xcc", $0] } + flags.swiftCompilerFlags + verbosity.ccArgs
3434

35-
let SWIFT_EXEC = toolchain.SWIFT_EXEC
36-
let CC = getenv("CC") ?? "clang"
37-
3835
var commands = [Command]()
3936
var targets = Targets()
4037

4138
for module in graph.modules {
4239
switch module {
4340
case let module as SwiftModule:
44-
let compile = try Command.compile(swiftModule: module, configuration: conf, prefix: prefix, otherArgs: swiftcArgs + toolchain.platformArgsSwiftc, SWIFT_EXEC: SWIFT_EXEC)
41+
let compile = try Command.compile(swiftModule: module, configuration: conf, prefix: prefix, otherArgs: swiftcArgs + toolchain.swiftPlatformArgs, compilerExec: toolchain.swiftCompiler)
4542
commands.append(compile)
4643
targets.append([compile], for: module)
4744

@@ -51,7 +48,7 @@ public func describe(_ prefix: AbsolutePath, _ conf: Configuration, _ graph: Pac
5148
if module.isTest { continue }
5249
#endif
5350
// FIXME: Find a way to eliminate `externalModules` from here.
54-
let compile = try Command.compile(clangModule: module, externalModules: graph.externalModules, configuration: conf, prefix: prefix, CC: CC, otherArgs: flags.cCompilerFlags + toolchain.platformArgsClang)
51+
let compile = try Command.compile(clangModule: module, externalModules: graph.externalModules, configuration: conf, prefix: prefix, otherArgs: flags.cCompilerFlags + toolchain.clangPlatformArgs, compilerExec: toolchain.clangCompiler)
5552
commands += compile
5653
targets.append(compile, for: module)
5754

@@ -74,9 +71,9 @@ public func describe(_ prefix: AbsolutePath, _ conf: Configuration, _ graph: Pac
7471
#endif
7572
let command: Command
7673
if product.containsOnlyClangModules {
77-
command = try Command.linkClangModule(product, configuration: conf, prefix: prefix, otherArgs: Xld, CC: CC)
74+
command = try Command.linkClangModule(product, configuration: conf, prefix: prefix, otherArgs: Xld, linkerExec: toolchain.clangCompiler)
7875
} else {
79-
command = try Command.linkSwiftModule(product, configuration: conf, prefix: prefix, otherArgs: Xld + swiftcArgs + toolchain.platformArgsSwiftc + rpathArgs, SWIFT_EXEC: SWIFT_EXEC)
76+
command = try Command.linkSwiftModule(product, configuration: conf, prefix: prefix, otherArgs: Xld + swiftcArgs + toolchain.swiftPlatformArgs + rpathArgs, linkerExec: toolchain.swiftCompiler)
8077
}
8178

8279
commands.append(command)

Sources/Build/misc.swift

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,20 @@ import func POSIX.getenv
1616
import func POSIX.popen
1717

1818
public protocol Toolchain {
19-
var platformArgsClang: [String] { get }
20-
var platformArgsSwiftc: [String] { get }
21-
var sysroot: String? { get }
22-
var SWIFT_EXEC: String { get }
23-
var clang: String { get }
19+
/// Path of the `swiftc` compiler.
20+
var swiftCompiler: AbsolutePath { get }
21+
22+
/// Platform-specific arguments for Swift compiler.
23+
var swiftPlatformArgs: [String] { get }
24+
25+
/// Path of the `clang` compiler.
26+
var clangCompiler: AbsolutePath { get }
27+
28+
/// Platform-specific arguments for Clang compiler.
29+
var clangPlatformArgs: [String] { get }
30+
31+
/// Path of the default SDK (a.k.a. "sysroot"), if any.
32+
var defaultSDK: AbsolutePath? { get }
2433
}
2534

2635
extension AbsolutePath {

Sources/Commands/Error.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import enum PackageLoading.ManifestParseError
2020

2121
public enum Error: Swift.Error {
2222
case noManifestFound
23-
case invalidToolchain
23+
case invalidToolchain(problem: String)
2424
case buildYAMLNotFound(String)
2525
case repositoryHasChanges(String)
2626
}
@@ -30,8 +30,8 @@ extension Error: FixableError {
3030
switch self {
3131
case .noManifestFound:
3232
return "no \(Manifest.filename) file found"
33-
case .invalidToolchain:
34-
return "invalid inferred toolchain"
33+
case .invalidToolchain(let problem):
34+
return "invalid inferred toolchain: \(problem)"
3535
case .buildYAMLNotFound(let value):
3636
return "no build YAML found: \(value)"
3737
case .repositoryHasChanges(let value):

Sources/Commands/UserToolchain.swift

Lines changed: 74 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,47 +15,94 @@ import protocol Build.Toolchain
1515

1616
#if os(macOS)
1717
private let whichClangArgs = ["xcrun", "--find", "clang"]
18+
private let whichDefaultSDKArgs = ["xcrun", "--sdk", "macosx", "--show-sdk-path"]
1819
#else
1920
private let whichClangArgs = ["which", "clang"]
2021
#endif
2122

2223
struct UserToolchain: Toolchain {
23-
let SWIFT_EXEC: String
24-
let clang: String
25-
let sysroot: String?
24+
/// Path of the `swiftc` compiler.
25+
let swiftCompiler: AbsolutePath
26+
27+
/// Path of the `clang` compiler.
28+
let clangCompiler: AbsolutePath
29+
30+
/// Path of the default SDK (a.k.a. "sysroot"), if any.
31+
let defaultSDK: AbsolutePath?
2632

2733
#if os(macOS)
28-
var platformArgsClang: [String] {
29-
return ["-arch", "x86_64", "-mmacosx-version-min=10.10", "-isysroot", sysroot!]
34+
var clangPlatformArgs: [String] {
35+
return ["-arch", "x86_64", "-mmacosx-version-min=10.10", "-isysroot", defaultSDK!.asString]
3036
}
31-
32-
var platformArgsSwiftc: [String] {
33-
return ["-target", "x86_64-apple-macosx10.10", "-sdk", sysroot!]
37+
var swiftPlatformArgs: [String] {
38+
return ["-target", "x86_64-apple-macosx10.10", "-sdk", defaultSDK!.asString]
3439
}
3540
#else
36-
let platformArgsClang: [String] = []
37-
let platformArgsSwiftc: [String] = []
41+
let clangPlatformArgs: [String] = []
42+
let swiftPlatformArgs: [String] = []
3843
#endif
3944

4045
init() throws {
41-
do {
42-
SWIFT_EXEC = getenv("SWIFT_EXEC")
43-
// use the swiftc installed alongside ourselves
44-
?? AbsolutePath(CommandLine.arguments[0], relativeTo: currentWorkingDirectory).parentDirectory.appending(component: "swiftc").asString
45-
46-
clang = try getenv("CC") ?? POSIX.popen(whichClangArgs).chomp()
47-
48-
#if os(macOS)
49-
sysroot = try getenv("SYSROOT") ?? POSIX.popen(["xcrun", "--sdk", "macosx", "--show-sdk-path"]).chomp()
50-
#else
51-
sysroot = nil
52-
#endif
53-
54-
guard !SWIFT_EXEC.isEmpty && !clang.isEmpty && (sysroot == nil || !sysroot!.isEmpty) else {
55-
throw Error.invalidToolchain
46+
// Find the Swift compiler, looking first in the environment.
47+
if let value = getenv("SWIFT_EXEC"), !value.isEmpty {
48+
// We have a value, but it could be an absolute or a relative path.
49+
swiftCompiler = AbsolutePath(value, relativeTo: currentWorkingDirectory)
50+
}
51+
else {
52+
// No value in env, so look for `swiftc` alongside our own binary.
53+
swiftCompiler = AbsolutePath(CommandLine.arguments[0], relativeTo: currentWorkingDirectory).parentDirectory.appending(component: "swiftc")
54+
}
55+
56+
// Check that it's valid in the file system.
57+
// FIXME: We should also check that it resolves to an executable file
58+
// (it could be a symlink to such as file).
59+
guard localFileSystem.exists(swiftCompiler) else {
60+
throw Error.invalidToolchain(problem: "could not find `swiftc` at expected path \(swiftCompiler.asString)")
61+
}
62+
63+
// Find the Clang compiler, looking first in the environment.
64+
if let value = getenv("CC"), !value.isEmpty {
65+
// We have a value, but it could be an absolute or a relative path.
66+
clangCompiler = AbsolutePath(value, relativeTo: currentWorkingDirectory)
67+
}
68+
else {
69+
// No value in env, so search for `clang`.
70+
guard let foundPath = try? POSIX.popen(whichClangArgs).chomp(), !foundPath.isEmpty else {
71+
throw Error.invalidToolchain(problem: "could not find `clang`")
72+
}
73+
clangCompiler = AbsolutePath(foundPath, relativeTo: currentWorkingDirectory)
74+
}
75+
76+
// Check that it's valid in the file system.
77+
// FIXME: We should also check that it resolves to an executable file
78+
// (it could be a symlink to such as file).
79+
guard localFileSystem.exists(clangCompiler) else {
80+
throw Error.invalidToolchain(problem: "could not find `clang` at expected path \(clangCompiler.asString)")
81+
}
82+
83+
// Find the default SDK (on macOS only).
84+
#if os(macOS)
85+
if let value = getenv("SYSROOT"), !value.isEmpty {
86+
// We have a value, but it could be an absolute or a relative path.
87+
defaultSDK = AbsolutePath(value, relativeTo: currentWorkingDirectory)
88+
}
89+
else {
90+
// No value in env, so search for it.
91+
guard let foundPath = try? POSIX.popen(whichDefaultSDKArgs).chomp(), !foundPath.isEmpty else {
92+
throw Error.invalidToolchain(problem: "could not find default SDK")
93+
}
94+
defaultSDK = AbsolutePath(foundPath, relativeTo: currentWorkingDirectory)
95+
}
96+
97+
// If we have an SDK, we check that it's valid in the file system.
98+
if let sdk = defaultSDK {
99+
// FIXME: We should probably also check that it is a directory, etc.
100+
guard localFileSystem.exists(sdk) else {
101+
throw Error.invalidToolchain(problem: "could not find default SDK at expected path \(sdk.asString)")
56102
}
57-
} catch POSIX.Error.exitStatus {
58-
throw Error.invalidToolchain
59103
}
104+
#else
105+
defaultSDK = nil
106+
#endif
60107
}
61108
}

Tests/BuildTests/DescribeTests.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ final class DescribeTests: XCTestCase {
2121
let dummyPackage = Package(manifest: Manifest(path: AbsolutePath("/"), url: "/", package: PackageDescription.Package(name: "Foo"), products: [], version: nil), path: AbsolutePath("/"), modules: [], testModules: [], products: [])
2222

2323
struct InvalidToolchain: Toolchain {
24-
var platformArgsClang: [String] { fatalError() }
25-
var platformArgsSwiftc: [String] { fatalError() }
26-
var sysroot: String? { fatalError() }
27-
var SWIFT_EXEC: String { fatalError() }
28-
var clang: String { fatalError() }
24+
var swiftCompiler: AbsolutePath { fatalError() }
25+
var clangCompiler: AbsolutePath { fatalError() }
26+
var defaultSDK: AbsolutePath? { fatalError() }
27+
var swiftPlatformArgs: [String] { fatalError() }
28+
var clangPlatformArgs: [String] { fatalError() }
2929
}
3030

3131
func testDescribingNoModulesThrows() {

0 commit comments

Comments
 (0)