Skip to content

Commit 6950706

Browse files
authored
Merge pull request #1340 from rintaro/5.9-macros-external-plugin-path
[5.9][Macros] Set -external-plugin-path when the toolchain is not Xcode
2 parents b4cd776 + 9f5ad9b commit 6950706

File tree

5 files changed

+132
-4
lines changed

5 files changed

+132
-4
lines changed

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,10 @@ extension Driver {
269269
commandLine.appendPath(localPluginPath)
270270
}
271271

272+
if isFrontendArgSupported(.externalPluginPath) {
273+
try commandLine.appendAll(.externalPluginPath, from: &parsedOptions)
274+
}
275+
272276
// Pass down -user-module-version if we are working with a compiler that
273277
// supports it.
274278
if let ver = parsedOptions.getLastArgument(.userModuleVersion)?.asSingle,

Sources/SwiftDriver/Toolchains/DarwinToolchain.swift

Lines changed: 61 additions & 0 deletions
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+
}

Sources/SwiftOptions/Options.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ extension Option {
5454
public static let checkOnoneCompleteness: Option = Option("-check-onone-completeness", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Print errors if the compile OnoneSupport module is missing symbols")
5555
public static let clangBuildSessionFile: Option = Option("-clang-build-session-file", .separate, attributes: [.frontend, .argumentIsPath], helpText: "Use the last modification time of <file> as the underlying Clang build session timestamp")
5656
public static let clangHeaderExposeDecls: Option = Option("-clang-header-expose-decls=", .joined, attributes: [.helpHidden, .frontend, .noDriver], metaVar: "all-public|has-expose-attr", helpText: "Which declarations should be exposed in the generated clang header.")
57+
public static let clangHeaderExposeModule: Option = Option("-clang-header-expose-module", .separate, attributes: [.helpHidden, .frontend, .noDriver], metaVar: "<imported-module-name>=<generated-header-name>", helpText: "Allow the compiler to assume that APIs from the specified module are exposed to C/C++/Objective-C in another generated header, so that APIs in the current module that depend on declarations from the specified module can be exposed in the generated header.")
5758
public static let clangTarget: Option = Option("-clang-target", .separate, attributes: [.frontend], helpText: "Separately set the target we should use for internal Clang instance")
5859
public static let codeCompleteCallPatternHeuristics: Option = Option("-code-complete-call-pattern-heuristics", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Use heuristics to guess whether we want call pattern completions")
5960
public static let codeCompleteInitsInPostfixExpr: Option = Option("-code-complete-inits-in-postfix-expr", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Include initializers when completing a postfix expression")
@@ -156,6 +157,8 @@ extension Option {
156157
public static let disableInferPublicConcurrentValue: Option = Option("-disable-infer-public-sendable", .flag, attributes: [.frontend, .noDriver], helpText: "Disable inference of Sendable conformances for public structs and enums")
157158
public static let disableInterfaceLockfile: Option = Option("-disable-interface-lock", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Don't lock interface file when building module")
158159
public static let disableInvalidEphemeralnessAsError: Option = Option("-disable-invalid-ephemeralness-as-error", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Diagnose invalid ephemeral to non-ephemeral conversions as warnings")
160+
public static let disableLayoutStringValueWitnessesInstantiation: Option = Option("-disable-layout-string-value-witnesses-instantiation", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Disable runtime instantiation of layout string value witnesses for generic types")
161+
public static let disableLayoutStringValueWitnesses: Option = Option("-disable-layout-string-value-witnesses", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Disable layout string based value witnesses")
159162
public static let disableLegacyTypeInfo: Option = Option("-disable-legacy-type-info", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Completely disable legacy type layout")
160163
public static let disableLlvmOptzns: Option = Option("-disable-llvm-optzns", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Don't run LLVM optimization passes")
161164
public static let disableLlvmValueNames: Option = Option("-disable-llvm-value-names", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Don't add names to local values in LLVM IR")
@@ -357,11 +360,12 @@ extension Option {
357360
public static let enableExplicitExistentialTypes: Option = Option("-enable-explicit-existential-types", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Enable experimental support for explicit existential types")
358361
public static let enableImplicitBacktracingModuleImport: Option = Option("-enable-implicit-backtracing-module-import", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Enable the implicit import of the _Backtracing module.")
359362
public static let enableImplicitDynamic: Option = Option("-enable-implicit-dynamic", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Add 'dynamic' to all declarations")
360-
public static let enableImportObjcForwardDeclarations: Option = Option("-enable-import-objc-forward-declarations", .flag, attributes: [.helpHidden, .frontend, .noDriver, .moduleInterface], helpText: "Attempt to import Objective-C forward declarations")
361363
public static let enableImportPtrauthFieldFunctionPointers: Option = Option("-enable-import-ptrauth-field-function-pointers", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Enable import of custom ptrauth qualified field function pointers")
362364
public static let enableIncrementalImports: Option = Option("-enable-incremental-imports", .flag, attributes: [.frontend], helpText: "Enable cross-module incremental build metadata and driver scheduling for Swift modules")
363365
public static let enableInferPublicConcurrentValue: Option = Option("-enable-infer-public-sendable", .flag, attributes: [.frontend, .noDriver], helpText: "Enable inference of Sendable conformances for public structs and enums")
364366
public static let enableInvalidEphemeralnessAsError: Option = Option("-enable-invalid-ephemeralness-as-error", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Diagnose invalid ephemeral to non-ephemeral conversions as errors")
367+
public static let enableLayoutStringValueWitnessesInstantiation: Option = Option("-enable-layout-string-value-witnesses-instantiation", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Enable runtime instantiation of layout string value witnesses for generic types")
368+
public static let enableLayoutStringValueWitnesses: Option = Option("-enable-layout-string-value-witnesses", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Enable layout string based value witnesses")
365369
public static let enableLexicalBorrowScopes: Option = Option("-enable-lexical-borrow-scopes=", .joined, attributes: [.helpHidden, .frontend, .noDriver, .moduleInterface], metaVar: "true|false", helpText: "Whether to emit lexical borrow scopes (default: true)")
366370
public static let enableLexicalLifetimes: Option = Option("-enable-lexical-lifetimes=", .joined, attributes: [.helpHidden, .frontend, .noDriver], metaVar: "true|false", helpText: "Whether to enable lexical lifetimes")
367371
public static let enableLexicalLifetimesNoArg: Option = Option("-enable-lexical-lifetimes", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Enable lexical lifetimes")
@@ -423,6 +427,7 @@ extension Option {
423427
public static let explicitSwiftModuleMap: Option = Option("-explicit-swift-module-map-file", .separate, attributes: [.frontend, .noDriver], metaVar: "<path>", helpText: "Specify a JSON file containing information of explicit Swift modules")
424428
public static let exportAs: Option = Option("-export-as", .separate, attributes: [.frontend], helpText: "Module name to use when referenced in clients module interfaces")
425429
public static let externalPassPipelineFilename: Option = Option("-external-pass-pipeline-filename", .separate, attributes: [.helpHidden, .frontend, .noDriver], metaVar: "<pass_pipeline_file>", helpText: "Use the pass pipeline defined by <pass_pipeline_file>")
430+
public static let externalPluginPath: Option = Option("-external-plugin-path", .separate, attributes: [.frontend, .argumentIsPath], metaVar: "<path>#<plugin-server-path>", helpText: "Add directory to the plugin search path with a plugin server executable")
426431
public static let e: Option = Option("-e", .separate, attributes: [], helpText: "Executes a line of code provided on the command line")
427432
public static let FEQ: Option = Option("-F=", .joined, alias: Option.F, attributes: [.frontend, .argumentIsPath])
428433
public static let fileCompilationDir: Option = Option("-file-compilation-dir", .separate, attributes: [.frontend], metaVar: "<path>", helpText: "The compilation directory to embed in the debug info. Coverage mapping is not supported yet.")
@@ -711,6 +716,7 @@ extension Option {
711716
public static let typecheckModuleFromInterface: Option = Option("-typecheck-module-from-interface", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Treat the (single) input as a swiftinterface and typecheck it", group: .modes)
712717
public static let typecheck: Option = Option("-typecheck", .flag, attributes: [.frontend, .noInteractive, .doesNotAffectIncrementalBuild], helpText: "Parse and type-check input file(s)", group: .modes)
713718
public static let typoCorrectionLimit: Option = Option("-typo-correction-limit", .separate, attributes: [.helpHidden, .frontend], metaVar: "<n>", helpText: "Limit the number of times the compiler will attempt typo correction to <n>")
719+
public static let unavailableDeclOptimizationEQ: Option = Option("-unavailable-decl-optimization=", .joined, attributes: [.frontend, .noInteractive], metaVar: "<complete,none>", helpText: "Specify the optimization mode for unavailable declarations. The value may be 'none' (no optimization) or 'complete' (code is not generated at all unavailable declarations)")
714720
public static let updateCode: Option = Option("-update-code", .flag, attributes: [.helpHidden, .frontend, .noInteractive, .doesNotAffectIncrementalBuild], helpText: "Update Swift code")
715721
public static let useClangFunctionTypes: Option = Option("-use-clang-function-types", .flag, attributes: [.frontend, .noDriver], helpText: "Use stored Clang function types for computing canonical types.")
716722
public static let useFrontendParseableOutput: Option = Option("-use-frontend-parseable-output", .flag, attributes: [.helpHidden], helpText: "Emit parseable-output from swift-frontend jobs instead of from the driver")
@@ -816,6 +822,7 @@ extension Option {
816822
Option.checkOnoneCompleteness,
817823
Option.clangBuildSessionFile,
818824
Option.clangHeaderExposeDecls,
825+
Option.clangHeaderExposeModule,
819826
Option.clangTarget,
820827
Option.codeCompleteCallPatternHeuristics,
821828
Option.codeCompleteInitsInPostfixExpr,
@@ -918,6 +925,8 @@ extension Option {
918925
Option.disableInferPublicConcurrentValue,
919926
Option.disableInterfaceLockfile,
920927
Option.disableInvalidEphemeralnessAsError,
928+
Option.disableLayoutStringValueWitnessesInstantiation,
929+
Option.disableLayoutStringValueWitnesses,
921930
Option.disableLegacyTypeInfo,
922931
Option.disableLlvmOptzns,
923932
Option.disableLlvmValueNames,
@@ -1119,11 +1128,12 @@ extension Option {
11191128
Option.enableExplicitExistentialTypes,
11201129
Option.enableImplicitBacktracingModuleImport,
11211130
Option.enableImplicitDynamic,
1122-
Option.enableImportObjcForwardDeclarations,
11231131
Option.enableImportPtrauthFieldFunctionPointers,
11241132
Option.enableIncrementalImports,
11251133
Option.enableInferPublicConcurrentValue,
11261134
Option.enableInvalidEphemeralnessAsError,
1135+
Option.enableLayoutStringValueWitnessesInstantiation,
1136+
Option.enableLayoutStringValueWitnesses,
11271137
Option.enableLexicalBorrowScopes,
11281138
Option.enableLexicalLifetimes,
11291139
Option.enableLexicalLifetimesNoArg,
@@ -1185,6 +1195,7 @@ extension Option {
11851195
Option.explicitSwiftModuleMap,
11861196
Option.exportAs,
11871197
Option.externalPassPipelineFilename,
1198+
Option.externalPluginPath,
11881199
Option.e,
11891200
Option.FEQ,
11901201
Option.fileCompilationDir,
@@ -1473,6 +1484,7 @@ extension Option {
14731484
Option.typecheckModuleFromInterface,
14741485
Option.typecheck,
14751486
Option.typoCorrectionLimit,
1487+
Option.unavailableDeclOptimizationEQ,
14761488
Option.updateCode,
14771489
Option.useClangFunctionTypes,
14781490
Option.useFrontendParseableOutput,

Tests/SwiftDriverTests/SwiftDriverTests.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6727,6 +6727,55 @@ final class SwiftDriverTests: XCTestCase {
67276727
XCTAssertTrue(job.commandLine.contains(.path(.absolute(try driver.toolchain.executableDir.parentDirectory.appending(components: "local", "lib", "swift", "host", "plugins")))))
67286728
}
67296729

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

Tests/TestUtilities/DriverExtensions.swift

Lines changed: 4 additions & 2 deletions
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
diagnosticsEngine: 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)