Skip to content

[Explicit Module Builds] Specify which Clang module dependencies are bridging header dependencies #1571

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Sources/CSwiftScan/include/swiftscan_header.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ typedef struct {
(*swiftscan_swift_binary_detail_get_is_framework)(swiftscan_module_details_t);
swiftscan_string_ref_t
(*swiftscan_swift_binary_detail_get_module_cache_key)(swiftscan_module_details_t);
swiftscan_string_set_t *
(*swiftscan_swift_binary_detail_get_header_dependency_module_dependencies)(swiftscan_module_details_t);

//=== Swift Placeholder Module Details query APIs -------------------------===//
swiftscan_string_ref_t
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,8 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
inputs: inout [TypedVirtualPath],
commandLine: inout [Job.ArgTemplate]) throws {
// Prohibit the frontend from implicitly building textual modules into binary modules.
var swiftDependencyArtifacts: [SwiftModuleArtifactInfo] = []
var clangDependencyArtifacts: [ClangModuleArtifactInfo] = []
var swiftDependencyArtifacts: Set<SwiftModuleArtifactInfo> = []
var clangDependencyArtifacts: Set<ClangModuleArtifactInfo> = []
try addModuleDependencies(of: moduleId,
clangDependencyArtifacts: &clangDependencyArtifacts,
swiftDependencyArtifacts: &swiftDependencyArtifacts)
Expand All @@ -276,8 +276,6 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
inputs.append(TypedVirtualPath(file: headerDep.path, type: .pch))
}
}

// Clang module dependencies are specified on the command line explicitly
for moduleArtifactInfo in clangDependencyArtifacts {
let clangModulePath =
TypedVirtualPath(file: moduleArtifactInfo.clangModulePath.path,
Expand Down Expand Up @@ -311,8 +309,9 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT

private mutating func addModuleDependency(of moduleId: ModuleDependencyId,
dependencyId: ModuleDependencyId,
clangDependencyArtifacts: inout [ClangModuleArtifactInfo],
swiftDependencyArtifacts: inout [SwiftModuleArtifactInfo]
clangDependencyArtifacts: inout Set<ClangModuleArtifactInfo>,
swiftDependencyArtifacts: inout Set<SwiftModuleArtifactInfo>,
bridgingHeaderDeps: Set<ModuleDependencyId>? = nil
) throws {
switch dependencyId {
case .swift:
Expand All @@ -325,7 +324,7 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
isFramework = swiftModuleDetails.isFramework ?? false
// Accumulate the required information about this dependency
// TODO: add .swiftdoc and .swiftsourceinfo for this module.
swiftDependencyArtifacts.append(
swiftDependencyArtifacts.insert(
SwiftModuleArtifactInfo(name: dependencyId.moduleName,
modulePath: TextualVirtualPath(path: swiftModulePath.fileHandle),
isFramework: isFramework,
Expand All @@ -335,11 +334,12 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
let dependencyClangModuleDetails =
try dependencyGraph.clangModuleDetails(of: dependencyId)
// Accumulate the required information about this dependency
clangDependencyArtifacts.append(
clangDependencyArtifacts.insert(
ClangModuleArtifactInfo(name: dependencyId.moduleName,
modulePath: TextualVirtualPath(path: dependencyInfo.modulePath.path),
moduleMapPath: dependencyClangModuleDetails.moduleMapPath,
moduleCacheKey: dependencyClangModuleDetails.moduleCacheKey))
moduleCacheKey: dependencyClangModuleDetails.moduleCacheKey,
isBridgingHeaderDependency: bridgingHeaderDeps?.contains(dependencyId) ?? true))
case .swiftPrebuiltExternal:
let prebuiltModuleDetails = try dependencyGraph.swiftPrebuiltDetails(of: dependencyId)
let compiledModulePath = prebuiltModuleDetails.compiledModulePath
Expand All @@ -348,7 +348,7 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
.init(file: compiledModulePath.path, type: .swiftModule)
// Accumulate the required information about this dependency
// TODO: add .swiftdoc and .swiftsourceinfo for this module.
swiftDependencyArtifacts.append(
swiftDependencyArtifacts.insert(
SwiftModuleArtifactInfo(name: dependencyId.moduleName,
modulePath: TextualVirtualPath(path: swiftModulePath.fileHandle),
headerDependencies: prebuiltModuleDetails.headerDependencyPaths,
Expand All @@ -359,19 +359,46 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
}
}

/// Collect the Set of all Clang module dependencies which are dependencies of either
/// the `moduleId` bridging header or dependencies of bridging headers
/// of any prebuilt binary Swift modules in the dependency graph.
private func collectHeaderModuleDeps(of moduleId: ModuleDependencyId) throws -> Set<ModuleDependencyId>? {
var bridgingHeaderDeps: Set<ModuleDependencyId>? = nil
guard let moduleDependencies = reachabilityMap[moduleId] else {
fatalError("Expected reachability information for the module: \(moduleId.moduleName).")
}
if let dependencySourceBridingHeaderDeps =
try dependencyGraph.moduleInfo(of: moduleId).bridgingHeaderModuleDependencies {
bridgingHeaderDeps = Set(dependencySourceBridingHeaderDeps)
} else {
bridgingHeaderDeps = Set<ModuleDependencyId>()
}
// Collect all binary Swift module dependnecies' header input module dependencies
for dependencyId in moduleDependencies {
if case .swiftPrebuiltExternal(_) = dependencyId {
let prebuiltDependencyDetails = try dependencyGraph.swiftPrebuiltDetails(of: dependencyId)
for headerDependency in prebuiltDependencyDetails.headerDependencyModuleDependencies ?? [] {
bridgingHeaderDeps!.insert(headerDependency)
}
}
}
return bridgingHeaderDeps
}

/// Add a specific module dependency as an input and a corresponding command
/// line flag.
private mutating func addModuleDependencies(of moduleId: ModuleDependencyId,
clangDependencyArtifacts: inout [ClangModuleArtifactInfo],
swiftDependencyArtifacts: inout [SwiftModuleArtifactInfo]
clangDependencyArtifacts: inout Set<ClangModuleArtifactInfo>,
swiftDependencyArtifacts: inout Set<SwiftModuleArtifactInfo>
) throws {
guard let moduleDependencies = reachabilityMap[moduleId] else {
fatalError("Expected reachability information for the module: \(moduleId.moduleName).")
}
for dependencyId in moduleDependencies {
try addModuleDependency(of: moduleId, dependencyId: dependencyId,
clangDependencyArtifacts: &clangDependencyArtifacts,
swiftDependencyArtifacts: &swiftDependencyArtifacts)
swiftDependencyArtifacts: &swiftDependencyArtifacts,
bridgingHeaderDeps: try collectHeaderModuleDeps(of: moduleId))
}
}

Expand Down Expand Up @@ -430,8 +457,8 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
public mutating func resolveBridgingHeaderDependencies(inputs: inout [TypedVirtualPath],
commandLine: inout [Job.ArgTemplate]) throws {
let mainModuleId: ModuleDependencyId = .swift(dependencyGraph.mainModuleName)
var swiftDependencyArtifacts: [SwiftModuleArtifactInfo] = []
var clangDependencyArtifacts: [ClangModuleArtifactInfo] = []
var swiftDependencyArtifacts: Set<SwiftModuleArtifactInfo> = []
var clangDependencyArtifacts: Set<ClangModuleArtifactInfo> = []
let mainModuleDetails = try dependencyGraph.swiftModuleDetails(of: mainModuleId)

var addedDependencies: Set<ModuleDependencyId> = []
Expand Down Expand Up @@ -491,8 +518,8 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT

/// Serialize the output file artifacts for a given module in JSON format.
private func serializeModuleDependencies(for moduleId: ModuleDependencyId,
swiftDependencyArtifacts: [SwiftModuleArtifactInfo],
clangDependencyArtifacts: [ClangModuleArtifactInfo]
swiftDependencyArtifacts: Set<SwiftModuleArtifactInfo>,
clangDependencyArtifacts: Set<ClangModuleArtifactInfo>
) throws -> Data {
// The module dependency map in CAS needs to be stable.
// Sort the dependencies by name.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,24 +164,14 @@ public struct SwiftPrebuiltExternalModuleDetails: Codable, Hashable {
/// The paths to the binary module's header dependencies
public var headerDependencyPaths: [TextualVirtualPath]?

/// Clang module dependencies of the textual header input
public var headerDependencyModuleDependencies: [ModuleDependencyId]?

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

/// The module cache key of the pre-built module.
public var moduleCacheKey: String?

public init(compiledModulePath: TextualVirtualPath,
moduleDocPath: TextualVirtualPath? = nil,
moduleSourceInfoPath: TextualVirtualPath? = nil,
headerDependencies: [TextualVirtualPath]? = nil,
isFramework: Bool, moduleCacheKey: String? = nil) throws {
self.compiledModulePath = compiledModulePath
self.moduleDocPath = moduleDocPath
self.moduleSourceInfoPath = moduleSourceInfoPath
self.headerDependencyPaths = headerDependencies
self.isFramework = isFramework
self.moduleCacheKey = moduleCacheKey
}
}

/// Details specific to Clang modules.
Expand All @@ -201,18 +191,6 @@ public struct ClangModuleDetails: Codable, Hashable {

/// The module cache key of the output module.
public var moduleCacheKey: String?

public init(moduleMapPath: TextualVirtualPath,
contextHash: String,
commandLine: [String],
capturedPCMArgs: Set<[String]>?,
moduleCacheKey: String? = nil) {
self.moduleMapPath = moduleMapPath
self.contextHash = contextHash
self.commandLine = commandLine
self.capturedPCMArgs = capturedPCMArgs
self.moduleCacheKey = moduleCacheKey
}
}

public struct ModuleInfo: Codable, Hashable {
Expand Down Expand Up @@ -302,6 +280,19 @@ extension ModuleInfo.Details: Codable {
}
}

extension ModuleInfo {
var bridgingHeaderModuleDependencies: [ModuleDependencyId]? {
switch details {
case .swift(let swiftDetails):
return swiftDetails.bridgingHeaderDependencies
case .swiftPrebuiltExternal(let swiftPrebuiltDetails):
return swiftPrebuiltDetails.headerDependencyModuleDependencies
default:
return nil
}
}
}

/// Describes the complete set of dependencies for a Swift module, including
/// all of the Swift and C modules and source files it depends on.
public struct InterModuleDependencyGraph: Codable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@ public class InterModuleDependencyOracle {
return swiftScan.hasBinarySwiftModuleIsFramework
}

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

@_spi(Testing) public func supportsScannerDiagnostics() throws -> Bool {
guard let swiftScan = swiftScanLibInstance else {
fatalError("Attempting to query supported scanner API with no scanner instance.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
/// - Swift Module Path
/// - Swift Doc Path
/// - Swift Source Info Path
@_spi(Testing) public struct SwiftModuleArtifactInfo: Codable {
@_spi(Testing) public struct SwiftModuleArtifactInfo: Codable, Hashable {
/// The module's name
public let moduleName: String
/// The path for the module's .swiftmodule file
Expand Down Expand Up @@ -48,7 +48,7 @@
/// - Clang Module (name)
/// - Clang Module (PCM) Path
/// - Clang Module Map Path
@_spi(Testing) public struct ClangModuleArtifactInfo: Codable {
@_spi(Testing) public struct ClangModuleArtifactInfo: Codable, Hashable {
/// The module's name
public let moduleName: String
/// The path for the module's .pcm file
Expand All @@ -57,24 +57,28 @@
public let clangModuleMapPath: TextualVirtualPath
/// A flag to indicate whether this module is a framework
public let isFramework: Bool
/// A flag to indicate whether this module is a dependency
/// of the main module's bridging header
public let isBridgingHeaderDependency: Bool
/// The cache key for the module.
public let clangModuleCacheKey: String?

init(name: String, modulePath: TextualVirtualPath, moduleMapPath: TextualVirtualPath,
moduleCacheKey: String? = nil) {
moduleCacheKey: String? = nil, isBridgingHeaderDependency: Bool = true) {
self.moduleName = name
self.clangModulePath = modulePath
self.clangModuleMapPath = moduleMapPath
self.isFramework = false
self.isBridgingHeaderDependency = isBridgingHeaderDependency
self.clangModuleCacheKey = moduleCacheKey
}
}

enum ModuleDependencyArtifactInfo: Codable {
@_spi(Testing) public enum ModuleDependencyArtifactInfo: Codable {
case clang(ClangModuleArtifactInfo)
case swift(SwiftModuleArtifactInfo)

func encode(to encoder: Encoder) throws {
@_spi(Testing) public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .swift(let swiftInfo):
Expand All @@ -83,6 +87,17 @@ enum ModuleDependencyArtifactInfo: Codable {
try container.encode(clangInfo)
}
}

@_spi(Testing) public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
let thing = try container.decode(SwiftModuleArtifactInfo.self)
self = .swift(thing)
} catch {
let thing = try container.decode(ClangModuleArtifactInfo.self)
self = .clang(thing)
}
}
}

/// Describes a given module's batch dependency scanning input info
Expand Down
19 changes: 13 additions & 6 deletions Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -249,15 +249,22 @@ private extension SwiftScan {
} else {
isFramework = false
}

let headerDependencyModuleDependencies: [ModuleDependencyId]? =
hasBinarySwiftModuleHeaderModuleDependencies ?
try getOptionalStringArrayDetail(from: moduleDetailsRef,
using: api.swiftscan_swift_binary_detail_get_header_dependency_module_dependencies)?.map { .clang($0) } : nil

let moduleCacheKey = supportsCaching ? try getOptionalStringDetail(from: moduleDetailsRef,
using: api.swiftscan_swift_binary_detail_get_module_cache_key) : nil

return try SwiftPrebuiltExternalModuleDetails(compiledModulePath: compiledModulePath,
moduleDocPath: moduleDocPath,
moduleSourceInfoPath: moduleSourceInfoPath,
headerDependencies: headerDependencies,
isFramework: isFramework,
moduleCacheKey: moduleCacheKey)
return SwiftPrebuiltExternalModuleDetails(compiledModulePath: compiledModulePath,
moduleDocPath: moduleDocPath,
moduleSourceInfoPath: moduleSourceInfoPath,
headerDependencyPaths: headerDependencies,
headerDependencyModuleDependencies: headerDependencyModuleDependencies,
isFramework: isFramework,
moduleCacheKey: moduleCacheKey)
}

/// Construct a `SwiftPlaceholderModuleDetails` from a `swiftscan_module_details_t` reference
Expand Down
9 changes: 9 additions & 0 deletions Sources/SwiftDriver/SwiftScan/SwiftScan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,10 @@ internal extension swiftscan_diagnostic_severity_t {
api.swiftscan_swift_binary_detail_get_is_framework != nil
}

@_spi(Testing) public var hasBinarySwiftModuleHeaderModuleDependencies : Bool {
api.swiftscan_swift_binary_detail_get_header_dependency_module_dependencies != nil
}

@_spi(Testing) public var canLoadStoreScannerCache : Bool {
api.swiftscan_scanner_cache_load != nil &&
api.swiftscan_scanner_cache_serialize != nil &&
Expand Down Expand Up @@ -534,6 +538,11 @@ private extension swiftscan_functions_t {
self.swiftscan_swift_binary_detail_get_is_framework =
try loadOptional("swiftscan_swift_binary_detail_get_is_framework")

// Clang module dependencies of header input of binary module dependencies
self.swiftscan_swift_binary_detail_get_header_dependency_module_dependencies =
try loadOptional("swiftscan_swift_binary_detail_get_header_dependency_module_dependencies")

// Bridging PCH build command-line
self.swiftscan_swift_textual_detail_get_bridging_pch_command_line =
try loadOptional("swiftscan_swift_textual_detail_get_bridging_pch_command_line")

Expand Down
Loading