From 168d09b7c170416580eb9da146c9e56f810ef3b1 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Thu, 16 Mar 2023 14:02:07 -0700 Subject: [PATCH] [Macros] Set -external-plugin-path when the toolchain is not Xcode --- .../SwiftDriver/Jobs/FrontendJobHelpers.swift | 4 ++ .../Toolchains/DarwinToolchain.swift | 61 +++++++++++++++++++ Tests/SwiftDriverTests/SwiftDriverTests.swift | 49 +++++++++++++++ Tests/TestUtilities/DriverExtensions.swift | 6 +- 4 files changed, 118 insertions(+), 2 deletions(-) diff --git a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift index c19bfdb41..7e1ac8eab 100644 --- a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift +++ b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift @@ -275,6 +275,10 @@ extension Driver { commandLine.appendPath(localPluginPath) } + if isFrontendArgSupported(.externalPluginPath) { + try commandLine.appendAll(.externalPluginPath, from: &parsedOptions) + } + if isFrontendArgSupported(.blockListFile) { try Driver.findBlocklists(RelativeTo: try toolchain.executableDir).forEach { commandLine.appendFlag(.blockListFile) diff --git a/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift b/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift index ea1ebd1ef..ae08c02ed 100644 --- a/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift +++ b/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift @@ -373,6 +373,32 @@ public final class DarwinToolchain: Toolchain { frontendTargetInfo: FrontendTargetInfo, driver: inout Driver ) throws { + // Pass -external-plugin-path if the current toolchain is not a Xcode + // default toolchain. + if + driver.isFrontendArgSupported(.externalPluginPath), + let xcodeDir = try self.findCurrentSelectedXcodeDir(), + try !self.executableDir.isDescendant(of: xcodeDir), + let xcodeExecutableDir = try self.findXcodeExecutableDir() + { + let xcodePluginServerPath = xcodeExecutableDir + .appending(component: "swift-plugin-server") + + if fileSystem.isExecutableFile(xcodePluginServerPath) { + let xcodeToolchainUsrPath = xcodeExecutableDir.parentDirectory + + let xcodePluginPath = xcodeToolchainUsrPath + .appending(components: "lib", "swift", "host", "plugins") + commandLine.appendFlag(.externalPluginPath) + commandLine.appendFlag(xcodePluginPath.pathString + "#" + xcodePluginServerPath.pathString) + + let xcodeLocalPluginPath = xcodeToolchainUsrPath + .appending(components: "local", "lib", "swift", "host", "plugins") + commandLine.appendFlag(.externalPluginPath) + commandLine.appendFlag(xcodeLocalPluginPath.pathString + "#" + xcodePluginServerPath.pathString) + } + } + guard let sdkPath = frontendTargetInfo.sdkPath?.path, let sdkInfo = getTargetSDKInfo(sdkPath: sdkPath) else { return } @@ -441,3 +467,38 @@ private extension Version { return self.description } } + +extension DarwinToolchain { + func findXcodeExecutableDir() throws -> AbsolutePath? { +#if os(macOS) + let result = try executor.checkNonZeroExit( + args: "xcrun", "-toolchain", "default", "-f", "swiftc", + environment: env + ).trimmingCharacters(in: .whitespacesAndNewlines) + + guard !result.isEmpty else { + return nil + } + return try AbsolutePath(validating: result) + .parentDirectory // swiftc +#else + return nil +#endif + } + + func findCurrentSelectedXcodeDir() throws -> AbsolutePath? { +#if os(macOS) + let result = try executor.checkNonZeroExit( + args: "xcode-select", "-p", + environment: env + ).trimmingCharacters(in: .whitespacesAndNewlines) + + guard !result.isEmpty else { + return nil + } + return try AbsolutePath(validating: result) +#else + return nil +#endif + } +} diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index 5e58fb1d1..e0a8cfd0f 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -6739,6 +6739,55 @@ final class SwiftDriverTests: XCTestCase { XCTAssertTrue(job.commandLine.contains(.path(.absolute(try driver.toolchain.executableDir.parentDirectory.appending(components: "local", "lib", "swift", "host", "plugins"))))) } + func testExternalPluginPaths() throws { +#if !os(macOS) + throw XCTSkip("Supported only in macOS") +#endif + + try withTemporaryDirectory { tmpDir in + var driver = try Driver(args: ["swiftc", "-typecheck", "foo.swift"], + compilerExecutableDir: tmpDir) + guard driver.isFrontendArgSupported(.externalPluginPath) else { + return + } + + let jobs = try driver.planBuild().removingAutolinkExtractJobs() + XCTAssertEqual(jobs.count, 1) + let job = jobs.first! + + // This happens only Xcode toolchain has 'swift-plugin-server' which we + // don't know. + let idx1 = job.commandLine.firstIndex(of: .flag("-external-plugin-path")) + try XCTSkipIf(idx1 == nil) + switch job.commandLine[job.commandLine.index(after: idx1!)] { + case .flag(let value): + let components = value.split(separator: "#") + if components.count == 2 { + XCTAssertTrue(components[0].hasSuffix("/usr/lib/swift/host/plugins")) + XCTAssertTrue(components[1].hasSuffix("/usr/bin/swift-plugin-server")) + } else { + XCTFail("# separated count must 2") + } + default: + XCTFail("invalid arg type after '-external-plugin-path'") + } + + let idx2 = job.commandLine[job.commandLine.index(after: idx1!)...].firstIndex(of: .flag("-external-plugin-path")) + switch job.commandLine[job.commandLine.index(after: try XCTUnwrap(idx2))] { + case .flag(let value): + let components = value.split(separator: "#") + if (components.count == 2) { + XCTAssertTrue(components[0].hasSuffix("/usr/local/lib/swift/host/plugins")) + XCTAssertTrue(components[1].hasSuffix("/usr/bin/swift-plugin-server")) + } else { + XCTFail("# separated count must 2") + } + default: + XCTFail("invalid arg type after '-external-plugin-path'") + } + } + } + func testClangModuleValidateOnce() throws { let flagTest = try Driver(args: ["swiftc", "-typecheck", "foo.swift"]) guard flagTest.isFrontendArgSupported(.clangBuildSessionFile), diff --git a/Tests/TestUtilities/DriverExtensions.swift b/Tests/TestUtilities/DriverExtensions.swift index 4431b0d74..d089d7a99 100644 --- a/Tests/TestUtilities/DriverExtensions.swift +++ b/Tests/TestUtilities/DriverExtensions.swift @@ -23,7 +23,8 @@ extension Driver { env: [String: String] = ProcessEnv.vars, diagnosticsEngine: DiagnosticsEngine = DiagnosticsEngine(handlers: [Driver.stderrDiagnosticsHandler]), fileSystem: FileSystem = localFileSystem, - integratedDriver: Bool = true + integratedDriver: Bool = true, + compilerExecutableDir: AbsolutePath? = nil ) throws { let executor = try SwiftDriverExecutor(diagnosticsEngine: diagnosticsEngine, processSet: ProcessSet(), @@ -34,7 +35,8 @@ extension Driver { diagnosticsOutput: .engine(diagnosticsEngine), fileSystem: fileSystem, executor: executor, - integratedDriver: integratedDriver) + integratedDriver: integratedDriver, + compilerExecutableDir: compilerExecutableDir) } /// For tests that need to set the sdk path.