Skip to content

Commit b9388b0

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 80f0ecc commit b9388b0

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
@@ -87,23 +87,16 @@ internal extension Driver {
8787
contents)
8888
}
8989

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

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

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

Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift

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

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

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

0 commit comments

Comments
 (0)