Skip to content

Fallback on swift-frontend -scan-dependencies ... fallback path in case libSwiftScan shared library cannot be loaded. #1601

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 13, 2024
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 {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cachemeifyoucan should we error if caching is enabled but we failed to load libSwiftScan?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error out sounds good to me.

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 @@ -1517,6 +1517,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