diff --git a/Sources/Build/BuildPlan.swift b/Sources/Build/BuildPlan.swift index b39bc9b919f..03e8d45c3a4 100644 --- a/Sources/Build/BuildPlan.swift +++ b/Sources/Build/BuildPlan.swift @@ -963,8 +963,7 @@ public final class SwiftTargetBuildDescription { /// When `scanInvocation` argument is set to `true`, omit the side-effect producing arguments /// such as emitting a module or supplementary outputs. public func emitCommandLine(scanInvocation: Bool = false) throws -> [String] { - var result: [String] = [] - result.append(buildParameters.toolchain.swiftCompilerPath.pathString) + var result: [String] = buildParameters.toolchain.commandLineForSwiftCompilation(fileSystem: fileSystem) result.append("-module-name") result.append(target.c99name) @@ -1006,8 +1005,7 @@ public final class SwiftTargetBuildDescription { throw InternalError("expecting emitSwiftModuleSeparately in build parameters") } - var result: [String] = [] - result.append(buildParameters.toolchain.swiftCompilerPath.pathString) + var result: [String] = buildParameters.toolchain.commandLineForSwiftCompilation(fileSystem: fileSystem) result.append("-module-name") result.append(target.c99name) @@ -1053,8 +1051,7 @@ public final class SwiftTargetBuildDescription { throw InternalError("expecting emitSwiftModuleSeparately in build parameters") } - var result: [String] = [] - result.append(buildParameters.toolchain.swiftCompilerPath.pathString) + var result: [String] = buildParameters.toolchain.commandLineForSwiftCompilation(fileSystem: fileSystem) result.append("-module-name") result.append(target.c99name) diff --git a/Sources/Build/LLBuildManifestBuilder.swift b/Sources/Build/LLBuildManifestBuilder.swift index 66c6f45ad37..6f98b618464 100644 --- a/Sources/Build/LLBuildManifestBuilder.swift +++ b/Sources/Build/LLBuildManifestBuilder.swift @@ -510,6 +510,13 @@ extension LLBuildManifestBuilder { let isLibrary = target.target.type == .library || target.target.type == .test let cmdName = target.target.getCommandName(config: buildConfig) + let commandLine = buildParameters.toolchain.commandLineForSwiftCompilation(fileSystem: fileSystem) + guard let first = commandLine.first, let executable = try? AbsolutePath(validating: first) else { + throw StringError("empty commandline for Swift compilation") + } + + // TODO: `--driver-mode=swiftc` needs to be the first argument, but going through `SwiftCompilerTool` does not allow that + manifest.addSwiftCmd( name: cmdName, inputs: inputs, diff --git a/Sources/PackageLoading/ManifestLoader.swift b/Sources/PackageLoading/ManifestLoader.swift index 9baa2f56e3b..16c27e30248 100644 --- a/Sources/PackageLoading/ManifestLoader.swift +++ b/Sources/PackageLoading/ManifestLoader.swift @@ -510,7 +510,7 @@ public final class ManifestLoader: ManifestLoaderProtocol { let moduleCachePath = try (ProcessEnv.vars["SWIFTPM_MODULECACHE_OVERRIDE"] ?? ProcessEnv.vars["SWIFTPM_TESTS_MODULECACHE"]).flatMap{ try AbsolutePath(validating: $0) } var cmd: [String] = [] - cmd += [self.toolchain.swiftCompilerPathForManifests.pathString] + cmd += self.toolchain.commandLineForManifestCompilation(fileSystem: localFileSystem) // if runtimePath is set to "PackageFrameworks" that means we could be developing SwiftPM in Xcode // which produces a framework for dynamic package products. diff --git a/Sources/PackageModel/Toolchain.swift b/Sources/PackageModel/Toolchain.swift index d14b3aa6829..42bb4bc2495 100644 --- a/Sources/PackageModel/Toolchain.swift +++ b/Sources/PackageModel/Toolchain.swift @@ -19,6 +19,9 @@ public protocol Toolchain { /// Path of the `swiftc` compiler. var swiftCompilerPath: AbsolutePath { get } + /// Path of the `swiftc` compiler to use for manifest compilation. + var swiftCompilerPathForManifests: AbsolutePath { get } + /// Path containing the macOS Swift stdlib. var macosSwiftStdlib: AbsolutePath { get throws } @@ -75,4 +78,21 @@ extension Toolchain { public var extraSwiftCFlags: [String] { extraFlags.swiftCompilerFlags } + + private static func commandLineForCompilation(compilerPath: AbsolutePath, fileSystem: FileSystem) -> [String] { + let swiftDriverPath = compilerPath.parentDirectory.appending(component: "swift-driver") + if fileSystem.exists(swiftDriverPath) { + return [swiftDriverPath.pathString, "--driver-mode=swiftc"] + } else { + return [compilerPath.pathString] + } + } + + public func commandLineForManifestCompilation(fileSystem: FileSystem) -> [String] { + return Self.commandLineForCompilation(compilerPath: swiftCompilerPathForManifests, fileSystem: fileSystem) + } + + public func commandLineForSwiftCompilation(fileSystem: FileSystem) -> [String] { + return Self.commandLineForCompilation(compilerPath: swiftCompilerPath, fileSystem: fileSystem) + } } diff --git a/Sources/SPMTestSupport/Toolchain.swift b/Sources/SPMTestSupport/Toolchain.swift index 6a50eb9a954..c88e02482a3 100644 --- a/Sources/SPMTestSupport/Toolchain.swift +++ b/Sources/SPMTestSupport/Toolchain.swift @@ -94,28 +94,4 @@ extension UserToolchain { return true #endif } - - /// Helper function to determine whether serialized diagnostics work properly in the current environment. - public func supportsSerializedDiagnostics(otherSwiftFlags: [String] = []) -> Bool { - do { - try testWithTemporaryDirectory { tmpPath in - let inputPath = tmpPath.appending(component: "best.swift") - try localFileSystem.writeFileContents(inputPath, string: "func foo() -> Bool {\nvar unused: Int\nreturn true\n}\n") - let outputPath = tmpPath.appending(component: "foo") - let serializedDiagnosticsPath = tmpPath.appending(component: "out.dia") - let toolchainPath = self.swiftCompilerPath.parentDirectory.parentDirectory - try Process.checkNonZeroExit(arguments: ["/usr/bin/xcrun", "--toolchain", toolchainPath.pathString, "swiftc", inputPath.pathString, "-Xfrontend", "-serialize-diagnostics-path", "-Xfrontend", serializedDiagnosticsPath.pathString, "-g", "-o", outputPath.pathString] + otherSwiftFlags) - try Process.checkNonZeroExit(arguments: [outputPath.pathString]) - - let diaFileContents = try localFileSystem.readFileContents(serializedDiagnosticsPath) - let diagnosticsSet = try SerializedDiagnostics(bytes: diaFileContents) - if diagnosticsSet.diagnostics.isEmpty { - throw StringError("does not support diagnostics") - } - } - return true - } catch { - return false - } - } } diff --git a/Sources/Workspace/DefaultPluginScriptRunner.swift b/Sources/Workspace/DefaultPluginScriptRunner.swift index dcdfe6c0aca..90b85d4e856 100644 --- a/Sources/Workspace/DefaultPluginScriptRunner.swift +++ b/Sources/Workspace/DefaultPluginScriptRunner.swift @@ -121,9 +121,9 @@ public struct DefaultPluginScriptRunner: PluginScriptRunner, Cancellable { // FIXME: Much of this is similar to what the ManifestLoader is doing. This should be consolidated. // We use the toolchain's Swift compiler for compiling the plugin. - var commandLine = [self.toolchain.swiftCompilerPathForManifests.pathString] + var commandLine = self.toolchain.commandLineForManifestCompilation(fileSystem: fileSystem) - observabilityScope.emit(debug: "Using compiler \(self.toolchain.swiftCompilerPathForManifests.pathString)") + observabilityScope.emit(debug: "Using compiler command line: \(commandLine)") // Get access to the path containing the PackagePlugin module and library. let pluginLibraryPath = self.toolchain.swiftPMLibrariesLocation.pluginLibraryPath diff --git a/Tests/BuildTests/MockBuildTestHelper.swift b/Tests/BuildTests/MockBuildTestHelper.swift index 07af9660c07..fb1a6ab72bb 100644 --- a/Tests/BuildTests/MockBuildTestHelper.swift +++ b/Tests/BuildTests/MockBuildTestHelper.swift @@ -17,6 +17,7 @@ struct MockToolchain: PackageModel.Toolchain { let librarianPath = AbsolutePath(path: "/fake/path/to/ar") #endif let swiftCompilerPath = AbsolutePath(path: "/fake/path/to/swiftc") + let swiftCompilerPathForManifests = AbsolutePath(path: "/fake/path/to/swiftc") #if os(macOS) let extraFlags = BuildFlags(cxxCompilerFlags: ["-lc++"]) diff --git a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift index 7e04d9d666b..4f4dfe0a6bf 100644 --- a/Tests/SPMBuildCoreTests/PluginInvocationTests.swift +++ b/Tests/SPMBuildCoreTests/PluginInvocationTests.swift @@ -412,14 +412,12 @@ class PluginInvocationTests: XCTestCase { XCTAssertEqual(delegate.compiledResult, result) XCTAssertNil(delegate.cachedResult) - if try UserToolchain.default.supportsSerializedDiagnostics() { - // Check the serialized diagnostics. We should no longer have an error but now have a warning. - let diaFileContents = try localFileSystem.readFileContents(result.diagnosticsFile) - let diagnosticsSet = try SerializedDiagnostics(bytes: diaFileContents) - XCTAssertEqual(diagnosticsSet.diagnostics.count, 1, "unexpected diagnostics count in \(diagnosticsSet.diagnostics) from \(result.diagnosticsFile.pathString)") - let warningDiagnostic = try XCTUnwrap(diagnosticsSet.diagnostics.first) - XCTAssertTrue(warningDiagnostic.text.hasPrefix("variable \'unused\' was never used"), "\(warningDiagnostic)") - } + // Check the serialized diagnostics. We should no longer have an error but now have a warning. + let diaFileContents = try localFileSystem.readFileContents(result.diagnosticsFile) + let diagnosticsSet = try SerializedDiagnostics(bytes: diaFileContents) + XCTAssertEqual(diagnosticsSet.diagnostics.count, 1, "unexpected diagnostics count in \(diagnosticsSet.diagnostics) from \(result.diagnosticsFile.pathString)") + let warningDiagnostic = try XCTUnwrap(diagnosticsSet.diagnostics.first) + XCTAssertTrue(warningDiagnostic.text.hasPrefix("variable \'unused\' was never used"), "\(warningDiagnostic)") // Check that the executable file exists. XCTAssertTrue(localFileSystem.exists(result.executableFile), "\(result.executableFile.pathString)") @@ -457,14 +455,12 @@ class PluginInvocationTests: XCTestCase { XCTAssertNil(delegate.compiledResult) XCTAssertEqual(delegate.cachedResult, result) - if try UserToolchain.default.supportsSerializedDiagnostics() { - // Check that the diagnostics still have the same warning as before. - let diaFileContents = try localFileSystem.readFileContents(result.diagnosticsFile) - let diagnosticsSet = try SerializedDiagnostics(bytes: diaFileContents) - XCTAssertEqual(diagnosticsSet.diagnostics.count, 1) - let warningDiagnostic = try XCTUnwrap(diagnosticsSet.diagnostics.first) - XCTAssertTrue(warningDiagnostic.text.hasPrefix("variable \'unused\' was never used"), "\(warningDiagnostic)") - } + // Check that the diagnostics still have the same warning as before. + let diaFileContents = try localFileSystem.readFileContents(result.diagnosticsFile) + let diagnosticsSet = try SerializedDiagnostics(bytes: diaFileContents) + XCTAssertEqual(diagnosticsSet.diagnostics.count, 1) + let warningDiagnostic = try XCTUnwrap(diagnosticsSet.diagnostics.first) + XCTAssertTrue(warningDiagnostic.text.hasPrefix("variable \'unused\' was never used"), "\(warningDiagnostic)") // Check that the executable file exists. XCTAssertTrue(localFileSystem.exists(result.executableFile), "\(result.executableFile.pathString)") diff --git a/Utilities/bootstrap b/Utilities/bootstrap index 1c9b926a138..3c1c1be2bee 100755 --- a/Utilities/bootstrap +++ b/Utilities/bootstrap @@ -645,6 +645,9 @@ def build_swiftpm_with_swiftpm(args, integrated_swift_driver): symlink_force(args.swiftc_path, os.path.join(args.target_dir, args.conf, "swift")) symlink_force(args.swiftc_path, os.path.join(args.target_dir, args.conf, "swift-autolink-extract")) + # Symlink swift-driver built by CMake into the "fake" toolchain + symlink_force(os.path.join(args.bootstrap_dir, "..", "swift-driver", "bin", "swift-driver"), os.path.join(args.target_dir, args.conf, "swift-driver")) + lib_dir = os.path.join(args.target_dir, "lib", "swift") # Remove old cruft.