Skip to content

Commit 16cb2c5

Browse files
committed
Add capability to query '-print-target-info' in-process using libSwiftScan API
1 parent dd6bbf7 commit 16cb2c5

File tree

7 files changed

+128
-17
lines changed

7 files changed

+128
-17
lines changed

Sources/CSwiftScan/include/swiftscan_header.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@ typedef struct {
207207
void
208208
(*swiftscan_scan_invocation_dispose)(swiftscan_scan_invocation_t);
209209

210+
//=== Target Info Functions-------- ---------------------------------------===//
211+
swiftscan_string_ref_t
212+
(*swiftscan_compiler_target_info_query)(swiftscan_scan_invocation_t);
213+
210214
//=== Functionality Query Functions ---------------------------------------===//
211215
swiftscan_string_set_t *
212216
(*swiftscan_compiler_supported_arguments_query)(void);

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@ public struct Driver {
528528
self.hostTriple =
529529
try Self.computeHostTriple(&self.parsedOptions, diagnosticsEngine: diagnosticEngine,
530530
toolchain: self.toolchain, executor: self.executor,
531+
fileSystem: fileSystem,
531532
swiftCompilerPrefixArgs: self.swiftCompilerPrefixArgs)
532533

533534
// Classify and collect all of the input files.
@@ -2896,6 +2897,7 @@ extension Driver {
28962897
diagnosticsEngine: DiagnosticsEngine,
28972898
toolchain: Toolchain,
28982899
executor: DriverExecutor,
2900+
fileSystem: FileSystem,
28992901
swiftCompilerPrefixArgs: [String]) throws -> Triple {
29002902

29012903
let frontendOverride = try FrontendOverride(&parsedOptions, diagnosticsEngine)

Sources/SwiftDriver/Execution/DriverExecutor.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ extension DriverExecutor {
8787
capturingJSONOutputAs outputType: T.Type,
8888
forceResponseFiles: Bool,
8989
recordedInputModificationDates: [TypedVirtualPath: TimePoint]) throws -> T {
90+
//print(">>> EXEC: \(try self.description(of: job, forceResponseFiles: false))")
9091
let result = try execute(job: job,
9192
forceResponseFiles: forceResponseFiles,
9293
recordedInputModificationDates: recordedInputModificationDates)

Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ public extension Driver {
236236
return fallbackToFrontend
237237
}
238238

239-
private func sanitizeCommandForLibScanInvocation(_ command: inout [String]) {
239+
static func sanitizeCommandForLibScanInvocation(_ command: inout [String]) {
240240
// Remove the tool executable to only leave the arguments. When passing the
241241
// command line into libSwiftScan, the library is itself the tool and only
242242
// needs to parse the remaining arguments.
@@ -257,10 +257,10 @@ public extension Driver {
257257
let isSwiftScanLibAvailable = !(try initSwiftScanLib())
258258
if isSwiftScanLibAvailable {
259259
let cwd = workingDirectory ?? fileSystem.currentWorkingDirectory!
260-
var command = try itemizedJobCommand(of: preScanJob,
261-
useResponseFiles: .disabled,
262-
using: executor.resolver)
263-
sanitizeCommandForLibScanInvocation(&command)
260+
var command = try Self.itemizedJobCommand(of: preScanJob,
261+
useResponseFiles: .disabled,
262+
using: executor.resolver)
263+
Self.sanitizeCommandForLibScanInvocation(&command)
264264
imports =
265265
try interModuleDependencyOracle.getImports(workingDirectory: cwd,
266266
moduleAliases: moduleOutputInfo.aliases,
@@ -293,10 +293,10 @@ public extension Driver {
293293
let isSwiftScanLibAvailable = !(try initSwiftScanLib())
294294
if isSwiftScanLibAvailable {
295295
let cwd = workingDirectory ?? fileSystem.currentWorkingDirectory!
296-
var command = try itemizedJobCommand(of: scannerJob,
297-
useResponseFiles: .disabled,
298-
using: executor.resolver)
299-
sanitizeCommandForLibScanInvocation(&command)
296+
var command = try Self.itemizedJobCommand(of: scannerJob,
297+
useResponseFiles: .disabled,
298+
using: executor.resolver)
299+
Self.sanitizeCommandForLibScanInvocation(&command)
300300
dependencyGraph =
301301
try interModuleDependencyOracle.getDependencies(workingDirectory: cwd,
302302
moduleAliases: moduleOutputInfo.aliases,
@@ -339,10 +339,10 @@ public extension Driver {
339339
let isSwiftScanLibAvailable = !(try initSwiftScanLib())
340340
if isSwiftScanLibAvailable {
341341
let cwd = workingDirectory ?? fileSystem.currentWorkingDirectory!
342-
var command = try itemizedJobCommand(of: batchScanningJob,
343-
useResponseFiles: .disabled,
344-
using: executor.resolver)
345-
sanitizeCommandForLibScanInvocation(&command)
342+
var command = try Self.itemizedJobCommand(of: batchScanningJob,
343+
useResponseFiles: .disabled,
344+
using: executor.resolver)
345+
Self.sanitizeCommandForLibScanInvocation(&command)
346346
moduleVersionedGraphMap =
347347
try interModuleDependencyOracle.getBatchDependencies(workingDirectory: cwd,
348348
moduleAliases: moduleOutputInfo.aliases,
@@ -485,8 +485,8 @@ public extension Driver {
485485
contents)
486486
}
487487

488-
fileprivate func itemizedJobCommand(of job: Job, useResponseFiles: ResponseFileHandling,
489-
using resolver: ArgsResolver) throws -> [String] {
488+
static func itemizedJobCommand(of job: Job, useResponseFiles: ResponseFileHandling,
489+
using resolver: ArgsResolver) throws -> [String] {
490490
let (args, _) = try resolver.resolveArgumentList(for: job,
491491
useResponseFiles: useResponseFiles)
492492
return args

Sources/SwiftDriver/Jobs/PrintTargetInfoJob.swift

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
import protocol TSCBasic.FileSystem
14+
import class Foundation.JSONDecoder
15+
1316
/// Swift versions are major.minor.
1417
struct SwiftVersion {
1518
var major: Int
@@ -173,3 +176,67 @@ extension Toolchain {
173176
)
174177
}
175178
}
179+
180+
extension Driver {
181+
static func queryTargetInfoInProcess(of toolchain: Toolchain,
182+
fileSystem: FileSystem,
183+
invocationCommand: [String]) throws -> FrontendTargetInfo? {
184+
let optionalSwiftScanLibPath = try toolchain.lookupSwiftScanLib()
185+
if let swiftScanLibPath = optionalSwiftScanLibPath,
186+
fileSystem.exists(swiftScanLibPath) {
187+
let libSwiftScanInstance = try SwiftScan(dylib: swiftScanLibPath)
188+
if libSwiftScanInstance.canQueryTargetInfo() {
189+
let targetInfoData = try libSwiftScanInstance.queryTargetInfoJSON(invocationCommand: invocationCommand)
190+
return try JSONDecoder().decode(FrontendTargetInfo.self, from: targetInfoData)
191+
}
192+
}
193+
return nil
194+
}
195+
196+
static func computeTargetInfo(target: Triple?,
197+
targetVariant: Triple?,
198+
sdkPath: VirtualPath? = nil,
199+
resourceDirPath: VirtualPath? = nil,
200+
runtimeCompatibilityVersion: String? = nil,
201+
requiresInPlaceExecution: Bool = false,
202+
useStaticResourceDir: Bool = false,
203+
swiftCompilerPrefixArgs: [String],
204+
toolchain: Toolchain,
205+
fileSystem: FileSystem,
206+
executor: DriverExecutor) throws -> FrontendTargetInfo {
207+
let frontendTargetInfoJob =
208+
try toolchain.printTargetInfoJob(target: target, targetVariant: targetVariant,
209+
sdkPath: sdkPath, resourceDirPath: resourceDirPath,
210+
runtimeCompatibilityVersion: runtimeCompatibilityVersion,
211+
requiresInPlaceExecution: requiresInPlaceExecution,
212+
useStaticResourceDir: useStaticResourceDir,
213+
swiftCompilerPrefixArgs: swiftCompilerPrefixArgs)
214+
var command = try Self.itemizedJobCommand(of: frontendTargetInfoJob,
215+
useResponseFiles: .disabled,
216+
using: executor.resolver)
217+
Self.sanitizeCommandForLibScanInvocation(&command)
218+
if let targetInfo =
219+
try Self.queryTargetInfoInProcess(of: toolchain, fileSystem: fileSystem,
220+
invocationCommand: command) {
221+
return targetInfo
222+
}
223+
224+
// Fallback: Invoke `swift-frontend -print-target-info` and decode the output
225+
return try executor.execute(
226+
job: frontendTargetInfoJob,
227+
capturingJSONOutputAs: FrontendTargetInfo.self,
228+
forceResponseFiles: false,
229+
recordedInputModificationDates: [:])
230+
}
231+
232+
/// This method exists for testing purposes only
233+
@_spi(Testing) public func verifyBeingAbleToQueryTargetInfoInProcess(invocationCommand: [String]) throws -> Bool {
234+
guard let targetInfo = try Self.queryTargetInfoInProcess(of: toolchain,
235+
fileSystem: fileSystem,
236+
invocationCommand: invocationCommand) else {
237+
return false
238+
}
239+
print("libSwiftScan Compiler: \(targetInfo.compilerVersion)")
240+
return true
241+
}
242+
}

Sources/SwiftDriver/SwiftScan/SwiftScan.swift

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
import func Foundation.strdup
1616
import func Foundation.free
17+
import class Foundation.JSONDecoder
18+
import struct Foundation.Data
1719

1820
import protocol TSCBasic.DiagnosticData
1921
import struct TSCBasic.AbsolutePath
@@ -80,7 +82,7 @@ internal extension swiftscan_diagnostic_severity_t {
8082
}
8183

8284
/// Wrapper for libSwiftScan, taking care of initialization, shutdown, and dispatching dependency scanning queries.
83-
internal final class SwiftScan {
85+
@_spi(Testing) public final class SwiftScan {
8486
/// The path to the libSwiftScan dylib.
8587
let path: AbsolutePath
8688

@@ -311,6 +313,25 @@ internal final class SwiftScan {
311313
throw DependencyScanningError.argumentQueryFailed
312314
}
313315
}
316+
317+
@_spi(Testing) public func canQueryTargetInfo() -> Bool {
318+
return api.swiftscan_compiler_target_info_query != nil &&
319+
api.swiftscan_string_set_dispose != nil
320+
}
321+
322+
func queryTargetInfoJSON(invocationCommand: [String]) throws -> Data {
323+
// Create and configure the scanner invocation
324+
let invocation = api.swiftscan_scan_invocation_create()
325+
defer { api.swiftscan_scan_invocation_dispose(invocation) }
326+
withArrayOfCStrings(invocationCommand) { invocationStringArray in
327+
api.swiftscan_scan_invocation_set_argv(invocation,
328+
Int32(invocationCommand.count),
329+
invocationStringArray)
330+
}
331+
let targetInfoString = try toSwiftString(api.swiftscan_compiler_target_info_query(invocation))
332+
let targetInfoData = Data(targetInfoString.utf8)
333+
return targetInfoData
334+
}
314335
}
315336

316337
// Used for testing purposes only
@@ -347,6 +368,10 @@ private extension swiftscan_functions_t {
347368
self.swiftscan_compiler_supported_features_query =
348369
try loadOptional("swiftscan_compiler_supported_features_query")
349370

371+
// Target Info query
372+
self.swiftscan_compiler_target_info_query =
373+
try loadOptional("swiftscan_compiler_target_info_query")
374+
350375
// Dependency scanner serialization/deserialization features
351376
self.swiftscan_scanner_cache_serialize =
352377
try loadOptional("swiftscan_scanner_cache_serialize")

Tests/SwiftDriverTests/SwiftDriverTests.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4770,7 +4770,7 @@ final class SwiftDriverTests: XCTestCase {
47704770

47714771
func testPrintTargetInfo() throws {
47724772
do {
4773-
var driver = try Driver(args: ["swift", "-print-target-info", "-target", "arm64-apple-ios12.0", "-sdk", "bar", "-resource-dir", "baz"])
4773+
var driver = try Driver(args: ["swift", "-print-target-info", "-sdk", "bar", "-resource-dir", "baz"])
47744774
let plannedJobs = try driver.planBuild()
47754775
XCTAssertTrue(plannedJobs.count == 1)
47764776
let job = plannedJobs[0]
@@ -4781,6 +4781,18 @@ final class SwiftDriverTests: XCTestCase {
47814781
XCTAssertTrue(job.commandLine.contains(.flag("-resource-dir")))
47824782
}
47834783

4784+
do {
4785+
let targetInfoArgs = ["-print-target-info", "-sdk", "bar", "-resource-dir", "baz"]
4786+
let driver = try Driver(args: ["swift"] + targetInfoArgs)
4787+
let swiftScanLibPath = try XCTUnwrap(driver.toolchain.lookupSwiftScanLib())
4788+
if localFileSystem.exists(swiftScanLibPath) {
4789+
let libSwiftScanInstance = try SwiftScan(dylib: swiftScanLibPath)
4790+
if libSwiftScanInstance.canQueryTargetInfo() {
4791+
XCTAssertTrue(try driver.verifyBeingAbleToQueryTargetInfoInProcess(invocationCommand: targetInfoArgs))
4792+
}
4793+
}
4794+
}
4795+
47844796
do {
47854797
struct MockExecutor: DriverExecutor {
47864798
let resolver: ArgsResolver

0 commit comments

Comments
 (0)