diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index 55f3ce3c5..44553388c 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -158,6 +158,10 @@ public struct Driver { /// Whether we are using the driver as the integrated driver via libSwiftDriver public let integratedDriver: Bool + /// If true, the driver instance is executed in the context of a + /// Swift compiler image which contains symbols normally queried from a libSwiftScan instance. + internal let compilerIntegratedTooling: Bool + /// The file system which we should interact with. @_spi(Testing) public let fileSystem: FileSystem @@ -409,6 +413,10 @@ public struct Driver { /// as explicit inputs by the various compilation jobs. @_spi(Testing) public var explicitDependencyBuildPlanner: ExplicitDependencyBuildPlanner? = nil + /// A reference to the instance of libSwiftScan which is shared with the driver's + /// `InterModuleDependencyOracle`, but also used for non-scanning tasks, such as target info + /// and supported compiler feature queries + @_spi(Testing) public var swiftScanLibInstance: SwiftScan? = nil /// An oracle for querying inter-module dependencies /// Can either be an argument to the driver in many-module contexts where dependency information /// is shared across many targets; otherwise, a new instance is created by the driver itself. @@ -572,6 +580,33 @@ public struct Driver { fileSystem: fileSystem, executor: executor, integratedDriver: integratedDriver, + compilerIntegratedTooling: false, + compilerExecutableDir: compilerExecutableDir, + externalTargetModuleDetailsMap: externalTargetModuleDetailsMap, + interModuleDependencyOracle: interModuleDependencyOracle + ) + } + + @available(*, deprecated, renamed: "init(args:env:diagnosticsOutput:fileSystem:executor:integratedDriver:compilerIntegratedTooling:compilerExecutableDir:externalTargetModuleDetailsMap:interModuleDependencyOracle:)") + public init( + args: [String], + env: [String: String] = ProcessEnv.vars, + diagnosticsOutput: DiagnosticsOutput = .engine(DiagnosticsEngine(handlers: [Driver.stderrDiagnosticsHandler])), + fileSystem: FileSystem = localFileSystem, + executor: DriverExecutor, + integratedDriver: Bool = true, + compilerExecutableDir: AbsolutePath? = nil, + externalTargetModuleDetailsMap: ExternalTargetModuleDetailsMap? = nil, + interModuleDependencyOracle: InterModuleDependencyOracle? = nil + ) throws { + try self.init( + args: args, + env: env, + diagnosticsOutput: diagnosticsOutput, + fileSystem: fileSystem, + executor: executor, + integratedDriver: integratedDriver, + compilerIntegratedTooling: false, compilerExecutableDir: compilerExecutableDir, externalTargetModuleDetailsMap: externalTargetModuleDetailsMap, interModuleDependencyOracle: interModuleDependencyOracle @@ -592,6 +627,8 @@ public struct Driver { /// is present to streamline testing, it shouldn't be used in production. /// - Parameter integratedDriver: Used to distinguish whether the driver is being used as /// an executable or as a library. + /// - Parameter compilerIntegratedTooling: If true, this code is executed in the context of a + /// Swift compiler image which contains symbols normally queried from a libSwiftScan instance. /// - Parameter compilerExecutableDir: Directory that contains the compiler executable to be used. /// Used when in `integratedDriver` mode as a substitute for the driver knowing its executable path. /// - Parameter externalTargetModuleDetailsMap: A dictionary of external targets that are a part of @@ -606,6 +643,7 @@ public struct Driver { fileSystem: FileSystem = localFileSystem, executor: DriverExecutor, integratedDriver: Bool = true, + compilerIntegratedTooling: Bool = false, compilerExecutableDir: AbsolutePath? = nil, externalTargetModuleDetailsMap: ExternalTargetModuleDetailsMap? = nil, interModuleDependencyOracle: InterModuleDependencyOracle? = nil @@ -613,6 +651,7 @@ public struct Driver { self.env = env self.fileSystem = fileSystem self.integratedDriver = integratedDriver + self.compilerIntegratedTooling = compilerIntegratedTooling let diagnosticsEngine: DiagnosticsEngine switch diagnosticsOutput { @@ -668,7 +707,7 @@ public struct Driver { self.useStaticResourceDir = staticExecutable || staticStdlib // Build the toolchain and determine target information. - (self.toolchain, self.frontendTargetInfo, self.swiftCompilerPrefixArgs) = + (self.toolchain, self.swiftCompilerPrefixArgs) = try Self.computeToolchain( &self.parsedOptions, diagnosticsEngine: diagnosticEngine, compilerMode: self.compilerMode, env: env, @@ -677,13 +716,40 @@ public struct Driver { workingDirectory: self.workingDirectory, compilerExecutableDir: compilerExecutableDir) + // Create an instance of an inter-module dependency oracle, if the driver's + // client did not provide one. The clients are expected to provide an oracle + // when they wish to share module dependency information across targets. + if let dependencyOracle = interModuleDependencyOracle { + self.interModuleDependencyOracle = dependencyOracle + } else { + self.interModuleDependencyOracle = InterModuleDependencyOracle() + } + + self.swiftScanLibInstance = try Self.initializeSwiftScanInstance(&parsedOptions, + diagnosticsEngine: diagnosticEngine, + toolchain: self.toolchain, + interModuleDependencyOracle: self.interModuleDependencyOracle, + fileSystem: self.fileSystem, + compilerIntegratedTooling: self.compilerIntegratedTooling) + // Compute the host machine's triple self.hostTriple = try Self.computeHostTriple(&self.parsedOptions, diagnosticsEngine: diagnosticEngine, + libSwiftScan: self.swiftScanLibInstance, toolchain: self.toolchain, executor: self.executor, fileSystem: fileSystem, - workingDirectory: self.workingDirectory, - swiftCompilerPrefixArgs: self.swiftCompilerPrefixArgs) + workingDirectory: self.workingDirectory) + + // Compute the entire target info, including runtime resource paths + self.frontendTargetInfo = try Self.computeTargetInfo(&self.parsedOptions, diagnosticsEngine: diagnosticEngine, + compilerMode: self.compilerMode, env: env, + executor: self.executor, + libSwiftScan: self.swiftScanLibInstance, + toolchain: self.toolchain, + fileSystem: fileSystem, + useStaticResourceDir: self.useStaticResourceDir, + workingDirectory: self.workingDirectory, + compilerExecutableDir: compilerExecutableDir) // Classify and collect all of the input files. let inputFiles = try Self.collectInputFiles(&self.parsedOptions, diagnosticsEngine: diagnosticsEngine, fileSystem: self.fileSystem) @@ -716,15 +782,6 @@ public struct Driver { } } - // Create an instance of an inter-module dependency oracle, if the driver's - // client did not provide one. The clients are expected to provide an oracle - // when they wish to share module dependency information across targets. - if let dependencyOracle = interModuleDependencyOracle { - self.interModuleDependencyOracle = dependencyOracle - } else { - self.interModuleDependencyOracle = InterModuleDependencyOracle() - } - self.fileListThreshold = try Self.computeFileListThreshold(&self.parsedOptions, diagnosticsEngine: diagnosticsEngine) self.shouldUseInputFileList = inputFiles.count > fileListThreshold @@ -810,6 +867,7 @@ public struct Driver { self.supportedFrontendFlags = try Self.computeSupportedCompilerArgs(of: self.toolchain, + libSwiftScan: self.swiftScanLibInstance, parsedOptions: &self.parsedOptions, diagnosticsEngine: diagnosticEngine, fileSystem: fileSystem, @@ -854,6 +912,18 @@ public struct Driver { self.scannerPrefixMapToolchain = nil } + // Initialize the CAS instance + if self.swiftScanLibInstance != nil && + self.enableCaching && + self.supportedFrontendFeatures.contains(KnownCompilerFeature.compilation_caching.rawValue) { + self.cas = + try self.interModuleDependencyOracle.getOrCreateCAS(pluginPath: try Self.getCASPluginPath(parsedOptions: &self.parsedOptions, + toolchain: self.toolchain), + onDiskPath: try Self.getOnDiskCASPath(parsedOptions: &self.parsedOptions, + toolchain: self.toolchain), + pluginOptions: try Self.getCASPluginOptions(parsedOptions: &self.parsedOptions)) + } + self.enabledSanitizers = try Self.parseSanitizerArgValues( &parsedOptions, diagnosticEngine: diagnosticEngine, @@ -3226,23 +3296,53 @@ extension Driver { static func computeHostTriple( _ parsedOptions: inout ParsedOptions, diagnosticsEngine: DiagnosticsEngine, + libSwiftScan: SwiftScan?, toolchain: Toolchain, executor: DriverExecutor, fileSystem: FileSystem, - workingDirectory: AbsolutePath?, - swiftCompilerPrefixArgs: [String]) throws -> Triple { + workingDirectory: AbsolutePath?) throws -> Triple { let frontendOverride = try FrontendOverride(&parsedOptions, diagnosticsEngine) frontendOverride.setUpForTargetInfo(toolchain) defer { frontendOverride.setUpForCompilation(toolchain) } return try Self.computeTargetInfo(target: nil, targetVariant: nil, swiftCompilerPrefixArgs: frontendOverride.prefixArgsForTargetInfo, + libSwiftScan: libSwiftScan, toolchain: toolchain, fileSystem: fileSystem, workingDirectory: workingDirectory, diagnosticsEngine: diagnosticsEngine, executor: executor).target.triple } + static func initializeSwiftScanInstance( + _ parsedOptions: inout ParsedOptions, + diagnosticsEngine: DiagnosticsEngine, + toolchain: Toolchain, + interModuleDependencyOracle: InterModuleDependencyOracle, + fileSystem: FileSystem, + compilerIntegratedTooling: Bool) throws -> SwiftScan? { + guard !parsedOptions.hasArgument(.driverScanDependenciesNonLib) else { + return nil + } + + let swiftScanLibPath: AbsolutePath? = compilerIntegratedTooling ? nil : try toolchain.lookupSwiftScanLib() + do { + guard compilerIntegratedTooling || + (swiftScanLibPath != nil && fileSystem.exists(swiftScanLibPath!)) else { + diagnosticsEngine.emit(.warn_scan_dylib_not_found()) + return nil + } + + // Ensure the oracle initializes or verifies the existing scanner instance + try interModuleDependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: swiftScanLibPath) + // The driver needs a reference to this for non-scanning tasks + return interModuleDependencyOracle.getScannerInstance() + } catch { + diagnosticsEngine.emit(.warn_scan_dylib_load_failed(swiftScanLibPath?.description ?? "built-in")) + } + return nil + } + static func computeToolchain( _ parsedOptions: inout ParsedOptions, diagnosticsEngine: DiagnosticsEngine, @@ -3253,23 +3353,11 @@ extension Driver { useStaticResourceDir: Bool, workingDirectory: AbsolutePath?, compilerExecutableDir: AbsolutePath? - ) throws -> (Toolchain, FrontendTargetInfo, [String]) { + ) throws -> (Toolchain, [String]) { let explicitTarget = (parsedOptions.getLastArgument(.target)?.asSingle) .map { Triple($0, normalizing: true) } - let explicitTargetVariant = (parsedOptions.getLastArgument(.targetVariant)?.asSingle) - .map { - Triple($0, normalizing: true) - } - - // Determine the resource directory. - let resourceDirPath: VirtualPath? - if let resourceDirArg = parsedOptions.getLastArgument(.resourceDir) { - resourceDirPath = try VirtualPath(path: resourceDirArg.asSingle) - } else { - resourceDirPath = nil - } let toolchainType = try explicitTarget?.toolchainType(diagnosticsEngine) ?? defaultToolchainType @@ -3283,30 +3371,61 @@ extension Driver { compilerExecutableDir: compilerExecutableDir, toolDirectory: toolDir) + let frontendOverride = try FrontendOverride(&parsedOptions, diagnosticsEngine) + return (toolchain, frontendOverride.prefixArgs) + } + + static func computeTargetInfo(_ parsedOptions: inout ParsedOptions, + diagnosticsEngine: DiagnosticsEngine, + compilerMode: CompilerMode, + env: [String: String], + executor: DriverExecutor, + libSwiftScan: SwiftScan?, + toolchain: Toolchain, + fileSystem: FileSystem, + useStaticResourceDir: Bool, + workingDirectory: AbsolutePath?, + compilerExecutableDir: AbsolutePath?) throws -> FrontendTargetInfo { + let explicitTarget = (parsedOptions.getLastArgument(.target)?.asSingle) + .map { + Triple($0, normalizing: true) + } + let explicitTargetVariant = (parsedOptions.getLastArgument(.targetVariant)?.asSingle) + .map { + Triple($0, normalizing: true) + } + let frontendOverride = try FrontendOverride(&parsedOptions, diagnosticsEngine) frontendOverride.setUpForTargetInfo(toolchain) defer { frontendOverride.setUpForCompilation(toolchain) } + // Find the SDK, if any. let sdkPath: VirtualPath? = Self.computeSDKPath( &parsedOptions, compilerMode: compilerMode, toolchain: toolchain, targetTriple: explicitTarget, fileSystem: fileSystem, diagnosticsEngine: diagnosticsEngine, env: env) - // Query the frontend for target information. do { + // Determine the resource directory. + let resourceDirPath: VirtualPath? + if let resourceDirArg = parsedOptions.getLastArgument(.resourceDir) { + resourceDirPath = try VirtualPath(path: resourceDirArg.asSingle) + } else { + resourceDirPath = nil + } var info: FrontendTargetInfo = try Self.computeTargetInfo(target: explicitTarget, targetVariant: explicitTargetVariant, sdkPath: sdkPath, resourceDirPath: resourceDirPath, runtimeCompatibilityVersion: - parsedOptions.getLastArgument(.runtimeCompatibilityVersion)?.asSingle, + parsedOptions.getLastArgument(.runtimeCompatibilityVersion)?.asSingle, useStaticResourceDir: useStaticResourceDir, swiftCompilerPrefixArgs: frontendOverride.prefixArgsForTargetInfo, + libSwiftScan: libSwiftScan, toolchain: toolchain, fileSystem: fileSystem, workingDirectory: workingDirectory, diagnosticsEngine: diagnosticsEngine, executor: executor) - // Parse the runtime compatibility version. If present, it will override // what is reported by the frontend. if let versionString = @@ -3328,23 +3447,23 @@ extension Driver { diagnosticsEngine.emit(.warning_inferring_simulator_target(originalTriple: explicitTarget, inferredTriple: info.target.triple)) } - return (toolchain, info, frontendOverride.prefixArgs) + return info } catch let JobExecutionError.decodingError(decodingError, dataToDecode, processResult) { let stringToDecode = String(data: dataToDecode, encoding: .utf8) let errorDesc: String switch decodingError { - case let .typeMismatch(type, context): - errorDesc = "type mismatch: \(type), path: \(context.codingPath)" - case let .valueNotFound(type, context): - errorDesc = "value missing: \(type), path: \(context.codingPath)" - case let .keyNotFound(key, context): - errorDesc = "key missing: \(key), path: \(context.codingPath)" - case let .dataCorrupted(context): - errorDesc = "data corrupted at path: \(context.codingPath)" - @unknown default: - errorDesc = "unknown decoding error" + case let .typeMismatch(type, context): + errorDesc = "type mismatch: \(type), path: \(context.codingPath)" + case let .valueNotFound(type, context): + errorDesc = "value missing: \(type), path: \(context.codingPath)" + case let .keyNotFound(key, context): + errorDesc = "key missing: \(key), path: \(context.codingPath)" + case let .dataCorrupted(context): + errorDesc = "data corrupted at path: \(context.codingPath)" + @unknown default: + errorDesc = "unknown decoding error" } throw Error.unableToDecodeFrontendTargetInfo( stringToDecode, @@ -3638,21 +3757,23 @@ extension Driver { // CAS and Caching. extension Driver { - mutating func getCASPluginPath() throws -> AbsolutePath? { + static func getCASPluginPath(parsedOptions: inout ParsedOptions, + toolchain: Toolchain) throws -> AbsolutePath? { if let pluginPath = parsedOptions.getLastArgument(.casPluginPath)?.asSingle { return try AbsolutePath(validating: pluginPath.description) } return try toolchain.lookupToolchainCASPluginLib() } - mutating func getOnDiskCASPath() throws -> AbsolutePath? { + static func getOnDiskCASPath(parsedOptions: inout ParsedOptions, + toolchain: Toolchain) throws -> AbsolutePath? { if let casPathOpt = parsedOptions.getLastArgument(.casPath)?.asSingle { return try AbsolutePath(validating: casPathOpt.description) } return nil; } - mutating func getCASPluginOptions() throws -> [(String, String)] { + static func getCASPluginOptions(parsedOptions: inout ParsedOptions) throws -> [(String, String)] { var options : [(String, String)] = [] for opt in parsedOptions.arguments(for: .casPluginOption) { let pluginArg = opt.argument.asSingle.split(separator: "=", maxSplits: 1) diff --git a/Sources/SwiftDriver/Execution/DriverExecutor.swift b/Sources/SwiftDriver/Execution/DriverExecutor.swift index b93c45ad6..2628c2d5a 100644 --- a/Sources/SwiftDriver/Execution/DriverExecutor.swift +++ b/Sources/SwiftDriver/Execution/DriverExecutor.swift @@ -80,7 +80,7 @@ public struct DriverExecutorWorkload { } } -enum JobExecutionError: Error { +@_spi(Testing) public enum JobExecutionError: Error { case jobFailedWithNonzeroExitCode(Int, String) case failedToReadJobOutput // A way to pass more information to the catch point diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift index d389071c5..c6087f194 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift @@ -82,17 +82,24 @@ public class InterModuleDependencyOracle { diagnostics: &diagnostics) } - /// Given a specified toolchain path, locate and instantiate an instance of the SwiftScan library + @available(*, deprecated, message: "use verifyOrCreateScannerInstance(swiftScanLibPath:)") public func verifyOrCreateScannerInstance(fileSystem: FileSystem, swiftScanLibPath: AbsolutePath) throws { + return try verifyOrCreateScannerInstance(swiftScanLibPath: swiftScanLibPath) + } + + /// Given a specified toolchain path, locate and instantiate an instance of the SwiftScan library + public func verifyOrCreateScannerInstance(swiftScanLibPath: AbsolutePath?) throws { return try queue.sync { - if swiftScanLibInstance == nil { + guard let scanInstance = swiftScanLibInstance else { swiftScanLibInstance = try SwiftScan(dylib: swiftScanLibPath) - } else { - guard swiftScanLibInstance!.path == swiftScanLibPath else { - throw DependencyScanningError - .scanningLibraryInvocationMismatch(swiftScanLibInstance!.path, swiftScanLibPath) - } + return + } + + guard scanInstance.path?.description == swiftScanLibPath?.description else { + throw DependencyScanningError + .scanningLibraryInvocationMismatch(scanInstance.path?.description ?? "built-in", + swiftScanLibPath?.description ?? "built-in") } } } @@ -209,10 +216,20 @@ public class InterModuleDependencyOracle { } } + // Note: this is `true` even in the `compilerIntegratedTooling` mode + // where the `SwiftScan` instance refers to the own image the driver is + // running in, since there is still technically a `SwiftScan` handle + // capable of handling API requests expected of it. private var hasScannerInstance: Bool { self.swiftScanLibInstance != nil } + func getScannerInstance() -> SwiftScan? { + self.swiftScanLibInstance + } + func setScannerInstance(_ instance: SwiftScan?) { + self.swiftScanLibInstance = instance + } /// Queue to sunchronize accesses to the scanner - internal let queue = DispatchQueue(label: "org.swift.swift-driver.swift-scan") + let queue = DispatchQueue(label: "org.swift.swift-driver.swift-scan") /// A reference to an instance of the compiler's libSwiftScan shared library private var swiftScanLibInstance: SwiftScan? = nil diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift index 23be50297..70a4e5f9f 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift @@ -164,41 +164,6 @@ public extension Driver { contents) } - /// Returns false if the lib is available and ready to use - private mutating func initSwiftScanLib() throws -> Bool { - // `-nonlib-dependency-scanner` was specified - guard !parsedOptions.hasArgument(.driverScanDependenciesNonLib) else { - return true - } - - // If the libSwiftScan library cannot be found, - // attempt to fallback to using `swift-frontend -scan-dependencies` invocations for dependency - // scanning. - guard let scanLibPath = try toolchain.lookupSwiftScanLib(), - fileSystem.exists(scanLibPath) else { - diagnosticEngine.emit(.warn_scan_dylib_not_found()) - return true - } - - do { - try interModuleDependencyOracle.verifyOrCreateScannerInstance(fileSystem: fileSystem, - swiftScanLibPath: scanLibPath) - if isCachingEnabled { - self.cas = try interModuleDependencyOracle.getOrCreateCAS(pluginPath: try getCASPluginPath(), - onDiskPath: try getOnDiskCASPath(), - pluginOptions: try getCASPluginOptions()) - } - } catch { - if isCachingEnabled { - diagnosticEngine.emit(.error_caching_enabled_libswiftscan_load_failure(scanLibPath.description)) - } else { - diagnosticEngine.emit(.warn_scan_dylib_load_failed(scanLibPath.description)) - } - return true - } - return false - } - static func sanitizeCommandForLibScanInvocation(_ command: inout [String]) { // Remove the tool executable to only leave the arguments. When passing the // command line into libSwiftScan, the library is itself the tool and only @@ -217,8 +182,7 @@ public extension Driver { let forceResponseFiles = parsedOptions.hasArgument(.driverForceResponseFiles) let imports: InterModuleDependencyImports - let isSwiftScanLibAvailable = !(try initSwiftScanLib()) - if isSwiftScanLibAvailable { + if supportInProcessSwiftScanQueries { var scanDiagnostics: [ScannerDiagnosticPayload] = [] guard let cwd = workingDirectory else { throw DependencyScanningError.dependencyScanFailed("cannot determine working directory") @@ -294,8 +258,7 @@ public extension Driver { stdoutStream.flush() } - let isSwiftScanLibAvailable = !(try initSwiftScanLib()) - if isSwiftScanLibAvailable { + if supportInProcessSwiftScanQueries { var scanDiagnostics: [ScannerDiagnosticPayload] = [] guard let cwd = workingDirectory else { throw DependencyScanningError.dependencyScanFailed("cannot determine working directory") @@ -333,8 +296,7 @@ public extension Driver { let forceResponseFiles = parsedOptions.hasArgument(.driverForceResponseFiles) let moduleVersionedGraphMap: [ModuleDependencyId: [InterModuleDependencyGraph]] - let isSwiftScanLibAvailable = !(try initSwiftScanLib()) - if isSwiftScanLibAvailable { + if supportInProcessSwiftScanQueries { var scanDiagnostics: [ScannerDiagnosticPayload] = [] guard let cwd = workingDirectory else { throw DependencyScanningError.dependencyScanFailed("cannot determine working directory") @@ -502,3 +464,7 @@ public extension Driver { .parentDirectory // toolchain root } } + +extension Driver { + var supportInProcessSwiftScanQueries: Bool { return self.swiftScanLibInstance != nil } +} diff --git a/Sources/SwiftDriver/Jobs/EmitSupportedFeaturesJob.swift b/Sources/SwiftDriver/Jobs/EmitSupportedFeaturesJob.swift index 1adfbd827..06fce203d 100644 --- a/Sources/SwiftDriver/Jobs/EmitSupportedFeaturesJob.swift +++ b/Sources/SwiftDriver/Jobs/EmitSupportedFeaturesJob.swift @@ -59,18 +59,19 @@ extension Toolchain { extension Driver { static func computeSupportedCompilerArgs(of toolchain: Toolchain, + libSwiftScan: SwiftScan?, parsedOptions: inout ParsedOptions, diagnosticsEngine: DiagnosticsEngine, fileSystem: FileSystem, executor: DriverExecutor) throws -> Set { - do { - if let supportedArgs = - try querySupportedCompilerArgsInProcess(of: toolchain, fileSystem: fileSystem) { - return supportedArgs + if let libSwiftScanInstance = libSwiftScan, + libSwiftScanInstance.canQuerySupportedArguments() { + do { + return try libSwiftScanInstance.querySupportedArguments() + } catch { + diagnosticsEngine.emit(.remark_inprocess_supported_features_query_failed(error.localizedDescription)) } - } catch { - diagnosticsEngine.emit(.remark_inprocess_supported_features_query_failed(error.localizedDescription)) } // Fallback: Invoke `swift-frontend -emit-supported-features` and decode the output @@ -88,20 +89,6 @@ extension Driver { return Set(decodedSupportedFlagList) } - static func querySupportedCompilerArgsInProcess(of toolchain: Toolchain, - fileSystem: FileSystem) - throws -> Set? { - let optionalSwiftScanLibPath = try toolchain.lookupSwiftScanLib() - if let swiftScanLibPath = optionalSwiftScanLibPath, - fileSystem.exists(swiftScanLibPath) { - let libSwiftScanInstance = try SwiftScan(dylib: swiftScanLibPath) - if libSwiftScanInstance.canQuerySupportedArguments() { - return try libSwiftScanInstance.querySupportedArguments() - } - } - return nil - } - static func computeSupportedCompilerFeatures(of toolchain: Toolchain, env: [String: String]) throws -> Set { struct FeatureInfo: Codable { diff --git a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift index 0061a4b04..386412b5e 100644 --- a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift +++ b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift @@ -406,11 +406,13 @@ extension Driver { // CAS related options. if isCachingEnabled { commandLine.appendFlag(.cacheCompileJob) - if let casPath = try getOnDiskCASPath() { + if let casPath = try Self.getOnDiskCASPath(parsedOptions: &parsedOptions, + toolchain: toolchain) { commandLine.appendFlag(.casPath) commandLine.appendFlag(casPath.pathString) } - if let pluginPath = try getCASPluginPath() { + if let pluginPath = try Self.getCASPluginPath(parsedOptions: &parsedOptions, + toolchain: toolchain) { commandLine.appendFlag(.casPluginPath) commandLine.appendFlag(pluginPath.pathString) } diff --git a/Sources/SwiftDriver/Jobs/PrintTargetInfoJob.swift b/Sources/SwiftDriver/Jobs/PrintTargetInfoJob.swift index 679e63533..5839b8bd7 100644 --- a/Sources/SwiftDriver/Jobs/PrintTargetInfoJob.swift +++ b/Sources/SwiftDriver/Jobs/PrintTargetInfoJob.swift @@ -181,46 +181,39 @@ extension Toolchain { } extension Driver { - @_spi(Testing) public static func queryTargetInfoInProcess(of toolchain: Toolchain, + @_spi(Testing) public static func queryTargetInfoInProcess(libSwiftScanInstance: SwiftScan, + toolchain: Toolchain, fileSystem: FileSystem, workingDirectory: AbsolutePath?, - invocationCommand: [String]) throws -> FrontendTargetInfo? { - let optionalSwiftScanLibPath = try toolchain.lookupSwiftScanLib() - if let swiftScanLibPath = optionalSwiftScanLibPath, - fileSystem.exists(swiftScanLibPath) { - let libSwiftScanInstance = try SwiftScan(dylib: swiftScanLibPath) - if libSwiftScanInstance.canQueryTargetInfo() { - let cwd = try workingDirectory ?? fileSystem.tempDirectory - let compilerExecutablePath = try toolchain.resolvedTool(.swiftCompiler).path - let targetInfoData = - try libSwiftScanInstance.queryTargetInfoJSON(workingDirectory: cwd, - compilerExecutablePath: compilerExecutablePath, - invocationCommand: invocationCommand) - do { - return try JSONDecoder().decode(FrontendTargetInfo.self, from: targetInfoData) - } catch let decodingError as DecodingError { - let stringToDecode = String(data: targetInfoData, encoding: .utf8) - let errorDesc: String - switch decodingError { - case let .typeMismatch(type, context): - errorDesc = "type mismatch: \(type), path: \(context.codingPath)" - case let .valueNotFound(type, context): - errorDesc = "value missing: \(type), path: \(context.codingPath)" - case let .keyNotFound(key, context): - errorDesc = "key missing: \(key), path: \(context.codingPath)" - case let .dataCorrupted(context): - errorDesc = "data corrupted at path: \(context.codingPath)" - @unknown default: - errorDesc = "unknown decoding error" - } - throw Error.unableToDecodeFrontendTargetInfo( - stringToDecode, - invocationCommand, - errorDesc) - } + invocationCommand: [String]) throws -> FrontendTargetInfo { + let cwd = try workingDirectory ?? fileSystem.tempDirectory + let compilerExecutablePath = try toolchain.resolvedTool(.swiftCompiler).path + let targetInfoData = + try libSwiftScanInstance.queryTargetInfoJSON(workingDirectory: cwd, + compilerExecutablePath: compilerExecutablePath, + invocationCommand: invocationCommand) + do { + return try JSONDecoder().decode(FrontendTargetInfo.self, from: targetInfoData) + } catch let decodingError as DecodingError { + let stringToDecode = String(data: targetInfoData, encoding: .utf8) + let errorDesc: String + switch decodingError { + case let .typeMismatch(type, context): + errorDesc = "type mismatch: \(type), path: \(context.codingPath)" + case let .valueNotFound(type, context): + errorDesc = "value missing: \(type), path: \(context.codingPath)" + case let .keyNotFound(key, context): + errorDesc = "key missing: \(key), path: \(context.codingPath)" + case let .dataCorrupted(context): + errorDesc = "data corrupted at path: \(context.codingPath)" + @unknown default: + errorDesc = "unknown decoding error" } + throw Error.unableToDecodeFrontendTargetInfo( + stringToDecode, + invocationCommand, + errorDesc) } - return nil } static func computeTargetInfo(target: Triple?, @@ -231,6 +224,7 @@ extension Driver { requiresInPlaceExecution: Bool = false, useStaticResourceDir: Bool = false, swiftCompilerPrefixArgs: [String], + libSwiftScan: SwiftScan?, toolchain: Toolchain, fileSystem: FileSystem, workingDirectory: AbsolutePath?, @@ -243,20 +237,19 @@ extension Driver { requiresInPlaceExecution: requiresInPlaceExecution, useStaticResourceDir: useStaticResourceDir, swiftCompilerPrefixArgs: swiftCompilerPrefixArgs) - var command = try Self.itemizedJobCommand(of: frontendTargetInfoJob, - useResponseFiles: .disabled, - using: executor.resolver) - Self.sanitizeCommandForLibScanInvocation(&command) - - do { - if let targetInfo = - try Self.queryTargetInfoInProcess(of: toolchain, fileSystem: fileSystem, - workingDirectory: workingDirectory, - invocationCommand: command) { - return targetInfo + if let libSwiftScanInstance = libSwiftScan, + libSwiftScanInstance.canQueryTargetInfo() { + do { + var command = try Self.itemizedJobCommand(of: frontendTargetInfoJob, + useResponseFiles: .disabled, + using: executor.resolver) + Self.sanitizeCommandForLibScanInvocation(&command) + return try Self.queryTargetInfoInProcess(libSwiftScanInstance: libSwiftScanInstance, toolchain: toolchain, + fileSystem: fileSystem, workingDirectory: workingDirectory, + invocationCommand: command) + } catch { + diagnosticsEngine.emit(.remark_inprocess_target_info_query_failed(error.localizedDescription)) } - } catch { - diagnosticsEngine.emit(.remark_inprocess_target_info_query_failed(error.localizedDescription)) } // Fallback: Invoke `swift-frontend -print-target-info` and decode the output diff --git a/Sources/SwiftDriver/SwiftScan/Loader.swift b/Sources/SwiftDriver/SwiftScan/Loader.swift index 2ab4e6fd4..0c4f0928c 100644 --- a/Sources/SwiftDriver/SwiftScan/Loader.swift +++ b/Sources/SwiftDriver/SwiftScan/Loader.swift @@ -161,6 +161,19 @@ extension Loader { return Handle(value: handle) } + public static func getSelfHandle(mode: Flags) throws -> Handle { +#if os(Windows) + guard let handle = GetModuleHandleW(nil) else { + throw Loader.Error.open("GetModuleHandleW(nil) failure: \(GetLastError())") + } +#else + guard let handle = dlopen(nil, mode.rawValue) else { + throw Loader.Error.open(Loader.error() ?? "unknown error") + } +#endif + return Handle(value: handle) + } + public static func lookup(symbol: String, in module: Handle) -> T? { #if os(Windows) guard let pointer = GetProcAddress(module.value!, symbol) else { diff --git a/Sources/SwiftDriver/SwiftScan/SwiftScan.swift b/Sources/SwiftDriver/SwiftScan/SwiftScan.swift index 87b8fc538..b5f80e918 100644 --- a/Sources/SwiftDriver/SwiftScan/SwiftScan.swift +++ b/Sources/SwiftDriver/SwiftScan/SwiftScan.swift @@ -32,7 +32,7 @@ public enum DependencyScanningError: LocalizedError, DiagnosticData, Equatable { case moduleNameDecodeFailure(String) case unsupportedDependencyDetailsKind(Int) case invalidStringPtr - case scanningLibraryInvocationMismatch(AbsolutePath, AbsolutePath) + case scanningLibraryInvocationMismatch(String, String) case scanningLibraryNotFound(AbsolutePath) case argumentQueryFailed case unsupportedConfigurationForCaching(String) @@ -56,7 +56,7 @@ public enum DependencyScanningError: LocalizedError, DiagnosticData, Equatable { case .invalidStringPtr: return "Dependency module details contains a corrupted string reference" case .scanningLibraryInvocationMismatch(let path1, let path2): - return "Dependency Scanning library differs across driver invocations: \(path1.description) and \(path2.description)" + return "Dependency Scanning library differs across driver invocations: \(path1) and \(path2)" case .scanningLibraryNotFound(let path): return "Dependency Scanning library not found at path: \(path)" case .argumentQueryFailed: @@ -112,7 +112,7 @@ private extension String { /// Wrapper for libSwiftScan, taking care of initialization, shutdown, and dispatching dependency scanning queries. @_spi(Testing) public final class SwiftScan { /// The path to the libSwiftScan dylib. - let path: AbsolutePath + let path: AbsolutePath? /// The handle to the dylib. let dylib: Loader.Handle @@ -123,13 +123,21 @@ private extension String { /// Instance of a scanner, which maintains shared state across scan queries. let scanner: swiftscan_scanner_t; - @_spi(Testing) public init(dylib path: AbsolutePath) throws { + @_spi(Testing) public init(dylib path: AbsolutePath? = nil) throws { self.path = path - #if os(Windows) - self.dylib = try Loader.load(path.pathString, mode: []) - #else - self.dylib = try Loader.load(path.pathString, mode: [.lazy, .local, .first]) - #endif + if let externalPath = path { +#if os(Windows) + self.dylib = try Loader.load(externalPath.pathString, mode: []) +#else + self.dylib = try Loader.load(externalPath.pathString, mode: [.lazy, .local, .first]) +#endif + } else { +#if os(Windows) + self.dylib = try Loader.getSelfHandle(mode: []) +#else + self.dylib = try Loader.getSelfHandle(mode: [.lazy, .local, .first]) +#endif + } self.api = try swiftscan_functions_t(self.dylib) guard let scanner = api.swiftscan_scanner_create() else { throw DependencyScanningError.failedToInstantiateScanner @@ -535,7 +543,7 @@ private extension swiftscan_functions_t { // MARK: Optional Methods // Future optional methods can be queried here - func loadOptional(_ symbol: String) throws -> T? { + func loadOptional(_ symbol: String) -> T? { guard let sym: T = Loader.lookup(symbol: symbol, in: swiftscan) else { return nil } @@ -543,130 +551,130 @@ private extension swiftscan_functions_t { } // Supported features/flags query self.swiftscan_string_set_dispose = - try loadOptional("swiftscan_string_set_dispose") + loadOptional("swiftscan_string_set_dispose") self.swiftscan_compiler_supported_arguments_query = - try loadOptional("swiftscan_compiler_supported_arguments_query") + loadOptional("swiftscan_compiler_supported_arguments_query") self.swiftscan_compiler_supported_features_query = - try loadOptional("swiftscan_compiler_supported_features_query") + loadOptional("swiftscan_compiler_supported_features_query") // Target Info query self.swiftscan_compiler_target_info_query_v2 = - try loadOptional("swiftscan_compiler_target_info_query_v2") + loadOptional("swiftscan_compiler_target_info_query_v2") // Dependency scanner serialization/deserialization features self.swiftscan_scanner_cache_serialize = - try loadOptional("swiftscan_scanner_cache_serialize") + loadOptional("swiftscan_scanner_cache_serialize") self.swiftscan_scanner_cache_load = - try loadOptional("swiftscan_scanner_cache_load") + loadOptional("swiftscan_scanner_cache_load") self.swiftscan_scanner_cache_reset = - try loadOptional("swiftscan_scanner_cache_reset") + loadOptional("swiftscan_scanner_cache_reset") // Clang dependency captured PCM args self.swiftscan_clang_detail_get_captured_pcm_args = - try loadOptional("swiftscan_clang_detail_get_captured_pcm_args") + loadOptional("swiftscan_clang_detail_get_captured_pcm_args") // Scanner diagnostic emission query self.swiftscan_scanner_diagnostics_query = - try loadOptional("swiftscan_scanner_diagnostics_query") + loadOptional("swiftscan_scanner_diagnostics_query") self.swiftscan_scanner_diagnostics_reset = - try loadOptional("swiftscan_scanner_diagnostics_reset") + loadOptional("swiftscan_scanner_diagnostics_reset") self.swiftscan_diagnostic_get_message = - try loadOptional("swiftscan_diagnostic_get_message") + loadOptional("swiftscan_diagnostic_get_message") self.swiftscan_diagnostic_get_severity = - try loadOptional("swiftscan_diagnostic_get_severity") + loadOptional("swiftscan_diagnostic_get_severity") self.swiftscan_diagnostics_set_dispose = - try loadOptional("swiftscan_diagnostics_set_dispose") + loadOptional("swiftscan_diagnostics_set_dispose") self.swiftscan_string_dispose = - try loadOptional("swiftscan_string_dispose") + loadOptional("swiftscan_string_dispose") // isFramework on binary module dependencies self.swiftscan_swift_binary_detail_get_is_framework = - try loadOptional("swiftscan_swift_binary_detail_get_is_framework") + loadOptional("swiftscan_swift_binary_detail_get_is_framework") // Clang module dependencies of header input of binary module dependencies self.swiftscan_swift_binary_detail_get_header_dependency_module_dependencies = - try loadOptional("swiftscan_swift_binary_detail_get_header_dependency_module_dependencies") + loadOptional("swiftscan_swift_binary_detail_get_header_dependency_module_dependencies") // Bridging PCH build command-line self.swiftscan_swift_textual_detail_get_bridging_pch_command_line = - try loadOptional("swiftscan_swift_textual_detail_get_bridging_pch_command_line") + loadOptional("swiftscan_swift_textual_detail_get_bridging_pch_command_line") // Caching related APIs. self.swiftscan_swift_textual_detail_get_module_cache_key = - try loadOptional("swiftscan_swift_textual_detail_get_module_cache_key") + loadOptional("swiftscan_swift_textual_detail_get_module_cache_key") self.swiftscan_swift_binary_detail_get_module_cache_key = - try loadOptional("swiftscan_swift_binary_detail_get_module_cache_key") + loadOptional("swiftscan_swift_binary_detail_get_module_cache_key") self.swiftscan_clang_detail_get_module_cache_key = - try loadOptional("swiftscan_clang_detail_get_module_cache_key") - - self.swiftscan_cas_options_create = try loadOptional("swiftscan_cas_options_create") - self.swiftscan_cas_options_set_plugin_path = try loadOptional("swiftscan_cas_options_set_plugin_path") - self.swiftscan_cas_options_set_ondisk_path = try loadOptional("swiftscan_cas_options_set_ondisk_path") - self.swiftscan_cas_options_set_plugin_option = try loadOptional("swiftscan_cas_options_set_plugin_option") - self.swiftscan_cas_options_dispose = try loadOptional("swiftscan_cas_options_dispose") - self.swiftscan_cas_create_from_options = try loadOptional("swiftscan_cas_create_from_options") - self.swiftscan_cas_get_ondisk_size = try loadOptional("swiftscan_cas_get_ondisk_size") - self.swiftscan_cas_set_ondisk_size_limit = try loadOptional("swiftscan_cas_set_ondisk_size_limit") - self.swiftscan_cas_prune_ondisk_data = try loadOptional("swiftscan_cas_prune_ondisk_data") - self.swiftscan_cas_dispose = try loadOptional("swiftscan_cas_dispose") - self.swiftscan_cache_compute_key = try loadOptional("swiftscan_cache_compute_key") - self.swiftscan_cache_compute_key_from_input_index = try loadOptional("swiftscan_cache_compute_key_from_input_index") - self.swiftscan_cas_store = try loadOptional("swiftscan_cas_store") - - self.swiftscan_cache_query = try loadOptional("swiftscan_cache_query") - self.swiftscan_cache_query_async = try loadOptional("swiftscan_cache_query_async") - - self.swiftscan_cached_compilation_get_num_outputs = try loadOptional("swiftscan_cached_compilation_get_num_outputs") - self.swiftscan_cached_compilation_get_output = try loadOptional("swiftscan_cached_compilation_get_output") - self.swiftscan_cached_compilation_make_global_async = try loadOptional("swiftscan_cached_compilation_make_global_async") - self.swiftscan_cached_compilation_is_uncacheable = try loadOptional("swiftscan_cached_compilation_is_uncacheable") - self.swiftscan_cached_compilation_dispose = try loadOptional("swiftscan_cached_compilation_dispose") - - self.swiftscan_cached_output_load = try loadOptional("swiftscan_cached_output_load") - self.swiftscan_cached_output_load_async = try loadOptional("swiftscan_cached_output_load_async") - self.swiftscan_cached_output_is_materialized = try loadOptional("swiftscan_cached_output_is_materialized") - self.swiftscan_cached_output_get_casid = try loadOptional("swiftscan_cached_output_get_casid") - self.swiftscan_cached_output_get_name = try loadOptional("swiftscan_cached_output_get_name") - self.swiftscan_cached_output_dispose = try loadOptional("swiftscan_cached_output_dispose") - - self.swiftscan_cache_action_cancel = try loadOptional("swiftscan_cache_action_cancel") - self.swiftscan_cache_cancellation_token_dispose = try loadOptional("swiftscan_cache_cancellation_token_dispose") - - self.swiftscan_cache_download_cas_object_async = try loadOptional("swiftscan_cache_download_cas_object_async") - - self.swiftscan_cache_replay_instance_create = try loadOptional("swiftscan_cache_replay_instance_create") - self.swiftscan_cache_replay_instance_dispose = try loadOptional("swiftscan_cache_replay_instance_dispose") - self.swiftscan_cache_replay_compilation = try loadOptional("swiftscan_cache_replay_compilation") - - self.swiftscan_cache_replay_result_get_stdout = try loadOptional("swiftscan_cache_replay_result_get_stdout") - self.swiftscan_cache_replay_result_get_stderr = try loadOptional("swiftscan_cache_replay_result_get_stderr") - self.swiftscan_cache_replay_result_dispose = try loadOptional("swiftscan_cache_replay_result_dispose") - - self.swiftscan_diagnostic_get_source_location = try loadOptional("swiftscan_diagnostic_get_source_location") - self.swiftscan_source_location_get_buffer_identifier = try loadOptional("swiftscan_source_location_get_buffer_identifier") - self.swiftscan_source_location_get_line_number = try loadOptional("swiftscan_source_location_get_line_number") - self.swiftscan_source_location_get_column_number = try loadOptional("swiftscan_source_location_get_column_number") - - self.swiftscan_module_info_get_link_libraries = try loadOptional("swiftscan_module_info_get_link_libraries") - self.swiftscan_link_library_info_get_link_name = try loadOptional("swiftscan_link_library_info_get_link_name") - self.swiftscan_link_library_info_get_is_framework = try loadOptional("swiftscan_link_library_info_get_is_framework") - self.swiftscan_link_library_info_get_should_force_load = try loadOptional("swiftscan_link_library_info_get_should_force_load") + loadOptional("swiftscan_clang_detail_get_module_cache_key") + + self.swiftscan_cas_options_create = loadOptional("swiftscan_cas_options_create") + self.swiftscan_cas_options_set_plugin_path = loadOptional("swiftscan_cas_options_set_plugin_path") + self.swiftscan_cas_options_set_ondisk_path = loadOptional("swiftscan_cas_options_set_ondisk_path") + self.swiftscan_cas_options_set_plugin_option = loadOptional("swiftscan_cas_options_set_plugin_option") + self.swiftscan_cas_options_dispose = loadOptional("swiftscan_cas_options_dispose") + self.swiftscan_cas_create_from_options = loadOptional("swiftscan_cas_create_from_options") + self.swiftscan_cas_get_ondisk_size = loadOptional("swiftscan_cas_get_ondisk_size") + self.swiftscan_cas_set_ondisk_size_limit = loadOptional("swiftscan_cas_set_ondisk_size_limit") + self.swiftscan_cas_prune_ondisk_data = loadOptional("swiftscan_cas_prune_ondisk_data") + self.swiftscan_cas_dispose = loadOptional("swiftscan_cas_dispose") + self.swiftscan_cache_compute_key = loadOptional("swiftscan_cache_compute_key") + self.swiftscan_cache_compute_key_from_input_index = loadOptional("swiftscan_cache_compute_key_from_input_index") + self.swiftscan_cas_store = loadOptional("swiftscan_cas_store") + + self.swiftscan_cache_query = loadOptional("swiftscan_cache_query") + self.swiftscan_cache_query_async = loadOptional("swiftscan_cache_query_async") + + self.swiftscan_cached_compilation_get_num_outputs = loadOptional("swiftscan_cached_compilation_get_num_outputs") + self.swiftscan_cached_compilation_get_output = loadOptional("swiftscan_cached_compilation_get_output") + self.swiftscan_cached_compilation_make_global_async = loadOptional("swiftscan_cached_compilation_make_global_async") + self.swiftscan_cached_compilation_is_uncacheable = loadOptional("swiftscan_cached_compilation_is_uncacheable") + self.swiftscan_cached_compilation_dispose = loadOptional("swiftscan_cached_compilation_dispose") + + self.swiftscan_cached_output_load = loadOptional("swiftscan_cached_output_load") + self.swiftscan_cached_output_load_async = loadOptional("swiftscan_cached_output_load_async") + self.swiftscan_cached_output_is_materialized = loadOptional("swiftscan_cached_output_is_materialized") + self.swiftscan_cached_output_get_casid = loadOptional("swiftscan_cached_output_get_casid") + self.swiftscan_cached_output_get_name = loadOptional("swiftscan_cached_output_get_name") + self.swiftscan_cached_output_dispose = loadOptional("swiftscan_cached_output_dispose") + + self.swiftscan_cache_action_cancel = loadOptional("swiftscan_cache_action_cancel") + self.swiftscan_cache_cancellation_token_dispose = loadOptional("swiftscan_cache_cancellation_token_dispose") + + self.swiftscan_cache_download_cas_object_async = loadOptional("swiftscan_cache_download_cas_object_async") + + self.swiftscan_cache_replay_instance_create = loadOptional("swiftscan_cache_replay_instance_create") + self.swiftscan_cache_replay_instance_dispose = loadOptional("swiftscan_cache_replay_instance_dispose") + self.swiftscan_cache_replay_compilation = loadOptional("swiftscan_cache_replay_compilation") + + self.swiftscan_cache_replay_result_get_stdout = loadOptional("swiftscan_cache_replay_result_get_stdout") + self.swiftscan_cache_replay_result_get_stderr = loadOptional("swiftscan_cache_replay_result_get_stderr") + self.swiftscan_cache_replay_result_dispose = loadOptional("swiftscan_cache_replay_result_dispose") + + self.swiftscan_diagnostic_get_source_location = loadOptional("swiftscan_diagnostic_get_source_location") + self.swiftscan_source_location_get_buffer_identifier = loadOptional("swiftscan_source_location_get_buffer_identifier") + self.swiftscan_source_location_get_line_number = loadOptional("swiftscan_source_location_get_line_number") + self.swiftscan_source_location_get_column_number = loadOptional("swiftscan_source_location_get_column_number") + + self.swiftscan_module_info_get_link_libraries = loadOptional("swiftscan_module_info_get_link_libraries") + self.swiftscan_link_library_info_get_link_name = loadOptional("swiftscan_link_library_info_get_link_name") + self.swiftscan_link_library_info_get_is_framework = loadOptional("swiftscan_link_library_info_get_is_framework") + self.swiftscan_link_library_info_get_should_force_load = loadOptional("swiftscan_link_library_info_get_should_force_load") // Swift Overlay Dependencies self.swiftscan_swift_textual_detail_get_swift_overlay_dependencies = - try loadOptional("swiftscan_swift_textual_detail_get_swift_overlay_dependencies") + loadOptional("swiftscan_swift_textual_detail_get_swift_overlay_dependencies") // Header dependencies of binary modules self.swiftscan_swift_binary_detail_get_header_dependencies = - try loadOptional("swiftscan_swift_binary_detail_get_header_dependencies") + loadOptional("swiftscan_swift_binary_detail_get_header_dependencies") self.swiftscan_swift_binary_detail_get_header_dependency = - try loadOptional("swiftscan_swift_binary_detail_get_header_dependency") + loadOptional("swiftscan_swift_binary_detail_get_header_dependency") // Per-scan-query diagnostic output self.swiftscan_dependency_graph_get_diagnostics = - try loadOptional("swiftscan_dependency_graph_get_diagnostics") + loadOptional("swiftscan_dependency_graph_get_diagnostics") self.swiftscan_import_set_get_diagnostics = - try loadOptional("swiftscan_import_set_get_diagnostics") + loadOptional("swiftscan_import_set_get_diagnostics") // MARK: Required Methods func loadRequired(_ symbol: String) throws -> T { diff --git a/Sources/SwiftDriver/ToolingInterface/ToolingUtil.swift b/Sources/SwiftDriver/ToolingInterface/ToolingUtil.swift index 3459ddbba..680c14cc7 100644 --- a/Sources/SwiftDriver/ToolingInterface/ToolingUtil.swift +++ b/Sources/SwiftDriver/ToolingInterface/ToolingUtil.swift @@ -35,6 +35,20 @@ public func getSingleFrontendInvocationFromDriverArgumentsV2(driverPath: UnsafeP action: @convention(c) (CInt, UnsafePointer?>) -> Bool, diagnosticCallback: @convention(c) (CInt, UnsafePointer) -> Void, forceNoOutputs: Bool = false) -> Bool { + return getSingleFrontendInvocationFromDriverArgumentsV3(driverPath: driverPath, argListCount: argListCount, + argList: argList, action: action, + diagnosticCallback: diagnosticCallback, + compilerIntegratedTooling: true) +} + +@_cdecl("swift_getSingleFrontendInvocationFromDriverArgumentsV3") +public func getSingleFrontendInvocationFromDriverArgumentsV3(driverPath: UnsafePointer, + argListCount: CInt, + argList: UnsafePointer?>, + action: @convention(c) (CInt, UnsafePointer?>) -> Bool, + diagnosticCallback: @convention(c) (CInt, UnsafePointer) -> Void, + compilerIntegratedTooling: Bool = false, + forceNoOutputs: Bool = false) -> Bool { // Bridge the driver path argument let bridgedDriverPath = String(cString: driverPath) @@ -60,6 +74,7 @@ public func getSingleFrontendInvocationFromDriverArgumentsV2(driverPath: UnsafeP action: bridgedAction, diagnostics: &diagnostics, diagnosticCallback: bridgedDiagnosticCallback, + compilerIntegratedTooling: compilerIntegratedTooling, forceNoOutputs: forceNoOutputs) return result } @@ -72,6 +87,8 @@ public func getSingleFrontendInvocationFromDriverArgumentsV2(driverPath: UnsafeP /// \param argList The driver arguments (i.e. normal arguments for \c swiftc). /// \param diagnostics Contains the diagnostics emitted by the driver /// \param action invokes a user-provided action on the resulting frontend invocation command +/// \param compilerIntegratedTooling If true, this code is executed in the context of a +/// Swift compiler image which contains symbols normally queried from a libSwiftScan instance. /// \param forceNoOutputs If true, override the output mode to "-typecheck" and /// produce no outputs. For example, this disables "-emit-module" and "-c" and /// prevents the creation of temporary files. @@ -85,6 +102,7 @@ public func getSingleFrontendInvocationFromDriverArgumentsV2(driverPath: String, action: ([String]) -> Bool, diagnostics: inout [Diagnostic], diagnosticCallback: @escaping (CInt, String) -> Void, + compilerIntegratedTooling: Bool = false, forceNoOutputs: Bool = false) -> Bool { /// Handler for emitting diagnostics to tooling clients. let toolingDiagnosticsHandler: DiagnosticsEngine.DiagnosticsHandler = { diagnostic in @@ -148,7 +166,8 @@ public func getSingleFrontendInvocationFromDriverArgumentsV2(driverPath: String, env: ProcessEnv.vars) var driver = try Driver(args: parsedOptions.commandLine, diagnosticsOutput: .engine(diagnosticsEngine), - executor: executor) + executor: executor, + compilerIntegratedTooling: compilerIntegratedTooling) if diagnosticsEngine.hasErrors { return true } diff --git a/Tests/SwiftDriverTests/CachingBuildTests.swift b/Tests/SwiftDriverTests/CachingBuildTests.swift index 67ec92755..936b563d1 100644 --- a/Tests/SwiftDriverTests/CachingBuildTests.swift +++ b/Tests/SwiftDriverTests/CachingBuildTests.swift @@ -389,8 +389,7 @@ final class CachingBuildTests: XCTestCase { XCTAssertFalse(driver.diagnosticEngine.hasErrors) let scanLibPath = try XCTUnwrap(driver.getSwiftScanLibPath()) - try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) + try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) let cas = try dependencyOracle.getOrCreateCAS(pluginPath: nil, onDiskPath: casPath, pluginOptions: []) if let driverCAS = driver.cas { @@ -445,8 +444,7 @@ final class CachingBuildTests: XCTestCase { XCTAssertFalse(driver.diagnosticEngine.hasErrors) let scanLibPath = try XCTUnwrap(driver.getSwiftScanLibPath()) - try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) + try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) let cas = try dependencyOracle.getOrCreateCAS(pluginPath: nil, onDiskPath: casPath, pluginOptions: []) if let driverCAS = driver.cas { @@ -630,8 +628,7 @@ final class CachingBuildTests: XCTestCase { XCTAssertFalse(driver.diagnosticEngine.hasErrors) let scanLibPath = try XCTUnwrap(driver.getSwiftScanLibPath()) - try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) + try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) let cas = try dependencyOracle.getOrCreateCAS(pluginPath: nil, onDiskPath: casPath, pluginOptions: []) if let driverCAS = driver.cas { @@ -685,8 +682,7 @@ final class CachingBuildTests: XCTestCase { interModuleDependencyOracle: dependencyOracle) let scanLibPath = try XCTUnwrap(fooBuildDriver.getSwiftScanLibPath()) - try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) + try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) guard try dependencyOracle.supportsBinaryModuleHeaderDependencies() else { throw XCTSkip("libSwiftScan does not support binary module header dependencies.") } @@ -753,8 +749,7 @@ final class CachingBuildTests: XCTestCase { env: ProcessEnv.vars, interModuleDependencyOracle: dependencyOracle) let scanLibPath = try XCTUnwrap(driver.getSwiftScanLibPath()) - try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) + try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) let resolver = try ArgsResolver(fileSystem: localFileSystem) var scannerCommand = try driver.dependencyScannerInvocationCommand().1.map { try resolver.resolve($0) } // We generate full swiftc -frontend -scan-dependencies invocations in order to also be @@ -905,8 +900,7 @@ final class CachingBuildTests: XCTestCase { throw XCTSkip("frontend doesn't support prefix map") } let scanLibPath = try XCTUnwrap(driver.getSwiftScanLibPath()) - try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) + try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) let resolver = try ArgsResolver(fileSystem: localFileSystem) let scannerCommand = try driver.dependencyScannerInvocationCommand().1.map { try resolver.resolve($0) } @@ -972,8 +966,7 @@ final class CachingBuildTests: XCTestCase { XCTAssertFalse(driver.diagnosticEngine.hasErrors) let scanLibPath = try XCTUnwrap(driver.getSwiftScanLibPath()) - try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) + try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) let cas = try dependencyOracle.getOrCreateCAS(pluginPath: nil, onDiskPath: casPath, pluginOptions: []) if let driverCAS = driver.cas { @@ -1001,8 +994,7 @@ final class CachingBuildTests: XCTestCase { let casPath = path.appending(component: "cas") let driver = try Driver(args: ["swiftc", "-v"]) let scanLibPath = try XCTUnwrap(driver.getSwiftScanLibPath()) - try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) + try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) let cas = try dependencyOracle.getOrCreateCAS(pluginPath: nil, onDiskPath: casPath, pluginOptions: []) guard cas.supportsSizeManagement else { throw XCTSkip("CAS size management is not supported") diff --git a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift index 73666ffa1..9b2cc3250 100644 --- a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift +++ b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift @@ -426,8 +426,7 @@ final class ExplicitModuleBuildTests: XCTestCase { // 2. Run a dependency scan to find the just-built module let dependencyOracle = InterModuleDependencyOracle() let scanLibPath = try XCTUnwrap(toolchain.lookupSwiftScanLib()) - try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) + try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) guard try dependencyOracle.supportsLinkLibraries() else { throw XCTSkip("libSwiftScan does not support link library reporting.") } @@ -1195,8 +1194,7 @@ final class ExplicitModuleBuildTests: XCTestCase { // queries. let dependencyOracle = InterModuleDependencyOracle() let scanLibPath = try XCTUnwrap(toolchain.lookupSwiftScanLib()) - try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) + try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) try withTemporaryDirectory { path in let main = path.appending(component: "foo.swift") @@ -1372,8 +1370,7 @@ final class ExplicitModuleBuildTests: XCTestCase { // 2. Run a dependency scan to find the just-built module let dependencyOracle = InterModuleDependencyOracle() let scanLibPath = try XCTUnwrap(toolchain.lookupSwiftScanLib()) - try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) + try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) guard try dependencyOracle.supportsBinaryFrameworkDependencies() else { throw XCTSkip("libSwiftScan does not support framework binary dependency reporting.") } @@ -1417,8 +1414,7 @@ final class ExplicitModuleBuildTests: XCTestCase { // queries. let dependencyOracle = InterModuleDependencyOracle() let scanLibPath = try XCTUnwrap(toolchain.lookupSwiftScanLib()) - try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) + try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) // Create a simple test case. try withTemporaryDirectory { path in @@ -1523,8 +1519,7 @@ final class ExplicitModuleBuildTests: XCTestCase { // queries. let dependencyOracle = InterModuleDependencyOracle() let scanLibPath = try XCTUnwrap(toolchain.lookupSwiftScanLib()) - try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) + try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) guard try dependencyOracle.supportsScannerDiagnostics() else { throw XCTSkip("libSwiftScan does not support diagnostics query.") } @@ -1685,8 +1680,7 @@ final class ExplicitModuleBuildTests: XCTestCase { // queries. let dependencyOracle = InterModuleDependencyOracle() let scanLibPath = try XCTUnwrap(toolchain.lookupSwiftScanLib()) - try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) + try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) // Create a simple test case. try withTemporaryDirectory { path in @@ -1864,8 +1858,7 @@ final class ExplicitModuleBuildTests: XCTestCase { // queries. let dependencyOracle = InterModuleDependencyOracle() let scanLibPath = try XCTUnwrap(toolchain.lookupSwiftScanLib()) - try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) + try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) if !(try dependencyOracle.supportsPerScanDiagnostics()) { throw XCTSkip("Scanner does not support per-scan diagnostics") } @@ -2055,8 +2048,7 @@ final class ExplicitModuleBuildTests: XCTestCase { let (stdlibPath, shimsPath, toolchain, _) = try getDriverArtifactsForScanning() let dependencyOracle = InterModuleDependencyOracle() let scanLibPath = try XCTUnwrap(toolchain.lookupSwiftScanLib()) - try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) + try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) // Create a simple test case. try withTemporaryDirectory { path in let main = path.appending(component: "testDependencyScanning.swift") @@ -2157,8 +2149,7 @@ final class ExplicitModuleBuildTests: XCTestCase { let scanLibPath = try XCTUnwrap(toolchain.lookupSwiftScanLib()) // Run the first scan and serialize the cache contents. let firstDependencyOracle = InterModuleDependencyOracle() - try firstDependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) + try firstDependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) var firstScanDiagnostics: [ScannerDiagnosticPayload] = [] let firstScanGraph = try firstDependencyOracle.getDependencies(workingDirectory: path, @@ -2168,8 +2159,7 @@ final class ExplicitModuleBuildTests: XCTestCase { // Run the second scan, re-using the serialized cache contents. let secondDependencyOracle = InterModuleDependencyOracle() - try secondDependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem, - swiftScanLibPath: scanLibPath) + try secondDependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) XCTAssertFalse(secondDependencyOracle.loadScannerCache(from: cacheSavePath)) var secondScanDiagnostics: [ScannerDiagnosticPayload] = [] let secondScanGraph = diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index e58237ed4..e8df41841 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -5419,9 +5419,11 @@ final class SwiftDriverTests: XCTestCase { if localFileSystem.exists(swiftScanLibPath) { let libSwiftScanInstance = try SwiftScan(dylib: swiftScanLibPath) if libSwiftScanInstance.canQueryTargetInfo() { - XCTAssertTrue(try driver.verifyBeingAbleToQueryTargetInfoInProcess(workingDirectory: localFileSystem.currentWorkingDirectory, - invocationCommand: printTargetInfoCommand, - expectedSDKPath: "/bar")) + let _ = try Driver.queryTargetInfoInProcess(libSwiftScanInstance: libSwiftScanInstance, + toolchain: driver.toolchain, + fileSystem: localFileSystem, + workingDirectory: localFileSystem.currentWorkingDirectory, + invocationCommand: printTargetInfoCommand) } } } @@ -5439,9 +5441,11 @@ final class SwiftDriverTests: XCTestCase { if localFileSystem.exists(swiftScanLibPath) { let libSwiftScanInstance = try SwiftScan(dylib: swiftScanLibPath) if libSwiftScanInstance.canQueryTargetInfo() { - XCTAssertTrue(try driver.verifyBeingAbleToQueryTargetInfoInProcess(workingDirectory: localFileSystem.currentWorkingDirectory, - invocationCommand: printTargetInfoCommand, - expectedSDKPath: "/tmp/foo bar")) + let _ = try Driver.queryTargetInfoInProcess(libSwiftScanInstance: libSwiftScanInstance, + toolchain: driver.toolchain, + fileSystem: localFileSystem, + workingDirectory: localFileSystem.currentWorkingDirectory, + invocationCommand: printTargetInfoCommand) } } } @@ -5475,9 +5479,9 @@ final class SwiftDriverTests: XCTestCase { env: hideSwiftScanEnv, executor: MockExecutor(resolver: ArgsResolver(fileSystem: InMemoryFileSystem())))) { error in - if case .unableToDecodeFrontendTargetInfo = error as? Driver.Error {} + if case .decodingError = error as? JobExecutionError {} else { - XCTFail("not a decoding error") + XCTFail("not a decoding error: \(error)") } } } @@ -5486,15 +5490,9 @@ final class SwiftDriverTests: XCTestCase { XCTAssertThrowsError(try Driver(args: ["swift", "-print-target-info"], env: ["SWIFT_DRIVER_SWIFT_FRONTEND_EXEC": "/bad/path/to/swift-frontend"])) { error in - XCTAssertTrue(error is Driver.Error) - - switch error { - case Driver.Error.failedToRetrieveFrontendTargetInfo, - Driver.Error.failedToRunFrontendToRetrieveTargetInfo: - break; - - default: - XCTFail("unexpected error \(error)") + if case .posix_spawn = error as? TSCBasic.SystemError {} + else { + XCTFail("unexpected error: \(error)") } } } diff --git a/Tests/TestUtilities/DriverExtensions.swift b/Tests/TestUtilities/DriverExtensions.swift index 81de3fbb2..756353420 100644 --- a/Tests/TestUtilities/DriverExtensions.swift +++ b/Tests/TestUtilities/DriverExtensions.swift @@ -44,27 +44,6 @@ extension Driver { public static func sdkArgumentsForTesting() throws -> [String]? { try cachedSDKPath.map {["-sdk", try $0.get()]} } - - - public func verifyBeingAbleToQueryTargetInfoInProcess(workingDirectory: AbsolutePath?, - invocationCommand: [String], - expectedSDKPath: String) throws -> Bool { - guard let targetInfo = try Self.queryTargetInfoInProcess(of: toolchain, - fileSystem: fileSystem, - workingDirectory: workingDirectory, - invocationCommand: invocationCommand) else { - return false - } - - guard let sdkPath = targetInfo.sdkPath else { - return false - } - - if sdkPath.path.description != expectedSDKPath { - return false - } - return true - } } /// Set to nil if cannot perform on this host diff --git a/Tests/ToolingTestShim/CToolingTestShimImpl.c b/Tests/ToolingTestShim/CToolingTestShimImpl.c index 0ca224604..9dd8c2f60 100644 --- a/Tests/ToolingTestShim/CToolingTestShimImpl.c +++ b/Tests/ToolingTestShim/CToolingTestShimImpl.c @@ -1,7 +1,7 @@ #include "include/tooling_shim.h" -bool swift_getSingleFrontendInvocationFromDriverArgumentsV2(const char *, int, const char**, bool(int, const char**), - void(swiftdriver_tooling_diagnostic_kind, const char*), bool); +bool swift_getSingleFrontendInvocationFromDriverArgumentsV3(const char *, int, const char**, bool(int, const char**), + void(swiftdriver_tooling_diagnostic_kind, const char*), bool, bool); bool getSingleFrontendInvocationFromDriverArgumentsTest(const char *driverPath, int argListCount, const char** argList, @@ -9,6 +9,6 @@ bool getSingleFrontendInvocationFromDriverArgumentsTest(const char *driverPath, void diagnosticCallback(swiftdriver_tooling_diagnostic_kind diagnosticKind, const char* message), bool forceNoOutputs) { - return swift_getSingleFrontendInvocationFromDriverArgumentsV2(driverPath, argListCount, argList, - action, diagnosticCallback, forceNoOutputs); + return swift_getSingleFrontendInvocationFromDriverArgumentsV3(driverPath, argListCount, argList, + action, diagnosticCallback, false, forceNoOutputs); }