Skip to content

Commit

Permalink
[Dependency Scanning] Add support for Swift Overlay dependencies as a…
Browse files Browse the repository at this point in the history
… separate dependency details field

Instead of assuming such dependencies are contained in the 'directDependencies' field of the module info, support querying them from a details of a textual module.
  • Loading branch information
artemcm committed May 19, 2023
1 parent 3794e75 commit 5c08f2b
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 17 deletions.
2 changes: 2 additions & 0 deletions Sources/CSwiftScan/include/swiftscan_header.h
Expand Up @@ -122,6 +122,8 @@ typedef struct {
(*swiftscan_swift_textual_detail_get_context_hash)(swiftscan_module_details_t);
bool
(*swiftscan_swift_textual_detail_get_is_framework)(swiftscan_module_details_t);
swiftscan_string_set_t *
(*swiftscan_swift_textual_detail_get_swift_overlay_dependencies)(swiftscan_module_details_t);

//=== Swift Binary Module Details query APIs ------------------------------===//
swiftscan_string_ref_t
Expand Down
Expand Up @@ -124,7 +124,6 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
}
for moduleId in swiftDependencies {
let moduleInfo = try dependencyGraph.moduleInfo(of: moduleId)
let pcmArgs = try dependencyGraph.swiftModulePCMArgs(of: moduleId)
var inputs: [TypedVirtualPath] = []
let outputs: [TypedVirtualPath] = [
TypedVirtualPath(file: moduleInfo.modulePath.path, type: .swiftModule)
Expand Down
Expand Up @@ -75,9 +75,22 @@ extension InterModuleDependencyGraph {
}
// Traverse the set of modules in reverse topological order, assimilating transitive closures
for moduleId in topologicalIdList.reversed() {
for dependencyId in try moduleInfo(of: moduleId).directDependencies! {
let moduleInfo = try moduleInfo(of: moduleId)
for dependencyId in moduleInfo.directDependencies! {
transitiveClosureMap[moduleId]!.formUnion(transitiveClosureMap[dependencyId]!)
}
// For Swift dependencies, their corresponding Swift Overlay dependencies
// and bridging header dependencies are equivalent to direct dependencies.
if case .swift(let swiftModuleDetails) = moduleInfo.details {
let swiftOverlayDependencies = swiftModuleDetails.swiftOverlayDependencies ?? []
for dependencyId in swiftOverlayDependencies {
transitiveClosureMap[moduleId]!.formUnion(transitiveClosureMap[dependencyId]!)
}
let bridgingHeaderDependencies = swiftModuleDetails.bridgingHeaderDependencies ?? []
for dependencyId in bridgingHeaderDependencies {
transitiveClosureMap[moduleId]!.formUnion(transitiveClosureMap[dependencyId]!)
}
}
}
// For ease of use down-the-line, remove the node's self from its set of reachable nodes
for (key, _) in transitiveClosureMap {
Expand Down
Expand Up @@ -79,7 +79,9 @@ extension ModuleDependencyId: Codable {
/// Bridging header
public struct BridgingHeader: Codable {
var path: TextualVirtualPath
/// The source files referenced by the bridging header.
var sourceFiles: [TextualVirtualPath]
/// Modules that the bridging header specifically depends on
var moduleDependencies: [String]
}

Expand All @@ -92,13 +94,16 @@ public struct SwiftModuleDetails: Codable {
public var compiledModuleCandidates: [TextualVirtualPath]?

/// The bridging header, if any.
public var bridgingHeaderPath: TextualVirtualPath?

/// The source files referenced by the bridging header.
public var bridgingSourceFiles: [TextualVirtualPath]? = []

/// Modules that the bridging header specifically depends on
public var bridgingHeaderDependencies: [ModuleDependencyId]? = []
public var bridgingHeader: BridgingHeader?
public var bridgingHeaderPath: TextualVirtualPath? {
bridgingHeader?.path
}
public var bridgingSourceFiles: [TextualVirtualPath]? {
bridgingHeader?.sourceFiles
}
public var bridgingHeaderDependencies: [ModuleDependencyId]? {
bridgingHeader?.moduleDependencies.map { .clang($0) }
}

/// Options to the compile command
public var commandLine: [String]? = []
Expand All @@ -115,6 +120,9 @@ public struct SwiftModuleDetails: Codable {

/// A flag to indicate whether or not this module is a framework.
public var isFramework: Bool?

/// A set of Swift Overlays of Clang Module Dependencies
var swiftOverlayDependencies: [ModuleDependencyId]?
}

/// Details specific to Swift placeholder dependencies.
Expand Down
39 changes: 31 additions & 8 deletions Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift
Expand Up @@ -123,7 +123,8 @@ private extension SwiftScan {
guard let moduleDetailsRef = api.swiftscan_module_info_get_details(moduleInfoRef) else {
throw DependencyScanningError.missingField("modules[\(moduleId)].details")
}
let details = try constructModuleDetails(from: moduleDetailsRef)
let details = try constructModuleDetails(from: moduleDetailsRef,
moduleAliases: moduleAliases)

return (moduleId, ModuleInfo(modulePath: modulePath, sourceFiles: sourceFiles,
directDependencies: directDependencies,
Expand All @@ -133,12 +134,14 @@ private extension SwiftScan {
/// From a reference to a binary-format module info details object info returned by libSwiftScan,
/// construct an instance of an `ModuleInfo`.Details as used by the driver.
/// The object returned by libSwiftScan is a union so ensure to execute dependency-specific queries.
func constructModuleDetails(from moduleDetailsRef: swiftscan_module_details_t)
func constructModuleDetails(from moduleDetailsRef: swiftscan_module_details_t,
moduleAliases: [String: String]?)
throws -> ModuleInfo.Details {
let moduleKind = api.swiftscan_module_detail_get_kind(moduleDetailsRef)
switch moduleKind {
case SWIFTSCAN_DEPENDENCY_INFO_SWIFT_TEXTUAL:
return .swift(try constructSwiftTextualModuleDetails(from: moduleDetailsRef))
return .swift(try constructSwiftTextualModuleDetails(from: moduleDetailsRef,
moduleAliases: moduleAliases))
case SWIFTSCAN_DEPENDENCY_INFO_SWIFT_BINARY:
return .swiftPrebuiltExternal(try constructSwiftBinaryModuleDetails(from: moduleDetailsRef))
case SWIFTSCAN_DEPENDENCY_INFO_SWIFT_PLACEHOLDER:
Expand All @@ -151,7 +154,8 @@ private extension SwiftScan {
}

/// Construct a `SwiftModuleDetails` from a `swiftscan_module_details_t` reference
func constructSwiftTextualModuleDetails(from moduleDetailsRef: swiftscan_module_details_t)
func constructSwiftTextualModuleDetails(from moduleDetailsRef: swiftscan_module_details_t,
moduleAliases: [String: String]?)
throws -> SwiftModuleDetails {
let moduleInterfacePath =
try getOptionalPathDetail(from: moduleDetailsRef,
Expand All @@ -168,6 +172,15 @@ private extension SwiftScan {
let bridgingHeaderDependencies =
try getOptionalStringArrayDetail(from: moduleDetailsRef,
using: api.swiftscan_swift_textual_detail_get_bridging_module_dependencies)
let bridgingHeader: BridgingHeader?
if let resolvedBridgingHeaderPath = bridgingHeaderPath {
bridgingHeader = BridgingHeader(path: resolvedBridgingHeaderPath,
sourceFiles: bridgingSourceFiles ?? [],
moduleDependencies: bridgingHeaderDependencies ?? [])
} else {
bridgingHeader = nil
}

let commandLine =
try getOptionalStringArrayDetail(from: moduleDetailsRef,
using: api.swiftscan_swift_textual_detail_get_command_line)
Expand All @@ -180,15 +193,25 @@ private extension SwiftScan {
using: api.swiftscan_swift_textual_detail_get_context_hash)
let isFramework = api.swiftscan_swift_textual_detail_get_is_framework(moduleDetailsRef)

// Decode all dependencies of this module
let swiftOverlayDependencies: [ModuleDependencyId]?
if supportsSeparateSwiftOverlayDependencies(),
let encodedOverlayDepsRef = api.swiftscan_swift_textual_detail_get_swift_overlay_dependencies(moduleDetailsRef) {
let encodedOverlayDependencies = try toSwiftStringArray(encodedOverlayDepsRef.pointee)
swiftOverlayDependencies =
try encodedOverlayDependencies.map { try decodeModuleNameAndKind(from: $0, moduleAliases: moduleAliases) }
} else {
swiftOverlayDependencies = nil
}

return SwiftModuleDetails(moduleInterfacePath: moduleInterfacePath,
compiledModuleCandidates: compiledModuleCandidates,
bridgingHeaderPath: bridgingHeaderPath,
bridgingSourceFiles: bridgingSourceFiles,
bridgingHeaderDependencies: bridgingHeaderDependencies?.map { .clang($0) },
bridgingHeader: bridgingHeader,
commandLine: commandLine,
contextHash: contextHash,
extraPcmArgs: extraPcmArgs,
isFramework: isFramework)
isFramework: isFramework,
swiftOverlayDependencies: swiftOverlayDependencies)
}

/// Construct a `SwiftPrebuiltExternalModuleDetails` from a `swiftscan_module_details_t` reference
Expand Down
8 changes: 8 additions & 0 deletions Sources/SwiftDriver/SwiftScan/SwiftScan.swift
Expand Up @@ -263,6 +263,10 @@ internal extension swiftscan_diagnostic_severity_t {
func resetScannerCache() {
api.swiftscan_scanner_cache_reset(scanner)
}

@_spi(Testing) public func supportsSeparateSwiftOverlayDependencies() -> Bool {
return api.swiftscan_swift_textual_detail_get_swift_overlay_dependencies != nil
}

@_spi(Testing) public func supportsScannerDiagnostics() -> Bool {
return api.swiftscan_scanner_diagnostics_query != nil &&
Expand Down Expand Up @@ -419,6 +423,10 @@ private extension swiftscan_functions_t {
self.swiftscan_swift_binary_detail_get_is_framework =
try loadOptional("swiftscan_swift_binary_detail_get_is_framework")

// Swift Overlay Dependencies
self.swiftscan_swift_textual_detail_get_swift_overlay_dependencies =
try loadOptional("swiftscan_swift_textual_detail_get_swift_overlay_dependencies")

// MARK: Required Methods
func loadRequired<T>(_ symbol: String) throws -> T {
guard let sym: T = Loader.lookup(symbol: symbol, in: swiftscan) else {
Expand Down

0 comments on commit 5c08f2b

Please sign in to comment.