Skip to content

Commit c5a58e0

Browse files
committed
Allow for non-external lookup of libSwiftScan symbols and centralize the scanning instance use in the driver.
- The driver now holds a reference to the 'swiftScanLibInstance' which is shared with the 'interModuleDependencyOracle', but is also used for target info and supported compiler feature queries. This means a single SwiftScan instance is shared across all uses in a given driver instance. - Allow SwiftScan to be instantiated *without* a path to an external 'libSwiftScan.dylib', which will cause it to 'dlopen' with a NULL argument, and expect the scanner symbols to be found in the image that the driver code is a part of. This is useful for when compiler-based tooling (SourceKit) links the driver directly and uses its C API.
1 parent c0289c4 commit c5a58e0

15 files changed

+412
-333
lines changed

Sources/SwiftDriver/Driver/Driver.swift

+165-44
Large diffs are not rendered by default.

Sources/SwiftDriver/Execution/DriverExecutor.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public struct DriverExecutorWorkload {
8080
}
8181
}
8282

83-
enum JobExecutionError: Error {
83+
@_spi(Testing) public enum JobExecutionError: Error {
8484
case jobFailedWithNonzeroExitCode(Int, String)
8585
case failedToReadJobOutput
8686
// A way to pass more information to the catch point

Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift

+20-9
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,17 @@ public class InterModuleDependencyOracle {
8383
}
8484

8585
/// Given a specified toolchain path, locate and instantiate an instance of the SwiftScan library
86-
public func verifyOrCreateScannerInstance(fileSystem: FileSystem,
87-
swiftScanLibPath: AbsolutePath) throws {
86+
public func verifyOrCreateScannerInstance(swiftScanLibPath: AbsolutePath?) throws {
8887
return try queue.sync {
89-
if swiftScanLibInstance == nil {
88+
guard let scanInstance = swiftScanLibInstance else {
9089
swiftScanLibInstance = try SwiftScan(dylib: swiftScanLibPath)
91-
} else {
92-
guard swiftScanLibInstance!.path == swiftScanLibPath else {
93-
throw DependencyScanningError
94-
.scanningLibraryInvocationMismatch(swiftScanLibInstance!.path, swiftScanLibPath)
95-
}
90+
return
91+
}
92+
93+
guard scanInstance.path?.description == swiftScanLibPath?.description else {
94+
throw DependencyScanningError
95+
.scanningLibraryInvocationMismatch(scanInstance.path?.description ?? "built-in",
96+
swiftScanLibPath?.description ?? "built-in")
9697
}
9798
}
9899
}
@@ -209,10 +210,20 @@ public class InterModuleDependencyOracle {
209210
}
210211
}
211212

213+
// Note: this is `true` even in the `compilerIntegratedTooling` mode
214+
// where the `SwiftScan` instance refers to the own image the driver is
215+
// running in, since there is still technically a `SwiftScan` handle
216+
// capable of handling API requests expected of it.
212217
private var hasScannerInstance: Bool { self.swiftScanLibInstance != nil }
218+
func getScannerInstance() -> SwiftScan? {
219+
self.swiftScanLibInstance
220+
}
221+
func setScannerInstance(_ instance: SwiftScan?) {
222+
self.swiftScanLibInstance = instance
223+
}
213224

214225
/// Queue to sunchronize accesses to the scanner
215-
internal let queue = DispatchQueue(label: "org.swift.swift-driver.swift-scan")
226+
let queue = DispatchQueue(label: "org.swift.swift-driver.swift-scan")
216227

217228
/// A reference to an instance of the compiler's libSwiftScan shared library
218229
private var swiftScanLibInstance: SwiftScan? = nil

Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift

+7-41
Original file line numberDiff line numberDiff line change
@@ -164,41 +164,6 @@ public extension Driver {
164164
contents)
165165
}
166166

167-
/// Returns false if the lib is available and ready to use
168-
private mutating func initSwiftScanLib() throws -> Bool {
169-
// `-nonlib-dependency-scanner` was specified
170-
guard !parsedOptions.hasArgument(.driverScanDependenciesNonLib) else {
171-
return true
172-
}
173-
174-
// If the libSwiftScan library cannot be found,
175-
// attempt to fallback to using `swift-frontend -scan-dependencies` invocations for dependency
176-
// scanning.
177-
guard let scanLibPath = try toolchain.lookupSwiftScanLib(),
178-
fileSystem.exists(scanLibPath) else {
179-
diagnosticEngine.emit(.warn_scan_dylib_not_found())
180-
return true
181-
}
182-
183-
do {
184-
try interModuleDependencyOracle.verifyOrCreateScannerInstance(fileSystem: fileSystem,
185-
swiftScanLibPath: scanLibPath)
186-
if isCachingEnabled {
187-
self.cas = try interModuleDependencyOracle.getOrCreateCAS(pluginPath: try getCASPluginPath(),
188-
onDiskPath: try getOnDiskCASPath(),
189-
pluginOptions: try getCASPluginOptions())
190-
}
191-
} catch {
192-
if isCachingEnabled {
193-
diagnosticEngine.emit(.error_caching_enabled_libswiftscan_load_failure(scanLibPath.description))
194-
} else {
195-
diagnosticEngine.emit(.warn_scan_dylib_load_failed(scanLibPath.description))
196-
}
197-
return true
198-
}
199-
return false
200-
}
201-
202167
static func sanitizeCommandForLibScanInvocation(_ command: inout [String]) {
203168
// Remove the tool executable to only leave the arguments. When passing the
204169
// command line into libSwiftScan, the library is itself the tool and only
@@ -217,8 +182,7 @@ public extension Driver {
217182
let forceResponseFiles = parsedOptions.hasArgument(.driverForceResponseFiles)
218183
let imports: InterModuleDependencyImports
219184

220-
let isSwiftScanLibAvailable = !(try initSwiftScanLib())
221-
if isSwiftScanLibAvailable {
185+
if supportInProcessSwiftScanQueries {
222186
var scanDiagnostics: [ScannerDiagnosticPayload] = []
223187
guard let cwd = workingDirectory else {
224188
throw DependencyScanningError.dependencyScanFailed("cannot determine working directory")
@@ -294,8 +258,7 @@ public extension Driver {
294258
stdoutStream.flush()
295259
}
296260

297-
let isSwiftScanLibAvailable = !(try initSwiftScanLib())
298-
if isSwiftScanLibAvailable {
261+
if supportInProcessSwiftScanQueries {
299262
var scanDiagnostics: [ScannerDiagnosticPayload] = []
300263
guard let cwd = workingDirectory else {
301264
throw DependencyScanningError.dependencyScanFailed("cannot determine working directory")
@@ -333,8 +296,7 @@ public extension Driver {
333296
let forceResponseFiles = parsedOptions.hasArgument(.driverForceResponseFiles)
334297
let moduleVersionedGraphMap: [ModuleDependencyId: [InterModuleDependencyGraph]]
335298

336-
let isSwiftScanLibAvailable = !(try initSwiftScanLib())
337-
if isSwiftScanLibAvailable {
299+
if supportInProcessSwiftScanQueries {
338300
var scanDiagnostics: [ScannerDiagnosticPayload] = []
339301
guard let cwd = workingDirectory else {
340302
throw DependencyScanningError.dependencyScanFailed("cannot determine working directory")
@@ -502,3 +464,7 @@ public extension Driver {
502464
.parentDirectory // toolchain root
503465
}
504466
}
467+
468+
extension Driver {
469+
var supportInProcessSwiftScanQueries: Bool { return self.swiftScanLibInstance != nil }
470+
}

Sources/SwiftDriver/Jobs/EmitSupportedFeaturesJob.swift

+7-20
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,19 @@ extension Toolchain {
5959
extension Driver {
6060

6161
static func computeSupportedCompilerArgs(of toolchain: Toolchain,
62+
libSwiftScan: SwiftScan?,
6263
parsedOptions: inout ParsedOptions,
6364
diagnosticsEngine: DiagnosticsEngine,
6465
fileSystem: FileSystem,
6566
executor: DriverExecutor)
6667
throws -> Set<String> {
67-
do {
68-
if let supportedArgs =
69-
try querySupportedCompilerArgsInProcess(of: toolchain, fileSystem: fileSystem) {
70-
return supportedArgs
68+
if let libSwiftScanInstance = libSwiftScan,
69+
libSwiftScanInstance.canQuerySupportedArguments() {
70+
do {
71+
return try libSwiftScanInstance.querySupportedArguments()
72+
} catch {
73+
diagnosticsEngine.emit(.remark_inprocess_supported_features_query_failed(error.localizedDescription))
7174
}
72-
} catch {
73-
diagnosticsEngine.emit(.remark_inprocess_supported_features_query_failed(error.localizedDescription))
7475
}
7576

7677
// Fallback: Invoke `swift-frontend -emit-supported-features` and decode the output
@@ -88,20 +89,6 @@ extension Driver {
8889
return Set(decodedSupportedFlagList)
8990
}
9091

91-
static func querySupportedCompilerArgsInProcess(of toolchain: Toolchain,
92-
fileSystem: FileSystem)
93-
throws -> Set<String>? {
94-
let optionalSwiftScanLibPath = try toolchain.lookupSwiftScanLib()
95-
if let swiftScanLibPath = optionalSwiftScanLibPath,
96-
fileSystem.exists(swiftScanLibPath) {
97-
let libSwiftScanInstance = try SwiftScan(dylib: swiftScanLibPath)
98-
if libSwiftScanInstance.canQuerySupportedArguments() {
99-
return try libSwiftScanInstance.querySupportedArguments()
100-
}
101-
}
102-
return nil
103-
}
104-
10592
static func computeSupportedCompilerFeatures(of toolchain: Toolchain,
10693
env: [String: String]) throws -> Set<String> {
10794
struct FeatureInfo: Codable {

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

+4-2
Original file line numberDiff line numberDiff line change
@@ -406,11 +406,13 @@ extension Driver {
406406
// CAS related options.
407407
if isCachingEnabled {
408408
commandLine.appendFlag(.cacheCompileJob)
409-
if let casPath = try getOnDiskCASPath() {
409+
if let casPath = try Self.getOnDiskCASPath(parsedOptions: &parsedOptions,
410+
toolchain: toolchain) {
410411
commandLine.appendFlag(.casPath)
411412
commandLine.appendFlag(casPath.pathString)
412413
}
413-
if let pluginPath = try getCASPluginPath() {
414+
if let pluginPath = try Self.getCASPluginPath(parsedOptions: &parsedOptions,
415+
toolchain: toolchain) {
414416
commandLine.appendFlag(.casPluginPath)
415417
commandLine.appendFlag(pluginPath.pathString)
416418
}

Sources/SwiftDriver/Jobs/PrintTargetInfoJob.swift

+42-49
Original file line numberDiff line numberDiff line change
@@ -181,46 +181,39 @@ extension Toolchain {
181181
}
182182

183183
extension Driver {
184-
@_spi(Testing) public static func queryTargetInfoInProcess(of toolchain: Toolchain,
184+
@_spi(Testing) public static func queryTargetInfoInProcess(libSwiftScanInstance: SwiftScan,
185+
toolchain: Toolchain,
185186
fileSystem: FileSystem,
186187
workingDirectory: AbsolutePath?,
187-
invocationCommand: [String]) throws -> FrontendTargetInfo? {
188-
let optionalSwiftScanLibPath = try toolchain.lookupSwiftScanLib()
189-
if let swiftScanLibPath = optionalSwiftScanLibPath,
190-
fileSystem.exists(swiftScanLibPath) {
191-
let libSwiftScanInstance = try SwiftScan(dylib: swiftScanLibPath)
192-
if libSwiftScanInstance.canQueryTargetInfo() {
193-
let cwd = try workingDirectory ?? fileSystem.tempDirectory
194-
let compilerExecutablePath = try toolchain.resolvedTool(.swiftCompiler).path
195-
let targetInfoData =
196-
try libSwiftScanInstance.queryTargetInfoJSON(workingDirectory: cwd,
197-
compilerExecutablePath: compilerExecutablePath,
198-
invocationCommand: invocationCommand)
199-
do {
200-
return try JSONDecoder().decode(FrontendTargetInfo.self, from: targetInfoData)
201-
} catch let decodingError as DecodingError {
202-
let stringToDecode = String(data: targetInfoData, encoding: .utf8)
203-
let errorDesc: String
204-
switch decodingError {
205-
case let .typeMismatch(type, context):
206-
errorDesc = "type mismatch: \(type), path: \(context.codingPath)"
207-
case let .valueNotFound(type, context):
208-
errorDesc = "value missing: \(type), path: \(context.codingPath)"
209-
case let .keyNotFound(key, context):
210-
errorDesc = "key missing: \(key), path: \(context.codingPath)"
211-
case let .dataCorrupted(context):
212-
errorDesc = "data corrupted at path: \(context.codingPath)"
213-
@unknown default:
214-
errorDesc = "unknown decoding error"
215-
}
216-
throw Error.unableToDecodeFrontendTargetInfo(
217-
stringToDecode,
218-
invocationCommand,
219-
errorDesc)
220-
}
188+
invocationCommand: [String]) throws -> FrontendTargetInfo {
189+
let cwd = try workingDirectory ?? fileSystem.tempDirectory
190+
let compilerExecutablePath = try toolchain.resolvedTool(.swiftCompiler).path
191+
let targetInfoData =
192+
try libSwiftScanInstance.queryTargetInfoJSON(workingDirectory: cwd,
193+
compilerExecutablePath: compilerExecutablePath,
194+
invocationCommand: invocationCommand)
195+
do {
196+
return try JSONDecoder().decode(FrontendTargetInfo.self, from: targetInfoData)
197+
} catch let decodingError as DecodingError {
198+
let stringToDecode = String(data: targetInfoData, encoding: .utf8)
199+
let errorDesc: String
200+
switch decodingError {
201+
case let .typeMismatch(type, context):
202+
errorDesc = "type mismatch: \(type), path: \(context.codingPath)"
203+
case let .valueNotFound(type, context):
204+
errorDesc = "value missing: \(type), path: \(context.codingPath)"
205+
case let .keyNotFound(key, context):
206+
errorDesc = "key missing: \(key), path: \(context.codingPath)"
207+
case let .dataCorrupted(context):
208+
errorDesc = "data corrupted at path: \(context.codingPath)"
209+
@unknown default:
210+
errorDesc = "unknown decoding error"
221211
}
212+
throw Error.unableToDecodeFrontendTargetInfo(
213+
stringToDecode,
214+
invocationCommand,
215+
errorDesc)
222216
}
223-
return nil
224217
}
225218

226219
static func computeTargetInfo(target: Triple?,
@@ -231,6 +224,7 @@ extension Driver {
231224
requiresInPlaceExecution: Bool = false,
232225
useStaticResourceDir: Bool = false,
233226
swiftCompilerPrefixArgs: [String],
227+
libSwiftScan: SwiftScan?,
234228
toolchain: Toolchain,
235229
fileSystem: FileSystem,
236230
workingDirectory: AbsolutePath?,
@@ -243,20 +237,19 @@ extension Driver {
243237
requiresInPlaceExecution: requiresInPlaceExecution,
244238
useStaticResourceDir: useStaticResourceDir,
245239
swiftCompilerPrefixArgs: swiftCompilerPrefixArgs)
246-
var command = try Self.itemizedJobCommand(of: frontendTargetInfoJob,
247-
useResponseFiles: .disabled,
248-
using: executor.resolver)
249-
Self.sanitizeCommandForLibScanInvocation(&command)
250-
251-
do {
252-
if let targetInfo =
253-
try Self.queryTargetInfoInProcess(of: toolchain, fileSystem: fileSystem,
254-
workingDirectory: workingDirectory,
255-
invocationCommand: command) {
256-
return targetInfo
240+
if let libSwiftScanInstance = libSwiftScan,
241+
libSwiftScanInstance.canQueryTargetInfo() {
242+
do {
243+
var command = try Self.itemizedJobCommand(of: frontendTargetInfoJob,
244+
useResponseFiles: .disabled,
245+
using: executor.resolver)
246+
Self.sanitizeCommandForLibScanInvocation(&command)
247+
return try Self.queryTargetInfoInProcess(libSwiftScanInstance: libSwiftScanInstance, toolchain: toolchain,
248+
fileSystem: fileSystem, workingDirectory: workingDirectory,
249+
invocationCommand: command)
250+
} catch {
251+
diagnosticsEngine.emit(.remark_inprocess_target_info_query_failed(error.localizedDescription))
257252
}
258-
} catch {
259-
diagnosticsEngine.emit(.remark_inprocess_target_info_query_failed(error.localizedDescription))
260253
}
261254

262255
// Fallback: Invoke `swift-frontend -print-target-info` and decode the output

Sources/SwiftDriver/SwiftScan/Loader.swift

+13
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,19 @@ extension Loader {
161161
return Handle(value: handle)
162162
}
163163

164+
public static func getSelfHandle(mode: Flags) throws -> Handle {
165+
#if os(Windows)
166+
guard let handle = GetModuleHandleW(nil) else {
167+
throw Loader.Error.open("GetModuleHandleW(nil) failure: \(GetLastError())")
168+
}
169+
#else
170+
guard let handle = dlopen(nil, mode.rawValue) else {
171+
throw Loader.Error.open(Loader.error() ?? "unknown error")
172+
}
173+
#endif
174+
return Handle(value: handle)
175+
}
176+
164177
public static func lookup<T>(symbol: String, in module: Handle) -> T? {
165178
#if os(Windows)
166179
guard let pointer = GetProcAddress(module.value!, symbol) else {

0 commit comments

Comments
 (0)