Skip to content

Commit 88039a1

Browse files
committed
[Dependency Scanning] Do not dlopen the scanning library when not used.
And add an environment variable to overload the toolchain used to locate the swiftScan dylib
1 parent e0748bf commit 88039a1

File tree

4 files changed

+72
-47
lines changed

4 files changed

+72
-47
lines changed

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -422,13 +422,7 @@ public struct Driver {
422422
if let dependencyOracle = interModuleDependencyOracle {
423423
self.interModuleDependencyOracle = dependencyOracle
424424
} else {
425-
// For now, we expect to find the library in the same toolchain as the compiler itself
426-
let toolchainRootPath: AbsolutePath = try toolchain.getToolPath(.swiftCompiler)
427-
.parentDirectory // bin
428-
.parentDirectory // toolchain root
429-
self.interModuleDependencyOracle =
430-
try InterModuleDependencyOracle(fileSystem: fileSystem,
431-
toolchainPath: toolchainRootPath)
425+
self.interModuleDependencyOracle = InterModuleDependencyOracle()
432426

433427
// This is a shim for backwards-compatibility with ModuleInfoMap-based API
434428
// used by SwiftPM

Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,46 +28,61 @@ import Foundation
2828
/// An abstraction of a cache and query-engine of inter-module dependencies
2929
public class InterModuleDependencyOracle {
3030
/// Allow external clients to instantiate the oracle
31-
public init(fileSystem: FileSystem,
32-
toolchainPath: AbsolutePath) throws {
33-
guard fileSystem.exists(toolchainPath) else {
34-
fatalError("Path to specified toolchain does not exist: \(toolchainPath.description)")
35-
}
36-
37-
let swiftScanLibPath = toolchainPath.appending(component: "lib")
38-
.appending(component: "lib_InternalSwiftScan.dylib")
39-
guard fileSystem.exists(toolchainPath) else {
40-
fatalError("Could not find libSwiftScan at: \(swiftScanLibPath.description)")
41-
}
42-
43-
swiftScanLibInstance = try SwiftScan(dylib: swiftScanLibPath)
44-
}
31+
public init() {}
4532

4633
@_spi(Testing) public func getDependencies(workingDirectory: AbsolutePath,
4734
commandLine: [String])
4835
throws -> InterModuleDependencyGraph {
49-
try queue.sync {
50-
return try swiftScanLibInstance.scanDependencies(workingDirectory: workingDirectory,
36+
precondition(hasScannerInstance)
37+
return try queue.sync {
38+
return try swiftScanLibInstance!.scanDependencies(workingDirectory: workingDirectory,
5139
invocationCommand: commandLine)
5240
}
5341
}
5442

55-
func getBatchDependencies(workingDirectory: AbsolutePath,
56-
commandLine: [String],
57-
batchInfos: [BatchScanModuleInfo])
43+
@_spi(Testing) public func getBatchDependencies(workingDirectory: AbsolutePath,
44+
commandLine: [String],
45+
batchInfos: [BatchScanModuleInfo])
5846
throws -> [ModuleDependencyId: [InterModuleDependencyGraph]] {
59-
try queue.sync {
60-
return try swiftScanLibInstance.batchScanDependencies(workingDirectory: workingDirectory,
47+
precondition(hasScannerInstance)
48+
return try queue.sync {
49+
return try swiftScanLibInstance!.batchScanDependencies(workingDirectory: workingDirectory,
6150
invocationCommand: commandLine,
6251
batchInfos: batchInfos)
6352
}
6453
}
6554

55+
/// Given a specified toolchain path, locate and instantiate an instance of the SwiftScan library
56+
@_spi(Testing) public func verifyOrCreateScannerInstance(fileSystem: FileSystem,
57+
toolchainPath: AbsolutePath) throws {
58+
try queue.sync {
59+
if swiftScanLibInstance == nil {
60+
guard fileSystem.exists(toolchainPath) else {
61+
fatalError("Path to specified toolchain does not exist: \(toolchainPath.description)")
62+
}
63+
64+
let swiftScanLibPath = toolchainPath.appending(component: "lib")
65+
.appending(component: "lib_InternalSwiftScan.dylib")
66+
guard fileSystem.exists(toolchainPath) else {
67+
fatalError("Could not find libSwiftScan at: \(swiftScanLibPath.description)")
68+
}
69+
70+
swiftScanLibInstance = try SwiftScan(dylib: swiftScanLibPath)
71+
} else {
72+
let swiftScanLibPath = toolchainPath.appending(component: "lib")
73+
.appending(component: "lib_InternalSwiftScan.dylib")
74+
assert(swiftScanLibInstance!.path == swiftScanLibPath)
75+
}
76+
}
77+
}
78+
79+
private var hasScannerInstance: Bool { self.swiftScanLibInstance != nil }
80+
6681
/// Queue to sunchronize accesses to the scanner
6782
internal let queue = DispatchQueue(label: "org.swift.swift-driver.swift-scan")
6883

6984
/// A reference to an instance of the compiler's libSwiftScan shared library
70-
private let swiftScanLibInstance: SwiftScan
85+
private var swiftScanLibInstance: SwiftScan? = nil
7186

7287
// The below API is a legacy implementation of the oracle that is in-place to allow clients to
7388
// transition to the new API. It is to be removed once that transition is complete.

Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -88,23 +88,16 @@ internal extension Driver {
8888
contents)
8989
}
9090

91-
fileprivate func itemizedJobCommand(of job: Job, forceResponseFiles: Bool,
92-
using resolver: ArgsResolver) throws -> [String] {
93-
let (args, _) = try resolver.resolveArgumentList(for: job,
94-
forceResponseFiles: forceResponseFiles,
95-
quotePaths: true)
96-
return args
97-
}
98-
9991
mutating func performDependencyScan() throws -> InterModuleDependencyGraph {
10092
let scannerJob = try dependencyScanningJob()
10193
let forceResponseFiles = parsedOptions.hasArgument(.driverForceResponseFiles)
102-
let cwd = workingDirectory ?? fileSystem.currentWorkingDirectory!
103-
10494
let dependencyGraph: InterModuleDependencyGraph
10595

106-
if (parsedOptions.hasArgument(.driverScanDependenciesNonLib)) {
107-
96+
if (!parsedOptions.hasArgument(.driverScanDependenciesNonLib)) {
97+
try interModuleDependencyOracle.verifyOrCreateScannerInstance(fileSystem: fileSystem,
98+
toolchainPath:
99+
try getRootPath(of: toolchain))
100+
let cwd = workingDirectory ?? fileSystem.currentWorkingDirectory!
108101
var command = try itemizedJobCommand(of: scannerJob,
109102
forceResponseFiles: forceResponseFiles,
110103
using: executor.resolver)
@@ -134,6 +127,9 @@ internal extension Driver {
134127

135128
let moduleVersionedGraphMap: [ModuleDependencyId: [InterModuleDependencyGraph]]
136129
if (!parsedOptions.hasArgument(.driverScanDependenciesNonLib)) {
130+
try interModuleDependencyOracle.verifyOrCreateScannerInstance(fileSystem: fileSystem,
131+
toolchainPath:
132+
try getRootPath(of: toolchain))
137133
let cwd = workingDirectory ?? fileSystem.currentWorkingDirectory!
138134
var command = try itemizedJobCommand(of: batchScanningJob,
139135
forceResponseFiles: forceResponseFiles,
@@ -255,4 +251,21 @@ internal extension Driver {
255251
return .temporaryWithKnownContents(.init("\(moduleOutputInfo.name)-batch-module-scan.json"),
256252
contents)
257253
}
254+
255+
fileprivate func itemizedJobCommand(of job: Job, forceResponseFiles: Bool,
256+
using resolver: ArgsResolver) throws -> [String] {
257+
let (args, _) = try resolver.resolveArgumentList(for: job,
258+
forceResponseFiles: forceResponseFiles,
259+
quotePaths: true)
260+
return args
261+
}
262+
263+
fileprivate func getRootPath(of toolchain: Toolchain) throws -> AbsolutePath {
264+
if let overrideString = env["SWIFT_DRIVER_SWIFT_SCAN_TOOLCHAIN_PATH"] {
265+
return try AbsolutePath(validating: overrideString)
266+
}
267+
return try toolchain.getToolPath(.swiftCompiler)
268+
.parentDirectory // bin
269+
.parentDirectory // toolchain root
270+
}
258271
}

Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,9 @@ final class ExplicitModuleBuildTests: XCTestCase {
163163
let toolchainRootPath: AbsolutePath = try driver.toolchain.getToolPath(.swiftCompiler)
164164
.parentDirectory // bin
165165
.parentDirectory // toolchain root
166-
let dependencyOracle = try InterModuleDependencyOracle(fileSystem: localFileSystem,
167-
toolchainPath: toolchainRootPath)
166+
let dependencyOracle = InterModuleDependencyOracle()
167+
try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem,
168+
toolchainPath: toolchainRootPath)
168169
try dependencyOracle.mergeModules(from: moduleDependencyGraph)
169170
driver.explicitDependencyBuildPlanner =
170171
try ExplicitDependencyBuildPlanner(dependencyGraph: moduleDependencyGraph,
@@ -223,8 +224,9 @@ final class ExplicitModuleBuildTests: XCTestCase {
223224
let toolchainRootPath: AbsolutePath = try toolchain.getToolPath(.swiftCompiler)
224225
.parentDirectory // bin
225226
.parentDirectory // toolchain root
226-
let dependencyOracle = try InterModuleDependencyOracle(fileSystem: localFileSystem,
227-
toolchainPath: toolchainRootPath)
227+
let dependencyOracle = InterModuleDependencyOracle()
228+
try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem,
229+
toolchainPath: toolchainRootPath)
228230
try dependencyOracle.mergeModules(from: inputDependencyGraph)
229231

230232
// Construct a module dependency graph that will contain .swiftPlaceholder("B"),
@@ -520,8 +522,9 @@ final class ExplicitModuleBuildTests: XCTestCase {
520522
.appending(component: "shims")
521523
// The dependency oracle wraps an instance of libSwiftScan and ensures thread safety across
522524
// queries.
523-
let dependencyOracle = try InterModuleDependencyOracle(fileSystem: localFileSystem,
524-
toolchainPath: toolchainRootPath)
525+
let dependencyOracle = InterModuleDependencyOracle()
526+
try dependencyOracle.verifyOrCreateScannerInstance(fileSystem: localFileSystem,
527+
toolchainPath: toolchainRootPath)
525528

526529
// Create a simple test case.
527530
try withTemporaryDirectory { path in

0 commit comments

Comments
 (0)