Skip to content

Commit 168d09b

Browse files
committed
[Macros] Set -external-plugin-path when the toolchain is not Xcode
1 parent 12b941c commit 168d09b

File tree

4 files changed

+118
-2
lines changed

4 files changed

+118
-2
lines changed

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

+4
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,10 @@ extension Driver {
275275
commandLine.appendPath(localPluginPath)
276276
}
277277

278+
if isFrontendArgSupported(.externalPluginPath) {
279+
try commandLine.appendAll(.externalPluginPath, from: &parsedOptions)
280+
}
281+
278282
if isFrontendArgSupported(.blockListFile) {
279283
try Driver.findBlocklists(RelativeTo: try toolchain.executableDir).forEach {
280284
commandLine.appendFlag(.blockListFile)

Sources/SwiftDriver/Toolchains/DarwinToolchain.swift

+61
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,32 @@ public final class DarwinToolchain: Toolchain {
373373
frontendTargetInfo: FrontendTargetInfo,
374374
driver: inout Driver
375375
) throws {
376+
// Pass -external-plugin-path if the current toolchain is not a Xcode
377+
// default toolchain.
378+
if
379+
driver.isFrontendArgSupported(.externalPluginPath),
380+
let xcodeDir = try self.findCurrentSelectedXcodeDir(),
381+
try !self.executableDir.isDescendant(of: xcodeDir),
382+
let xcodeExecutableDir = try self.findXcodeExecutableDir()
383+
{
384+
let xcodePluginServerPath = xcodeExecutableDir
385+
.appending(component: "swift-plugin-server")
386+
387+
if fileSystem.isExecutableFile(xcodePluginServerPath) {
388+
let xcodeToolchainUsrPath = xcodeExecutableDir.parentDirectory
389+
390+
let xcodePluginPath = xcodeToolchainUsrPath
391+
.appending(components: "lib", "swift", "host", "plugins")
392+
commandLine.appendFlag(.externalPluginPath)
393+
commandLine.appendFlag(xcodePluginPath.pathString + "#" + xcodePluginServerPath.pathString)
394+
395+
let xcodeLocalPluginPath = xcodeToolchainUsrPath
396+
.appending(components: "local", "lib", "swift", "host", "plugins")
397+
commandLine.appendFlag(.externalPluginPath)
398+
commandLine.appendFlag(xcodeLocalPluginPath.pathString + "#" + xcodePluginServerPath.pathString)
399+
}
400+
}
401+
376402
guard let sdkPath = frontendTargetInfo.sdkPath?.path,
377403
let sdkInfo = getTargetSDKInfo(sdkPath: sdkPath) else { return }
378404

@@ -441,3 +467,38 @@ private extension Version {
441467
return self.description
442468
}
443469
}
470+
471+
extension DarwinToolchain {
472+
func findXcodeExecutableDir() throws -> AbsolutePath? {
473+
#if os(macOS)
474+
let result = try executor.checkNonZeroExit(
475+
args: "xcrun", "-toolchain", "default", "-f", "swiftc",
476+
environment: env
477+
).trimmingCharacters(in: .whitespacesAndNewlines)
478+
479+
guard !result.isEmpty else {
480+
return nil
481+
}
482+
return try AbsolutePath(validating: result)
483+
.parentDirectory // swiftc
484+
#else
485+
return nil
486+
#endif
487+
}
488+
489+
func findCurrentSelectedXcodeDir() throws -> AbsolutePath? {
490+
#if os(macOS)
491+
let result = try executor.checkNonZeroExit(
492+
args: "xcode-select", "-p",
493+
environment: env
494+
).trimmingCharacters(in: .whitespacesAndNewlines)
495+
496+
guard !result.isEmpty else {
497+
return nil
498+
}
499+
return try AbsolutePath(validating: result)
500+
#else
501+
return nil
502+
#endif
503+
}
504+
}

Tests/SwiftDriverTests/SwiftDriverTests.swift

+49
Original file line numberDiff line numberDiff line change
@@ -6739,6 +6739,55 @@ final class SwiftDriverTests: XCTestCase {
67396739
XCTAssertTrue(job.commandLine.contains(.path(.absolute(try driver.toolchain.executableDir.parentDirectory.appending(components: "local", "lib", "swift", "host", "plugins")))))
67406740
}
67416741

6742+
func testExternalPluginPaths() throws {
6743+
#if !os(macOS)
6744+
throw XCTSkip("Supported only in macOS")
6745+
#endif
6746+
6747+
try withTemporaryDirectory { tmpDir in
6748+
var driver = try Driver(args: ["swiftc", "-typecheck", "foo.swift"],
6749+
compilerExecutableDir: tmpDir)
6750+
guard driver.isFrontendArgSupported(.externalPluginPath) else {
6751+
return
6752+
}
6753+
6754+
let jobs = try driver.planBuild().removingAutolinkExtractJobs()
6755+
XCTAssertEqual(jobs.count, 1)
6756+
let job = jobs.first!
6757+
6758+
// This happens only Xcode toolchain has 'swift-plugin-server' which we
6759+
// don't know.
6760+
let idx1 = job.commandLine.firstIndex(of: .flag("-external-plugin-path"))
6761+
try XCTSkipIf(idx1 == nil)
6762+
switch job.commandLine[job.commandLine.index(after: idx1!)] {
6763+
case .flag(let value):
6764+
let components = value.split(separator: "#")
6765+
if components.count == 2 {
6766+
XCTAssertTrue(components[0].hasSuffix("/usr/lib/swift/host/plugins"))
6767+
XCTAssertTrue(components[1].hasSuffix("/usr/bin/swift-plugin-server"))
6768+
} else {
6769+
XCTFail("# separated count must 2")
6770+
}
6771+
default:
6772+
XCTFail("invalid arg type after '-external-plugin-path'")
6773+
}
6774+
6775+
let idx2 = job.commandLine[job.commandLine.index(after: idx1!)...].firstIndex(of: .flag("-external-plugin-path"))
6776+
switch job.commandLine[job.commandLine.index(after: try XCTUnwrap(idx2))] {
6777+
case .flag(let value):
6778+
let components = value.split(separator: "#")
6779+
if (components.count == 2) {
6780+
XCTAssertTrue(components[0].hasSuffix("/usr/local/lib/swift/host/plugins"))
6781+
XCTAssertTrue(components[1].hasSuffix("/usr/bin/swift-plugin-server"))
6782+
} else {
6783+
XCTFail("# separated count must 2")
6784+
}
6785+
default:
6786+
XCTFail("invalid arg type after '-external-plugin-path'")
6787+
}
6788+
}
6789+
}
6790+
67426791
func testClangModuleValidateOnce() throws {
67436792
let flagTest = try Driver(args: ["swiftc", "-typecheck", "foo.swift"])
67446793
guard flagTest.isFrontendArgSupported(.clangBuildSessionFile),

Tests/TestUtilities/DriverExtensions.swift

+4-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ extension Driver {
2323
env: [String: String] = ProcessEnv.vars,
2424
diagnosticsEngine: DiagnosticsEngine = DiagnosticsEngine(handlers: [Driver.stderrDiagnosticsHandler]),
2525
fileSystem: FileSystem = localFileSystem,
26-
integratedDriver: Bool = true
26+
integratedDriver: Bool = true,
27+
compilerExecutableDir: AbsolutePath? = nil
2728
) throws {
2829
let executor = try SwiftDriverExecutor(diagnosticsEngine: diagnosticsEngine,
2930
processSet: ProcessSet(),
@@ -34,7 +35,8 @@ extension Driver {
3435
diagnosticsOutput: .engine(diagnosticsEngine),
3536
fileSystem: fileSystem,
3637
executor: executor,
37-
integratedDriver: integratedDriver)
38+
integratedDriver: integratedDriver,
39+
compilerExecutableDir: compilerExecutableDir)
3840
}
3941

4042
/// For tests that need to set the sdk path.

0 commit comments

Comments
 (0)