Skip to content

Commit

Permalink
[Explicit Module Builds] Add support for header dependencies of binar…
Browse files Browse the repository at this point in the history
…y Swift module dependencies

When we encounter a pre-built Swift binary module dependency (without an interface file), such module may have been built with a bridging header, which must still be present and is referenced by the binary .swiftmodule as either a .h or, more-likely, a pre-built .pch in a fully-explicit build.

Clients must be able to know about such header dependencies in order to be able to import this binary module, because this binary module may be referencing types brought in via its bridging header. The build-system client (swift-driver) will then ensure these header dependencies are fed as inputs to all requiring compilation tasks.

This adds support to the driver to query such header dependencies and feed them as inputs to all requiring compilation tasks.
  • Loading branch information
artemcm committed Jun 12, 2023
1 parent f66dd67 commit c772569
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 19 deletions.
2 changes: 2 additions & 0 deletions Sources/CSwiftScan/include/swiftscan_header.h
Expand Up @@ -132,6 +132,8 @@ typedef struct {
(*swiftscan_swift_binary_detail_get_module_doc_path)(swiftscan_module_details_t);
swiftscan_string_ref_t
(*swiftscan_swift_binary_detail_get_module_source_info_path)(swiftscan_module_details_t);
swiftscan_string_set_t *
(*swiftscan_swift_binary_detail_get_header_dependencies)(swiftscan_module_details_t);
bool
(*swiftscan_swift_binary_detail_get_is_framework)(swiftscan_module_details_t);

Expand Down
Expand Up @@ -234,6 +234,12 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
for dependencyModule in swiftDependencyArtifacts {
inputs.append(TypedVirtualPath(file: dependencyModule.modulePath.path,
type: .swiftModule))

for headerDep in dependencyModule.prebuiltHeaderDependencyPaths ?? [] {
commandLine.appendFlags(["-Xcc", "-include-pch", "-Xcc"])
commandLine.appendPath(VirtualPath.lookup(headerDep.path))
inputs.append(TypedVirtualPath(file: headerDep.path, type: .pch))
}
}

// Clang module dependencies are specified on the command line explicitly
Expand Down Expand Up @@ -301,6 +307,7 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
swiftDependencyArtifacts.append(
SwiftModuleArtifactInfo(name: dependencyId.moduleName,
modulePath: TextualVirtualPath(path: swiftModulePath.fileHandle),
headerDependencies: prebuiltModuleDetails.headerDependencyPaths,
isFramework: isFramework))
case .swiftPlaceholder:
fatalError("Unresolved placeholder dependencies at planning stage: \(dependencyId) of \(moduleId)")
Expand Down
Expand Up @@ -146,16 +146,21 @@ public struct SwiftPrebuiltExternalModuleDetails: Codable {
/// The path to the .swiftSourceInfo file.
public var moduleSourceInfoPath: TextualVirtualPath?

/// The paths to the binary module's header dependencies
public var headerDependencyPaths: [TextualVirtualPath]?

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

public init(compiledModulePath: TextualVirtualPath,
moduleDocPath: TextualVirtualPath? = nil,
moduleSourceInfoPath: TextualVirtualPath? = nil,
headerDependencies: [TextualVirtualPath]? = nil,
isFramework: Bool) throws {
self.compiledModulePath = compiledModulePath
self.moduleDocPath = moduleDocPath
self.moduleSourceInfoPath = moduleSourceInfoPath
self.headerDependencyPaths = headerDependencies
self.isFramework = isFramework
}
}
Expand Down
Expand Up @@ -132,16 +132,23 @@ public class InterModuleDependencyOracle {

@_spi(Testing) public func supportsScannerDiagnostics() throws -> Bool {
guard let swiftScan = swiftScanLibInstance else {
fatalError("Attempting to reset scanner cache with no scanner instance.")
fatalError("Attempting to query supported scanner API with no scanner instance.")
}
return swiftScan.supportsScannerDiagnostics
}

@_spi(Testing) public func supportsBinaryModuleHeaderDependencies() throws -> Bool {
guard let swiftScan = swiftScanLibInstance else {
fatalError("Attempting to query supported scanner API with no scanner instance.")
}
return swiftScan.supportsScannerDiagnostics()
return swiftScan.supportsBinaryModuleHeaderDependencies
}

@_spi(Testing) public func getScannerDiagnostics() throws -> [ScannerDiagnosticPayload]? {
guard let swiftScan = swiftScanLibInstance else {
fatalError("Attempting to reset scanner cache with no scanner instance.")
}
guard swiftScan.supportsScannerDiagnostics() else {
guard swiftScan.supportsScannerDiagnostics else {
return nil
}
let diags = try swiftScan.queryScannerDiagnostics()
Expand Down
Expand Up @@ -24,15 +24,19 @@
public let docPath: TextualVirtualPath?
/// The path for the module's .swiftsourceinfo file
public let sourceInfoPath: TextualVirtualPath?
/// Header dependencies of this module
public let prebuiltHeaderDependencyPaths: [TextualVirtualPath]?
/// A flag to indicate whether this module is a framework
public let isFramework: Bool

init(name: String, modulePath: TextualVirtualPath, docPath: TextualVirtualPath? = nil,
sourceInfoPath: TextualVirtualPath? = nil, isFramework: Bool = false) {
sourceInfoPath: TextualVirtualPath? = nil, headerDependencies: [TextualVirtualPath]? = nil,
isFramework: Bool = false) {
self.moduleName = name
self.modulePath = modulePath
self.docPath = docPath
self.sourceInfoPath = sourceInfoPath
self.prebuiltHeaderDependencyPaths = headerDependencies
self.isFramework = isFramework
}
}
Expand Down
11 changes: 10 additions & 1 deletion Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift
Expand Up @@ -195,7 +195,7 @@ private extension SwiftScan {

// Decode all dependencies of this module
let swiftOverlayDependencies: [ModuleDependencyId]?
if supportsSeparateSwiftOverlayDependencies(),
if supportsSeparateSwiftOverlayDependencies,
let encodedOverlayDepsRef = api.swiftscan_swift_textual_detail_get_swift_overlay_dependencies(moduleDetailsRef) {
let encodedOverlayDependencies = try toSwiftStringArray(encodedOverlayDepsRef.pointee)
swiftOverlayDependencies =
Expand Down Expand Up @@ -228,6 +228,14 @@ private extension SwiftScan {
try getOptionalPathDetail(from: moduleDetailsRef,
using: api.swiftscan_swift_binary_detail_get_module_source_info_path)

let headerDependencies: [TextualVirtualPath]?
if supportsBinaryModuleHeaderDependencies {
headerDependencies = try getOptionalPathArrayDetail(from: moduleDetailsRef,
using: api.swiftscan_swift_binary_detail_get_header_dependencies)
} else {
headerDependencies = nil
}

let isFramework: Bool
if hasBinarySwiftModuleIsFramework {
isFramework = api.swiftscan_swift_binary_detail_get_is_framework(moduleDetailsRef)
Expand All @@ -238,6 +246,7 @@ private extension SwiftScan {
return try SwiftPrebuiltExternalModuleDetails(compiledModulePath: compiledModulePath,
moduleDocPath: moduleDocPath,
moduleSourceInfoPath: moduleSourceInfoPath,
headerDependencies: headerDependencies,
isFramework: isFramework)
}

Expand Down
37 changes: 23 additions & 14 deletions Sources/SwiftDriver/SwiftScan/SwiftScan.swift
Expand Up @@ -250,34 +250,39 @@ internal extension swiftscan_diagnostic_severity_t {
api.swiftscan_clang_detail_get_captured_pcm_args != nil
}

func serializeScannerCache(to path: AbsolutePath) {
api.swiftscan_scanner_cache_serialize(scanner,
path.description.cString(using: String.Encoding.utf8))
@_spi(Testing) public var supportsBinaryModuleHeaderDependencies : Bool {
return api.swiftscan_swift_binary_detail_get_header_dependencies != nil
}

func loadScannerCache(from path: AbsolutePath) -> Bool {
return api.swiftscan_scanner_cache_load(scanner,
path.description.cString(using: String.Encoding.utf8))
@_spi(Testing) public var supportsStringDispose : Bool {
return api.swiftscan_string_dispose != nil
}

func resetScannerCache() {
api.swiftscan_scanner_cache_reset(scanner)
}

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

@_spi(Testing) public var supportsScannerDiagnostics : Bool {
return api.swiftscan_scanner_diagnostics_query != nil &&
api.swiftscan_scanner_diagnostics_reset != nil &&
api.swiftscan_diagnostic_get_message != nil &&
api.swiftscan_diagnostic_get_severity != nil &&
api.swiftscan_diagnostics_set_dispose != nil
}

@_spi(Testing) public func supportsStringDispose() -> Bool {
return api.swiftscan_string_dispose != nil
func serializeScannerCache(to path: AbsolutePath) {
api.swiftscan_scanner_cache_serialize(scanner,
path.description.cString(using: String.Encoding.utf8))
}

func loadScannerCache(from path: AbsolutePath) -> Bool {
return api.swiftscan_scanner_cache_load(scanner,
path.description.cString(using: String.Encoding.utf8))
}

func resetScannerCache() {
api.swiftscan_scanner_cache_reset(scanner)
}

@_spi(Testing) public func queryScannerDiagnostics() throws -> [ScannerDiagnosticPayload] {
Expand Down Expand Up @@ -427,6 +432,10 @@ private extension swiftscan_functions_t {
self.swiftscan_swift_textual_detail_get_swift_overlay_dependencies =
try loadOptional("swiftscan_swift_textual_detail_get_swift_overlay_dependencies")

// Header dependencies of binary modules
self.swiftscan_swift_binary_detail_get_header_dependencies =
try loadOptional("swiftscan_swift_binary_detail_get_header_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 c772569

Please sign in to comment.