From 05c59ab514b28a32759c1b45f262fb0f4677e53b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 15 Jun 2023 00:39:52 -0700 Subject: [PATCH 1/2] Add SDK and platform paths to plugin executables --- .../SwiftDriver/Jobs/FrontendJobHelpers.swift | 62 ++++++++++++++++--- Tests/SwiftDriverTests/SwiftDriverTests.swift | 6 +- 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift index aae05f9b4..71dd5c0e8 100644 --- a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift +++ b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift @@ -27,6 +27,26 @@ fileprivate func shouldColorDiagnostics() -> Bool { return TerminalController.isTTY(stderrStream) } +extension VirtualPath { + // Given a virtual path pointing into a toolchain/SDK/platform, produce the + // path to `swift-plugin-server`. + fileprivate var pluginServerPath: VirtualPath { + self.appending(components: "usr", "swift-plugin-server") + } + + // Given a virtual path pointing into a toolchain/SDK/platform, produce the + // path to the plugins. + fileprivate var pluginPath: VirtualPath { + self.appending(components: "lib", "swift", "host", "plugins") + } + + // Given a virtual path pointing into a toolchain/SDK/platform, produce the + // path to the plugins. + fileprivate var localPluginPath: VirtualPath { + self.appending(component: "local").pluginPath + } +} + extension Driver { /// How the bridging header should be handled. enum BridgingHeaderHandling { @@ -261,18 +281,46 @@ extension Driver { try commandLine.appendLast(.emitMacroExpansionFiles, from: &parsedOptions) } - if isFrontendArgSupported(.pluginPath) { + // Emit user-provided plugin paths, in order. + if isFrontendArgSupported(.externalPluginPath) { + try commandLine.appendAll(.pluginPath, .externalPluginPath, from: &parsedOptions) + } else if isFrontendArgSupported(.pluginPath) { try commandLine.appendAll(.pluginPath, from: &parsedOptions) + } + + if isFrontendArgSupported(.externalPluginPath), let sdkPath = frontendTargetInfo.sdkPath?.path { + // Default paths for compiler plugins found within an SDK (accessed via + // that SDK's plugin server). + let sdkPathRoot = VirtualPath.lookup(sdkPath).appending(components: "usr") + commandLine.appendFlag(.externalPluginPath) + commandLine.appendFlag("\(sdkPathRoot.pluginServerPath.name.spm_shellEscaped())#\(sdkPathRoot.pluginPath.name)") + + commandLine.appendFlag(.externalPluginPath) + commandLine.appendFlag("\(sdkPathRoot.pluginServerPath.name.spm_shellEscaped())#\(sdkPathRoot.localPluginPath.name)") - let defaultPluginPath = try toolchain.executableDir.parentDirectory - .appending(components: "lib", "swift", "host", "plugins") + // Default paths for compiler plugins within the platform (accessed via that + // platform's plugin server). + let platformPathRoot = VirtualPath.lookup(sdkPath) + .parentDirectory + .parentDirectory + .parentDirectory + .appending(components: "Developer", "usr") + commandLine.appendFlag(.externalPluginPath) + commandLine.appendFlag("\(platformPathRoot.pluginServerPath.name.spm_shellEscaped())#\(platformPathRoot.pluginPath.name)") + + commandLine.appendFlag(.externalPluginPath) + commandLine.appendFlag("\(platformPathRoot.pluginServerPath.name.spm_shellEscaped())#\(platformPathRoot.localPluginPath.name)") + } + + if isFrontendArgSupported(.pluginPath) { + // Default paths for compiler plugins found within the toolchain + // (loaded as shared libraries). + let pluginPathRoot = VirtualPath.absolute(try toolchain.executableDir.parentDirectory) commandLine.appendFlag(.pluginPath) - commandLine.appendPath(defaultPluginPath) + commandLine.appendPath(pluginPathRoot.pluginPath) - let localPluginPath = try toolchain.executableDir.parentDirectory - .appending(components: "local", "lib", "swift", "host", "plugins") commandLine.appendFlag(.pluginPath) - commandLine.appendPath(localPluginPath) + commandLine.appendPath(pluginPathRoot.localPluginPath) } if isFrontendArgSupported(.externalPluginPath) { diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index a1b9abfff..1525d63f7 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -6798,14 +6798,16 @@ final class SwiftDriverTests: XCTestCase { } func testPluginPaths() throws { - var driver = try Driver(args: ["swiftc", "-typecheck", "foo.swift"]) - guard driver.isFrontendArgSupported(.pluginPath) else { + let sdkRoot = testInputsPath.appending(component: "SDKChecks").appending(component: "iPhoneOS.sdk") + var driver = try Driver(args: ["swiftc", "-typecheck", "foo.swift", "-sdk", VirtualPath.absolute(sdkRoot).name]) + guard driver.isFrontendArgSupported(.pluginPath) && driver.isFrontendArgSupported(.externalPluginPath) else { return } let jobs = try driver.planBuild().removingAutolinkExtractJobs() XCTAssertEqual(jobs.count, 1) let job = jobs.first! + XCTAssertTrue(job.commandLine.contains(.flag("-external-plugin-path"))) XCTAssertTrue(job.commandLine.contains(.flag("-plugin-path"))) XCTAssertTrue(job.commandLine.contains(.path(.absolute(try driver.toolchain.executableDir.parentDirectory.appending(components: "lib", "swift", "host", "plugins"))))) XCTAssertTrue(job.commandLine.contains(.path(.absolute(try driver.toolchain.executableDir.parentDirectory.appending(components: "local", "lib", "swift", "host", "plugins"))))) From b67a70c87b908687829a66294c2990d269a9b334 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 15 Jun 2023 13:42:26 -0700 Subject: [PATCH 2/2] Improve/fix SDK and platform external plugin path arguments Improve on the prior implementation of adding SDK and platform external plugin path arguments in a few ways: * Only do this for Darwin, because other platforms don't have similar abstractions at the moment * Fix incorrect paths, because I had not written tests * Add tests, because otherwise nobody should trust this code * Make sure all of the command-line arguments for compiler plugins are passed through to the frontend in the order they were provided * Pass `-load-plugin-library` and `-load-plugin-executable` through to the frontend --- .../SwiftDriver/Jobs/FrontendJobHelpers.swift | 75 ++++--------------- .../Toolchains/DarwinToolchain.swift | 44 +++++++++++ Tests/SwiftDriverTests/SwiftDriverTests.swift | 50 ++++++++++++- 3 files changed, 107 insertions(+), 62 deletions(-) diff --git a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift index 71dd5c0e8..fe638b2dd 100644 --- a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift +++ b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift @@ -27,26 +27,6 @@ fileprivate func shouldColorDiagnostics() -> Bool { return TerminalController.isTTY(stderrStream) } -extension VirtualPath { - // Given a virtual path pointing into a toolchain/SDK/platform, produce the - // path to `swift-plugin-server`. - fileprivate var pluginServerPath: VirtualPath { - self.appending(components: "usr", "swift-plugin-server") - } - - // Given a virtual path pointing into a toolchain/SDK/platform, produce the - // path to the plugins. - fileprivate var pluginPath: VirtualPath { - self.appending(components: "lib", "swift", "host", "plugins") - } - - // Given a virtual path pointing into a toolchain/SDK/platform, produce the - // path to the plugins. - fileprivate var localPluginPath: VirtualPath { - self.appending(component: "local").pluginPath - } -} - extension Driver { /// How the bridging header should be handled. enum BridgingHeaderHandling { @@ -283,48 +263,9 @@ extension Driver { // Emit user-provided plugin paths, in order. if isFrontendArgSupported(.externalPluginPath) { - try commandLine.appendAll(.pluginPath, .externalPluginPath, from: &parsedOptions) + try commandLine.appendAll(.pluginPath, .externalPluginPath, .loadPluginLibrary, .loadPluginExecutable, from: &parsedOptions) } else if isFrontendArgSupported(.pluginPath) { - try commandLine.appendAll(.pluginPath, from: &parsedOptions) - } - - if isFrontendArgSupported(.externalPluginPath), let sdkPath = frontendTargetInfo.sdkPath?.path { - // Default paths for compiler plugins found within an SDK (accessed via - // that SDK's plugin server). - let sdkPathRoot = VirtualPath.lookup(sdkPath).appending(components: "usr") - commandLine.appendFlag(.externalPluginPath) - commandLine.appendFlag("\(sdkPathRoot.pluginServerPath.name.spm_shellEscaped())#\(sdkPathRoot.pluginPath.name)") - - commandLine.appendFlag(.externalPluginPath) - commandLine.appendFlag("\(sdkPathRoot.pluginServerPath.name.spm_shellEscaped())#\(sdkPathRoot.localPluginPath.name)") - - // Default paths for compiler plugins within the platform (accessed via that - // platform's plugin server). - let platformPathRoot = VirtualPath.lookup(sdkPath) - .parentDirectory - .parentDirectory - .parentDirectory - .appending(components: "Developer", "usr") - commandLine.appendFlag(.externalPluginPath) - commandLine.appendFlag("\(platformPathRoot.pluginServerPath.name.spm_shellEscaped())#\(platformPathRoot.pluginPath.name)") - - commandLine.appendFlag(.externalPluginPath) - commandLine.appendFlag("\(platformPathRoot.pluginServerPath.name.spm_shellEscaped())#\(platformPathRoot.localPluginPath.name)") - } - - if isFrontendArgSupported(.pluginPath) { - // Default paths for compiler plugins found within the toolchain - // (loaded as shared libraries). - let pluginPathRoot = VirtualPath.absolute(try toolchain.executableDir.parentDirectory) - commandLine.appendFlag(.pluginPath) - commandLine.appendPath(pluginPathRoot.pluginPath) - - commandLine.appendFlag(.pluginPath) - commandLine.appendPath(pluginPathRoot.localPluginPath) - } - - if isFrontendArgSupported(.externalPluginPath) { - try commandLine.appendAll(.externalPluginPath, from: &parsedOptions) + try commandLine.appendAll(.pluginPath, .loadPluginLibrary, from: &parsedOptions) } if isFrontendArgSupported(.blockListFile) { @@ -442,6 +383,18 @@ extension Driver { inputs: &inputs, frontendTargetInfo: frontendTargetInfo, driver: &self) + + // Platform-agnostic options that need to happen after platform-specific ones. + if isFrontendArgSupported(.pluginPath) { + // Default paths for compiler plugins found within the toolchain + // (loaded as shared libraries). + let pluginPathRoot = VirtualPath.absolute(try toolchain.executableDir.parentDirectory) + commandLine.appendFlag(.pluginPath) + commandLine.appendPath(pluginPathRoot.pluginPath) + + commandLine.appendFlag(.pluginPath) + commandLine.appendPath(pluginPathRoot.localPluginPath) + } } mutating func addFrontendSupplementaryOutputArguments(commandLine: inout [Job.ArgTemplate], diff --git a/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift b/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift index 8b8bce051..7fc3b01a5 100644 --- a/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift +++ b/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift @@ -421,6 +421,30 @@ public final class DarwinToolchain: Toolchain { commandLine.appendFlag(.clangTarget) commandLine.appendFlag(clangTargetTriple) } + + if driver.isFrontendArgSupported(.externalPluginPath) { + // Default paths for compiler plugins found within an SDK (accessed via + // that SDK's plugin server). + let sdkPathRoot = VirtualPath.lookup(sdkPath).appending(components: "usr") + commandLine.appendFlag(.externalPluginPath) + commandLine.appendFlag("\(sdkPathRoot.pluginPath.name)#\(sdkPathRoot.pluginServerPath.name.spm_shellEscaped())") + + commandLine.appendFlag(.externalPluginPath) + commandLine.appendFlag("\(sdkPathRoot.localPluginPath.name)#\(sdkPathRoot.pluginServerPath.name.spm_shellEscaped())") + + // Default paths for compiler plugins within the platform (accessed via that + // platform's plugin server). + let platformPathRoot = VirtualPath.lookup(sdkPath) + .parentDirectory + .parentDirectory + .parentDirectory + .appending(components: "Developer", "usr") + commandLine.appendFlag(.externalPluginPath) + commandLine.appendFlag("\(platformPathRoot.pluginPath.name)#\(platformPathRoot.pluginServerPath.name.spm_shellEscaped())") + + commandLine.appendFlag(.externalPluginPath) + commandLine.appendFlag("\(platformPathRoot.localPluginPath.name)#\(platformPathRoot.pluginServerPath.name.spm_shellEscaped())") + } } } @@ -441,3 +465,23 @@ private extension Version { return self.description } } + +extension VirtualPath { + // Given a virtual path pointing into a toolchain/SDK/platform, produce the + // path to `swift-plugin-server`. + fileprivate var pluginServerPath: VirtualPath { + self.appending(components: "bin", "swift-plugin-server") + } + + // Given a virtual path pointing into a toolchain/SDK/platform, produce the + // path to the plugins. + var pluginPath: VirtualPath { + self.appending(components: "lib", "swift", "host", "plugins") + } + + // Given a virtual path pointing into a toolchain/SDK/platform, produce the + // path to the plugins. + var localPluginPath: VirtualPath { + self.appending(component: "local").pluginPath + } +} diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index 1525d63f7..f0d26d3e4 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -6799,7 +6799,7 @@ final class SwiftDriverTests: XCTestCase { func testPluginPaths() throws { let sdkRoot = testInputsPath.appending(component: "SDKChecks").appending(component: "iPhoneOS.sdk") - var driver = try Driver(args: ["swiftc", "-typecheck", "foo.swift", "-sdk", VirtualPath.absolute(sdkRoot).name]) + var driver = try Driver(args: ["swiftc", "-typecheck", "foo.swift", "-sdk", VirtualPath.absolute(sdkRoot).name, "-plugin-path", "PluginA", "-external-plugin-path", "PluginB#Bexe", "-load-plugin-library", "PluginB2", "-plugin-path", "PluginC"]) guard driver.isFrontendArgSupported(.pluginPath) && driver.isFrontendArgSupported(.externalPluginPath) else { return } @@ -6807,7 +6807,55 @@ final class SwiftDriverTests: XCTestCase { let jobs = try driver.planBuild().removingAutolinkExtractJobs() XCTAssertEqual(jobs.count, 1) let job = jobs.first! + + // Check that the we have the plugin paths we expect, in the order we expect. + let pluginAIndex = job.commandLine.firstIndex(of: .path(VirtualPath.relative(.init("PluginA")))) + XCTAssertNotNil(pluginAIndex) + + let pluginBIndex = job.commandLine.firstIndex(of: .path(VirtualPath.relative(.init("PluginB#Bexe")))) + XCTAssertNotNil(pluginBIndex) + XCTAssertLessThan(pluginAIndex!, pluginBIndex!) + + let pluginB2Index = job.commandLine.firstIndex(of: .path(VirtualPath.relative(.init("PluginB2")))) + XCTAssertNotNil(pluginB2Index) + XCTAssertLessThan(pluginBIndex!, pluginB2Index!) + + let pluginCIndex = job.commandLine.firstIndex(of: .path(VirtualPath.relative(.init("PluginC")))) + XCTAssertNotNil(pluginCIndex) + XCTAssertLessThan(pluginB2Index!, pluginCIndex!) + + #if os(macOS) XCTAssertTrue(job.commandLine.contains(.flag("-external-plugin-path"))) + let sdkServerPath = sdkRoot.appending(components: "usr", "bin", "swift-plugin-server").pathString + let sdkPluginPath = sdkRoot.appending(components: "usr", "lib", "swift", "host", "plugins").pathString + + let sdkPluginPathIndex = job.commandLine.firstIndex(of: .flag("\(sdkPluginPath)#\(sdkServerPath)")) + XCTAssertNotNil(sdkPluginPathIndex) + XCTAssertLessThan(pluginCIndex!, sdkPluginPathIndex!) + + let sdkLocalPluginPath = sdkRoot.appending(components: "usr", "local", "lib", "swift", "host", "plugins").pathString + let sdkLocalPluginPathIndex = job.commandLine.firstIndex(of: .flag("\(sdkLocalPluginPath)#\(sdkServerPath)")) + XCTAssertNotNil(sdkLocalPluginPathIndex) + XCTAssertLessThan(sdkPluginPathIndex!, sdkLocalPluginPathIndex!) + + let platformPath = sdkRoot.parentDirectory.parentDirectory.parentDirectory.appending(components: "Developer", "usr") + let platformServerPath = platformPath.appending(components: "bin", "swift-plugin-server").pathString + + let platformPluginPath = platformPath.appending(components: "lib", "swift", "host", "plugins") + let platformPluginPathIndex = job.commandLine.firstIndex(of: .flag("\(platformPluginPath)#\(platformServerPath)")) + XCTAssertNotNil(platformPluginPathIndex) + XCTAssertLessThan(sdkLocalPluginPathIndex!, platformPluginPathIndex!) + + let platformLocalPluginPath = platformPath.appending(components: "local", "lib", "swift", "host", "plugins") + let platformLocalPluginPathIndex = job.commandLine.firstIndex(of: .flag("\(platformLocalPluginPath)#\(platformServerPath)")) + XCTAssertNotNil(platformLocalPluginPathIndex) + XCTAssertLessThan(platformPluginPathIndex!, platformLocalPluginPathIndex!) + + let toolchainPluginPathIndex = job.commandLine.firstIndex(of: .path(.absolute(try driver.toolchain.executableDir.parentDirectory.appending(components: "lib", "swift", "host", "plugins")))) + XCTAssertNotNil(toolchainPluginPathIndex) + XCTAssertLessThan(platformLocalPluginPathIndex!, toolchainPluginPathIndex!) + #endif + XCTAssertTrue(job.commandLine.contains(.flag("-plugin-path"))) XCTAssertTrue(job.commandLine.contains(.path(.absolute(try driver.toolchain.executableDir.parentDirectory.appending(components: "lib", "swift", "host", "plugins"))))) XCTAssertTrue(job.commandLine.contains(.path(.absolute(try driver.toolchain.executableDir.parentDirectory.appending(components: "local", "lib", "swift", "host", "plugins")))))