Skip to content

Commit 46ee42e

Browse files
[BridgingHeader] Support automatic bridging header chaining
Teach swift driver to handle bridging header chaining and pass the correct arguments to swift-frontend.
1 parent 90ddf76 commit 46ee42e

13 files changed

+317
-58
lines changed

Sources/CSwiftScan/include/swiftscan_header.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
#include <stddef.h>
1818
#include <stdint.h>
1919

20-
#define SWIFTSCAN_VERSION_MAJOR 1
21-
#define SWIFTSCAN_VERSION_MINOR 0
20+
#define SWIFTSCAN_VERSION_MAJOR 2
21+
#define SWIFTSCAN_VERSION_MINOR 1
2222

2323
//=== Public Scanner Data Types -------------------------------------------===//
2424

@@ -143,6 +143,12 @@ typedef struct {
143143
(*swiftscan_swift_textual_detail_get_swift_overlay_dependencies)(swiftscan_module_details_t);
144144
swiftscan_string_ref_t
145145
(*swiftscan_swift_textual_detail_get_module_cache_key)(swiftscan_module_details_t);
146+
swiftscan_string_ref_t
147+
(*swiftscan_swift_textual_detail_get_user_module_version)(swiftscan_module_details_t);
148+
swiftscan_string_ref_t
149+
(*swiftscan_swift_textual_detail_get_chained_bridging_header_path)(swiftscan_module_details_t);
150+
swiftscan_string_ref_t
151+
(*swiftscan_swift_textual_detail_get_chained_bridging_header_content)(swiftscan_module_details_t);
146152

147153
//=== Swift Binary Module Details query APIs ------------------------------===//
148154
swiftscan_string_ref_t

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 87 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -332,17 +332,38 @@ public struct Driver {
332332
}
333333
}
334334

335+
/// If PCH job is needed.
336+
let producePCHJob: Bool
337+
338+
/// Original ObjC Header passed from command-line
339+
let originalObjCHeaderFile: VirtualPath.Handle?
340+
335341
/// The path to the imported Objective-C header.
336-
let importedObjCHeader: VirtualPath.Handle?
342+
lazy var importedObjCHeader: VirtualPath.Handle? = {
343+
assert(explicitDependencyBuildPlanner != nil ||
344+
!parsedOptions.hasArgument(.driverExplicitModuleBuild) ||
345+
!inputFiles.contains { $0.type == .swift },
346+
"should not be queried before scanning")
347+
let chainedBridgingHeader = try? explicitDependencyBuildPlanner?.getChainedBridgingHeaderFile()
348+
return try? computeImportedObjCHeader(&parsedOptions, compilerMode: compilerMode,
349+
chainedBridgingHeader: chainedBridgingHeader) ?? originalObjCHeaderFile
350+
}()
351+
352+
/// The directory to emit PCH file.
353+
lazy var bridgingPrecompiledHeaderOutputDir: VirtualPath? = {
354+
return try? computePrecompiledBridgingHeaderDir(&parsedOptions,
355+
compilerMode: compilerMode)
356+
}()
337357

338358
/// The path to the pch for the imported Objective-C header.
339359
lazy var bridgingPrecompiledHeader: VirtualPath.Handle? = {
340360
let contextHash = try? explicitDependencyBuildPlanner?.getMainModuleContextHash()
341-
return Self.computeBridgingPrecompiledHeader(&parsedOptions,
342-
compilerMode: compilerMode,
343-
importedObjCHeader: importedObjCHeader,
344-
outputFileMap: outputFileMap,
345-
contextHash: contextHash)
361+
return computeBridgingPrecompiledHeader(&parsedOptions,
362+
compilerMode: compilerMode,
363+
importedObjCHeader: importedObjCHeader,
364+
outputFileMap: outputFileMap,
365+
outputDirectory: bridgingPrecompiledHeaderOutputDir,
366+
contextHash: contextHash)
346367
}()
347368

348369
/// Path to the dependencies file.
@@ -1049,8 +1070,6 @@ public struct Driver {
10491070
parsedOptions: parsedOptions,
10501071
recordedInputModificationDates: recordedInputModificationDates)
10511072

1052-
self.importedObjCHeader = try Self.computeImportedObjCHeader(&parsedOptions, compilerMode: compilerMode, diagnosticEngine: diagnosticEngine)
1053-
10541073
self.supportedFrontendFlags =
10551074
try Self.computeSupportedCompilerArgs(of: self.toolchain,
10561075
libSwiftScan: self.swiftScanLibInstance,
@@ -1075,16 +1094,38 @@ public struct Driver {
10751094
diagnosticsEngine.emit(.warning("-cache-compile-job cannot be used without explicit module build, turn off caching"),
10761095
location: nil)
10771096
self.enableCaching = false
1078-
} else if importedObjCHeader != nil, !parsedOptions.hasFlag(positive: .enableBridgingPch, negative: .disableBridgingPch, default: true) {
1079-
diagnosticsEngine.emit(.warning("-cache-compile-job cannot be used with -disable-bridging-pch, turn off caching"),
1080-
location: nil)
1081-
self.enableCaching = false
10821097
} else {
10831098
self.enableCaching = true
10841099
}
10851100
} else {
10861101
self.enableCaching = false
10871102
}
1103+
1104+
// PCH related options.
1105+
if parsedOptions.hasArgument(.importObjcHeader) {
1106+
// Check for conflicting options.
1107+
if parsedOptions.hasArgument(.importUnderlyingModule) {
1108+
diagnosticEngine.emit(.error_framework_bridging_header)
1109+
}
1110+
1111+
if parsedOptions.hasArgument(.emitModuleInterface, .emitModuleInterfacePath) {
1112+
diagnosticEngine.emit(.error_bridging_header_module_interface)
1113+
}
1114+
}
1115+
var maybeNeedPCH = parsedOptions.hasFlag(positive: .enableBridgingPch, negative: .disableBridgingPch, default: true)
1116+
if enableCaching && !maybeNeedPCH {
1117+
diagnosticsEngine.emit(.warning("-cache-compile-job cannot be used with -disable-bridging-pch, turn on PCH generation"),
1118+
location: nil)
1119+
maybeNeedPCH = true
1120+
}
1121+
self.producePCHJob = maybeNeedPCH
1122+
1123+
if let objcHeaderPathArg = parsedOptions.getLastArgument(.importObjcHeader) {
1124+
self.originalObjCHeaderFile = try? VirtualPath.intern(path: objcHeaderPathArg.asSingle)
1125+
} else {
1126+
self.originalObjCHeaderFile = nil
1127+
}
1128+
10881129
self.useClangIncludeTree = !parsedOptions.hasArgument(.noClangIncludeTree) && !env.keys.contains("SWIFT_CACHING_USE_CLANG_CAS_FS")
10891130
self.scannerPrefixMap = try Self.computeScanningPrefixMapper(&parsedOptions)
10901131
if let sdkMapping = parsedOptions.getLastArgument(.scannerPrefixMapSdk)?.asSingle {
@@ -3052,36 +3093,47 @@ extension Driver {
30523093
// Imported Objective-C header.
30533094
extension Driver {
30543095
/// Compute the path of the imported Objective-C header.
3055-
static func computeImportedObjCHeader(
3096+
func computeImportedObjCHeader(
30563097
_ parsedOptions: inout ParsedOptions,
30573098
compilerMode: CompilerMode,
3058-
diagnosticEngine: DiagnosticsEngine
3059-
) throws -> VirtualPath.Handle? {
3060-
guard let objcHeaderPathArg = parsedOptions.getLastArgument(.importObjcHeader) else {
3061-
return nil
3099+
chainedBridgingHeader: ChainedBridgingHeaderFile?) throws -> VirtualPath.Handle? {
3100+
// handle chained bridging header.
3101+
if let chainedHeader = chainedBridgingHeader, !chainedHeader.path.isEmpty {
3102+
let path = try VirtualPath(path: chainedHeader.path)
3103+
let dirExists = try fileSystem.exists(path.parentDirectory)
3104+
if !dirExists, let dirToCreate = path.parentDirectory.absolutePath {
3105+
try fileSystem.createDirectory(dirToCreate, recursive: true)
3106+
}
3107+
try fileSystem.writeFileContents(path,
3108+
bytes: ByteString(encodingAsUTF8: chainedHeader.content),
3109+
atomically: true)
3110+
return path.intern()
30623111
}
3112+
return originalObjCHeaderFile
3113+
}
30633114

3064-
// Check for conflicting options.
3065-
if parsedOptions.hasArgument(.importUnderlyingModule) {
3066-
diagnosticEngine.emit(.error_framework_bridging_header)
3115+
/// Compute the path to the bridging precompiled header directory path.
3116+
func computePrecompiledBridgingHeaderDir(
3117+
_ parsedOptions: inout ParsedOptions,
3118+
compilerMode: CompilerMode) throws -> VirtualPath? {
3119+
if let input = originalObjCHeaderFile,
3120+
let outputPath = try? outputFileMap?.existingOutput(inputFile: input, outputType: .pch) {
3121+
return VirtualPath.lookup(outputPath).parentDirectory
30673122
}
3068-
3069-
if parsedOptions.hasArgument(.emitModuleInterface, .emitModuleInterfacePath) {
3070-
diagnosticEngine.emit(.error_bridging_header_module_interface)
3123+
if let outputDir = parsedOptions.getLastArgument(.pchOutputDir)?.asSingle {
3124+
return try VirtualPath(path: outputDir)
30713125
}
3072-
3073-
return try VirtualPath.intern(path: objcHeaderPathArg.asSingle)
3126+
return nil
30743127
}
30753128

30763129
/// Compute the path of the generated bridging PCH for the Objective-C header.
3077-
static func computeBridgingPrecompiledHeader(_ parsedOptions: inout ParsedOptions,
3078-
compilerMode: CompilerMode,
3079-
importedObjCHeader: VirtualPath.Handle?,
3080-
outputFileMap: OutputFileMap?,
3081-
contextHash: String?) -> VirtualPath.Handle? {
3082-
guard compilerMode.supportsBridgingPCH,
3083-
let input = importedObjCHeader,
3084-
parsedOptions.hasFlag(positive: .enableBridgingPch, negative: .disableBridgingPch, default: true) else {
3130+
func computeBridgingPrecompiledHeader(_ parsedOptions: inout ParsedOptions,
3131+
compilerMode: CompilerMode,
3132+
importedObjCHeader: VirtualPath.Handle?,
3133+
outputFileMap: OutputFileMap?,
3134+
outputDirectory: VirtualPath?,
3135+
contextHash: String?) -> VirtualPath.Handle? {
3136+
guard compilerMode.supportsBridgingPCH, producePCHJob, let input = importedObjCHeader else {
30853137
return nil
30863138
}
30873139

@@ -3096,8 +3148,8 @@ extension Driver {
30963148
} else {
30973149
pchFile = baseName.appendingFileTypeExtension(.pch)
30983150
}
3099-
if let outputDirectory = parsedOptions.getLastArgument(.pchOutputDir)?.asSingle {
3100-
return try? VirtualPath(path: outputDirectory).appending(component: pchFile).intern()
3151+
if let outputDirectory = outputDirectory {
3152+
return outputDirectory.appending(component: pchFile).intern()
31013153
} else {
31023154
return try? VirtualPath.temporary(RelativePath(validating: pchFile)).intern()
31033155
}

Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ public struct ExternalTargetModuleDetails {
2727
let isFramework: Bool
2828
}
2929

30+
/// A chained bridging header file.
31+
public struct ChainedBridgingHeaderFile {
32+
let path: String
33+
let content: String
34+
}
35+
3036
public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalTargetModuleDetails]
3137

3238
/// In Explicit Module Build mode, this planner is responsible for generating and providing
@@ -451,6 +457,17 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
451457
return mainModuleDetails.contextHash
452458
}
453459

460+
/// Get the chained bridging header info
461+
public func getChainedBridgingHeaderFile() throws -> ChainedBridgingHeaderFile? {
462+
let mainModuleId: ModuleDependencyId = .swift(dependencyGraph.mainModuleName)
463+
let mainModuleDetails = try dependencyGraph.swiftModuleDetails(of: mainModuleId)
464+
guard let path = mainModuleDetails.chainedBridgingHeaderPath,
465+
let content = mainModuleDetails.chainedBridgingHeaderContent else{
466+
return nil
467+
}
468+
return ChainedBridgingHeaderFile(path: path, content: content)
469+
}
470+
454471
/// Resolve all module dependencies of the main module and add them to the lists of
455472
/// inputs and command line flags.
456473
public mutating func resolveBridgingHeaderDependencies(inputs: inout [TypedVirtualPath],

Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public struct SwiftModuleDetails: Codable, Hashable {
124124
/// Options to the compile command
125125
public var commandLine: [String]? = []
126126

127-
/// Options to the compile command
127+
/// Options to the compile bridging header command
128128
public var bridgingPchCommandLine: [String]? = []
129129

130130
/// The context hash for this module that encodes the producing interface's path,
@@ -140,6 +140,11 @@ public struct SwiftModuleDetails: Codable, Hashable {
140140

141141
/// The module cache key of the output module.
142142
public var moduleCacheKey: String?
143+
144+
/// Chained bridging header path
145+
public var chainedBridgingHeaderPath: String?
146+
/// Chained bridging header content
147+
public var chainedBridgingHeaderContent: String?
143148
}
144149

145150
/// Details specific to Swift placeholder dependencies.

Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import protocol TSCBasic.FileSystem
1414
import struct TSCBasic.AbsolutePath
15+
import struct TSCBasic.RelativePath
1516
import struct TSCBasic.Diagnostic
1617
import var TSCBasic.localFileSystem
1718
import var TSCBasic.stdoutStream
@@ -163,6 +164,34 @@ public extension Driver {
163164
}
164165
}
165166

167+
if isFrontendArgSupported(.autoBridgingHeaderChaining) {
168+
if parsedOptions.hasFlag(positive: .autoBridgingHeaderChaining,
169+
negative: .noAutoBridgingHeaderChaining,
170+
default: false) || isCachingEnabled {
171+
if producePCHJob {
172+
commandLine.appendFlag(.autoBridgingHeaderChaining)
173+
} else {
174+
diagnosticEngine.emit(.warning("-auto-bridging-header-chaining requires generatePCH job, no chaining will be performed"))
175+
commandLine.appendFlag(.noAutoBridgingHeaderChaining)
176+
}
177+
} else {
178+
commandLine.appendFlag(.noAutoBridgingHeaderChaining)
179+
}
180+
}
181+
182+
// Provide a directory to path to scanner for where the chained bridging header will be.
183+
// Prefer writing next to pch output, otherwise next to module output path before fallback to temp directory for non-caching build.
184+
if isFrontendArgSupported(.scannerOutputDir) {
185+
if let outputDir = try? computePrecompiledBridgingHeaderDir(&parsedOptions,
186+
compilerMode: compilerMode) {
187+
commandLine.appendFlag(.scannerOutputDir)
188+
commandLine.appendPath(outputDir)
189+
} else {
190+
commandLine.appendFlag(.scannerOutputDir)
191+
commandLine.appendPath(VirtualPath.temporary(try RelativePath(validating: "scanner")))
192+
}
193+
}
194+
166195
// Pass on the input files
167196
commandLine.append(contentsOf: inputFiles.filter { $0.type == .swift }.map { .path($0.file) })
168197
return (inputs, commandLine)

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -455,26 +455,39 @@ extension Driver {
455455
try commandLine.appendAll(.Xcc, from: &parsedOptions)
456456
}
457457

458-
if let importedObjCHeader = importedObjCHeader,
459-
bridgingHeaderHandling != .ignored {
460-
commandLine.appendFlag(.importObjcHeader)
461-
if bridgingHeaderHandling == .precompiled,
462-
let pch = bridgingPrecompiledHeader {
463-
// For explicit module build, we directly pass the compiled pch as
464-
// `-import-objc-header`, rather than rely on swift-frontend to locate
458+
let objcHeaderFile = (kind == .scanDependencies) ? originalObjCHeaderFile : importedObjCHeader
459+
if let importedObjCHeader = objcHeaderFile, bridgingHeaderHandling != .ignored {
460+
if bridgingHeaderHandling == .precompiled, let pch = bridgingPrecompiledHeader {
461+
// For explicit module build, we directly pass the compiled pch to
462+
// swift-frontend, rather than rely on swift-frontend to locate
465463
// the pch in the pchOutputDir and can start an implicit build in case
466464
// of a lookup failure.
467465
if parsedOptions.contains(.pchOutputDir) &&
468466
!parsedOptions.contains(.driverExplicitModuleBuild) {
467+
commandLine.appendFlag(.importObjcHeader)
469468
try addPathArgument(VirtualPath.lookup(importedObjCHeader), to:&commandLine, remap: jobNeedPathRemap)
470469
try commandLine.appendLast(.pchOutputDir, from: &parsedOptions)
471470
if !compilerMode.isSingleCompilation {
472471
commandLine.appendFlag(.pchDisableValidation)
473472
}
474473
} else {
475-
try addPathArgument(VirtualPath.lookup(pch), to:&commandLine, remap: jobNeedPathRemap)
474+
// If header chaining is enabled, pass objc header through `-import-objc-header` and
475+
// PCH file through `-import-pch`. Otherwise, pass either the PCH or header through
476+
// `-import-objc-header` option.
477+
if isFrontendArgSupported(.importPch), importedObjCHeader != originalObjCHeaderFile {
478+
commandLine.appendFlag(.importPch)
479+
try addPathArgument(VirtualPath.lookup(pch), to:&commandLine, remap: jobNeedPathRemap)
480+
if let originalHeader = originalObjCHeaderFile {
481+
commandLine.appendFlag(.importObjcHeader)
482+
try addPathArgument(VirtualPath.lookup(originalHeader), to:&commandLine, remap: jobNeedPathRemap)
483+
}
484+
} else {
485+
commandLine.appendFlag(.importObjcHeader)
486+
try addPathArgument(VirtualPath.lookup(pch), to:&commandLine, remap: jobNeedPathRemap)
487+
}
476488
}
477489
} else {
490+
commandLine.appendFlag(.importObjcHeader)
478491
try addPathArgument(VirtualPath.lookup(importedObjCHeader), to:&commandLine, remap: jobNeedPathRemap)
479492
}
480493
}
@@ -957,8 +970,7 @@ extension Driver {
957970
return [:]
958971
}
959972
// Resolve command-line first.
960-
let resolver = try ArgsResolver(fileSystem: fileSystem)
961-
let arguments: [String] = try resolver.resolveArgumentList(for: commandLine)
973+
let arguments: [String] = try executor.resolver.resolveArgumentList(for: commandLine)
962974

963975
return try inputs.reduce(into: [:]) { keys, input in
964976
keys[input.0] = try cas.computeCacheKey(commandLine: arguments, index: input.1)
@@ -972,8 +984,7 @@ extension Driver {
972984
return nil
973985
}
974986
// Resolve command-line first.
975-
let resolver = try ArgsResolver(fileSystem: fileSystem)
976-
let arguments: [String] = try resolver.resolveArgumentList(for: commandLine)
987+
let arguments: [String] = try executor.resolver.resolveArgumentList(for: commandLine)
977988
return try cas.computeCacheKey(commandLine: arguments, index: index)
978989
}
979990
}

Sources/SwiftDriver/Jobs/VerifyModuleInterfaceJob.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ extension Driver {
3535
var inputs: [TypedVirtualPath] = [interfaceInput]
3636
commandLine.appendFlags("-frontend", "-typecheck-module-from-interface")
3737
try addPathArgument(interfaceInput.file, to: &commandLine)
38-
try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .verifyModuleInterface)
38+
try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .verifyModuleInterface, bridgingHeaderHandling: .ignored)
3939
// FIXME: MSVC runtime flags
4040

4141
// Output serialized diagnostics for this job, if specifically requested

Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,10 @@ private extension SwiftScan {
184184
let isFramework = api.swiftscan_swift_textual_detail_get_is_framework(moduleDetailsRef)
185185
let moduleCacheKey = supportsCaching ? try getOptionalStringDetail(from: moduleDetailsRef,
186186
using: api.swiftscan_swift_textual_detail_get_module_cache_key) : nil
187+
let chainedBridgingHeaderPath = supportsChainedBridgingHeader ?
188+
try getOptionalStringDetail(from: moduleDetailsRef, using: api.swiftscan_swift_textual_detail_get_chained_bridging_header_path) : nil
189+
let chainedBridgingHeaderContent = supportsChainedBridgingHeader ?
190+
try getOptionalStringDetail(from: moduleDetailsRef, using: api.swiftscan_swift_textual_detail_get_chained_bridging_header_content) : nil
187191

188192
// Decode all dependencies of this module
189193
let swiftOverlayDependencies: [ModuleDependencyId]?
@@ -204,7 +208,9 @@ private extension SwiftScan {
204208
contextHash: contextHash,
205209
isFramework: isFramework,
206210
swiftOverlayDependencies: swiftOverlayDependencies,
207-
moduleCacheKey: moduleCacheKey)
211+
moduleCacheKey: moduleCacheKey,
212+
chainedBridgingHeaderPath: chainedBridgingHeaderPath,
213+
chainedBridgingHeaderContent: chainedBridgingHeaderContent)
208214
}
209215

210216
/// Construct a `SwiftPrebuiltExternalModuleDetails` from a `swiftscan_module_details_t` reference

0 commit comments

Comments
 (0)