Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,14 @@ import class Foundation.JSONDecoder
import var Foundation.EXIT_SUCCESS

extension Diagnostic.Message {
static func warn_scanner_frontend_fallback() -> Diagnostic.Message {
.warning("Fallback to `swift-frontend` dependency scanner invocation")
static func warn_scan_dylib_not_found() -> Diagnostic.Message {
.warning("Unable to locate libSwiftScan. Fallback to `swift-frontend` dependency scanner invocation.")
}
static func warn_scan_dylib_load_failed(_ libPath: String) -> Diagnostic.Message {
.warning("In-process dependency scan query failed due to incompatible libSwiftScan (\(libPath)). Fallback to `swift-frontend` dependency scanner invocation. Specify '-nonlib-dependency-scanner' to silence this warning.")
}
static func error_caching_enabled_libswiftscan_load_failure(_ libPath: String) -> Diagnostic.Message {
.error("Swift Caching enabled - libSwiftScan load failed (\(libPath)).")
}
static func scanner_diagnostic_error(_ message: String) -> Diagnostic.Message {
.error(message)
Expand Down Expand Up @@ -156,27 +162,35 @@ public extension Driver {

/// Returns false if the lib is available and ready to use
private mutating func initSwiftScanLib() throws -> Bool {
// If `-nonlib-dependency-scanner` was specified or the libSwiftScan library cannot be found,
// `-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 !parsedOptions.hasArgument(.driverScanDependenciesNonLib),
let scanLibPath = try toolchain.lookupSwiftScanLib(),
guard let scanLibPath = try toolchain.lookupSwiftScanLib(),
fileSystem.exists(scanLibPath) else {
// This warning is mostly useful for debugging the driver, so let's hide it
// when libSwiftDriver is used, instead of a swift-driver executable.
if !integratedDriver {
diagnosticEngine.emit(.warn_scanner_frontend_fallback())
}

diagnosticEngine.emit(.warn_scan_dylib_not_found())
return true
}

try interModuleDependencyOracle.verifyOrCreateScannerInstance(fileSystem: fileSystem,
swiftScanLibPath: scanLibPath)
if isCachingEnabled {
self.cas = try interModuleDependencyOracle.getOrCreateCAS(pluginPath: try getCASPluginPath(),
onDiskPath: try getOnDiskCASPath(),
pluginOptions: try getCASPluginOptions())
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
}
Expand Down Expand Up @@ -257,7 +271,7 @@ public extension Driver {
}
}

mutating internal func performDependencyScan() throws -> InterModuleDependencyGraph {
mutating func performDependencyScan() throws -> InterModuleDependencyGraph {
let scannerJob = try dependencyScanningJob()
let forceResponseFiles = parsedOptions.hasArgument(.driverForceResponseFiles)
let dependencyGraph: InterModuleDependencyGraph
Expand Down
59 changes: 59 additions & 0 deletions Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1585,6 +1585,65 @@ final class ExplicitModuleBuildTests: XCTestCase {
}
}

// Ensure dependency scanning succeeds via fallback `swift-frontend -scan-dependenceis`
// mechanism if libSwiftScan.dylib fails to load.
func testDependencyScanningFallback() throws {
let (stdlibPath, shimsPath, _, _) = try getDriverArtifactsForScanning()

// Create a simple test case.
try withTemporaryDirectory { path in
let main = path.appending(component: "testDependencyScanningFallback.swift")
try localFileSystem.writeFileContents(main, bytes: "import C;")

let dummyBrokenDylib = path.appending(component: "lib_InternalSwiftScan.dylib")
try localFileSystem.writeFileContents(dummyBrokenDylib, bytes: "n/a")

var environment = ProcessEnv.vars
environment["SWIFT_DRIVER_SWIFTSCAN_LIB"] = dummyBrokenDylib.nativePathString(escaped: true)

let cHeadersPath: AbsolutePath =
try testInputsPath.appending(component: "ExplicitModuleBuilds")
.appending(component: "CHeaders")
let swiftModuleInterfacesPath: AbsolutePath =
try testInputsPath.appending(component: "ExplicitModuleBuilds")
.appending(component: "Swift")
let sdkArgumentsForTesting: [String] = (try? Driver.sdkArgumentsForTesting()) ?? []
let driverArgs: [String] = ["swiftc",
"-I", cHeadersPath.nativePathString(escaped: true),
"-I", swiftModuleInterfacesPath.nativePathString(escaped: true),
"-I", stdlibPath.nativePathString(escaped: true),
"-I", shimsPath.nativePathString(escaped: true),
"/tmp/Foo.o",
"-explicit-module-build",
"-working-directory", path.nativePathString(escaped: true),
"-disable-clang-target",
main.nativePathString(escaped: true)] + sdkArgumentsForTesting
do {
var driver = try Driver(args: driverArgs, env: environment)
let interModuleDependencyGraph = try driver.performDependencyScan()
XCTAssertTrue(driver.diagnosticEngine.diagnostics.contains { $0.behavior == .warning &&
$0.message.text == "In-process dependency scan query failed due to incompatible libSwiftScan (\(dummyBrokenDylib.nativePathString(escaped: true))). Fallback to `swift-frontend` dependency scanner invocation. Specify '-nonlib-dependency-scanner' to silence this warning."})
XCTAssertTrue(interModuleDependencyGraph.mainModule.directDependencies?.contains(where: { $0.moduleName == "C" }))
}

// Ensure no warning is emitted with '-nonlib-dependency-scanner'
do {
var driver = try Driver(args: driverArgs + ["-nonlib-dependency-scanner"], env: environment)
let _ = try driver.performDependencyScan()
XCTAssertFalse(driver.diagnosticEngine.diagnostics.contains { $0.behavior == .warning &&
$0.message.text == "In-process dependency scan query failed due to incompatible libSwiftScan (\(dummyBrokenDylib.nativePathString(escaped: true))). Fallback to `swift-frontend` dependency scanner invocation. Specify '-nonlib-dependency-scanner' to silence this warning."})
}

// Ensure error is emitted when caching is enabled
do {
var driver = try Driver(args: driverArgs + ["-cache-compile-job"], env: environment)
let _ = try driver.performDependencyScan()
XCTAssertFalse(driver.diagnosticEngine.diagnostics.contains { $0.behavior == .error &&
$0.message.text == "Swift Caching enabled - libSwiftScan load failed (\(dummyBrokenDylib.nativePathString(escaped: true))."})
}
}
}

func testParallelDependencyScanningDiagnostics() throws {
let (stdlibPath, shimsPath, toolchain, _) = try getDriverArtifactsForScanning()
// The dependency oracle wraps an instance of libSwiftScan and ensures thread safety across
Expand Down