Skip to content

Commit

Permalink
[Macros] Set -external-plugin-path when the toolchain is not Xcode
Browse files Browse the repository at this point in the history
  • Loading branch information
rintaro committed Apr 25, 2023
1 parent 12b941c commit 168d09b
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 2 deletions.
4 changes: 4 additions & 0 deletions Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift
Expand Up @@ -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)
Expand Down
61 changes: 61 additions & 0 deletions Sources/SwiftDriver/Toolchains/DarwinToolchain.swift
Expand Up @@ -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 }

Expand Down Expand Up @@ -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
}
}
49 changes: 49 additions & 0 deletions Tests/SwiftDriverTests/SwiftDriverTests.swift
Expand Up @@ -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),
Expand Down
6 changes: 4 additions & 2 deletions Tests/TestUtilities/DriverExtensions.swift
Expand Up @@ -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(),
Expand All @@ -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.
Expand Down

0 comments on commit 168d09b

Please sign in to comment.