Skip to content

Commit ad4af6a

Browse files
Improving caching build handling to support a range of compiler
Improve the logics for when to use bridging header pch command from dependency scanner, when to use explicit module verify interface, so swift driver can work with more swift-frontend.
1 parent ac707eb commit ad4af6a

File tree

10 files changed

+141
-10
lines changed

10 files changed

+141
-10
lines changed

Sources/SwiftDriver/Driver/Driver.swift

+4
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ public struct Driver {
7474
case missingContextHashOnSwiftDependency(String)
7575
case dependencyScanningFailure(Int, String)
7676
case missingExternalDependency(String)
77+
// Compiler Caching Failures
78+
case unsupportedConfigurationForCaching(String)
7779

7880
public var description: String {
7981
switch self {
@@ -135,6 +137,8 @@ public struct Driver {
135137
return "unable to load output file map '\(path)': \(error)"
136138
case .missingExternalDependency(let moduleName):
137139
return "Missing External dependency info for module: \(moduleName)"
140+
case .unsupportedConfigurationForCaching(let reason):
141+
return "unsupported configuration for -cache-compile-job: \(reason)"
138142
case .baselineGenerationRequiresTopLevelModule(let arg):
139143
return "generating a baseline with '\(arg)' is only supported with '-emit-module' or '-emit-module-path'"
140144
case .optionRequiresAnother(let first, let second):

Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift

+11-1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
7171
self.enableCAS = enableCAS
7272
}
7373

74+
/// Supports resolving bridging header pch command from swiftScan.
75+
public func supportsBridgingHeaderPCHCommand() throws -> Bool {
76+
return try swiftScanOracle.supportsBridgingHeaderPCHCommand()
77+
}
78+
7479
/// Generate build jobs for all dependencies of the main module.
7580
/// The main module itself is handled elsewhere in the driver.
7681
///
@@ -241,7 +246,12 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
241246
inputs.append(TypedVirtualPath(file: dependencyModule.modulePath.path,
242247
type: .swiftModule))
243248

244-
for headerDep in dependencyModule.prebuiltHeaderDependencyPaths ?? [] {
249+
let prebuiltHeaderDependencyPaths = dependencyModule.prebuiltHeaderDependencyPaths ?? []
250+
if enableCAS && !prebuiltHeaderDependencyPaths.isEmpty {
251+
throw Driver.Error.unsupportedConfigurationForCaching("module \(dependencyModule.moduleName) has prebuilt header dependency")
252+
}
253+
254+
for headerDep in prebuiltHeaderDependencyPaths {
245255
commandLine.appendFlags(["-Xcc", "-include-pch", "-Xcc"])
246256
commandLine.appendPath(VirtualPath.lookup(headerDep.path))
247257
inputs.append(TypedVirtualPath(file: headerDep.path, type: .pch))

Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift

+7
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,13 @@ public class InterModuleDependencyOracle {
152152
return swiftScan.supportsCaching
153153
}
154154

155+
@_spi(Testing) public func supportsBridgingHeaderPCHCommand() throws -> Bool {
156+
guard let swiftScan = swiftScanLibInstance else {
157+
fatalError("Attempting to query supported scanner API with no scanner instance.")
158+
}
159+
return swiftScan.supportsBridgingHeaderPCHCommand
160+
}
161+
155162
@_spi(Testing) public func getScannerDiagnostics() throws -> [ScannerDiagnosticPayload]? {
156163
guard let swiftScan = swiftScanLibInstance else {
157164
fatalError("Attempting to reset scanner cache with no scanner instance.")

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

+8
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,14 @@ extension Driver {
687687
try dependencyPlanner.resolveBridgingHeaderDependencies(inputs: &inputs, commandLine: &commandLine)
688688
}
689689

690+
/// If explicit dependency planner supports creating bridging header pch command.
691+
public func supportsBridgingHeaderPCHCommand() throws -> Bool {
692+
guard let dependencyPlanner = explicitDependencyBuildPlanner else {
693+
return false
694+
}
695+
return try dependencyPlanner.supportsBridgingHeaderPCHCommand()
696+
}
697+
690698
/// In Explicit Module Build mode, distinguish between main module jobs and intermediate dependency build jobs,
691699
/// such as Swift modules built from .swiftmodule files and Clang PCMs.
692700
public func isExplicitMainModuleJob(job: Job) -> Bool {

Sources/SwiftDriver/Jobs/GeneratePCHJob.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ extension Driver {
3030

3131
var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) }
3232

33-
// If using CAS, scanner should return the full build command.
34-
if enableCaching {
33+
if try supportsBridgingHeaderPCHCommand() {
3534
try addExplicitPCHBuildArguments(inputs: &inputs, commandLine: &commandLine)
3635
} else {
3736
try addGeneratePCHFlags(commandLine: &commandLine, inputs: &inputs)

Sources/SwiftDriver/Jobs/VerifyModuleInterfaceJob.swift

+7-4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ extension Driver {
2525
input: mainInput.fileHandle)
2626
}
2727

28+
@_spi(Testing)
29+
public func supportExplicitModuleVerifyInterface() -> Bool {
30+
// swift-frontend that has -input-file-key option can support explicit module build for verify interface.
31+
return isFrontendArgSupported(.inputFileKey)
32+
}
33+
2834
mutating func verifyModuleInterfaceJob(interfaceInput: TypedVirtualPath, emitModuleJob: Job, optIn: Bool) throws -> Job {
2935
var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) }
3036
var inputs: [TypedVirtualPath] = [interfaceInput]
@@ -40,15 +46,12 @@ extension Driver {
4046
outputs.append(TypedVirtualPath(file: outputPath, type: .diagnostics))
4147
}
4248

43-
if parsedOptions.contains(.driverExplicitModuleBuild) {
49+
if parsedOptions.contains(.driverExplicitModuleBuild) && supportExplicitModuleVerifyInterface() {
4450
commandLine.appendFlag("-explicit-interface-module-build")
4551
if let key = try computeCacheKeyForInterface(emitModuleJob: emitModuleJob, interfaceKind: interfaceInput.type) {
4652
commandLine.appendFlag("-input-file-key")
4753
commandLine.appendFlag(key)
4854
}
49-
// Need to create an output file for swiftmodule output. Currently put it next to the swift interface.
50-
let moduleOutPath = try interfaceInput.file.appendingToBaseName(".verified.swiftmodule")
51-
commandLine.appendFlags("-o", moduleOutPath.name)
5255
}
5356

5457
// TODO: remove this because we'd like module interface errors to fail the build.

Sources/SwiftDriver/SwiftScan/SwiftScan.swift

+4
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,10 @@ internal extension swiftscan_diagnostic_severity_t {
290290
api.swiftscan_clang_detail_get_module_cache_key != nil
291291
}
292292

293+
@_spi(Testing) public var supportsBridgingHeaderPCHCommand : Bool {
294+
return api.swiftscan_swift_textual_detail_get_bridging_pch_command_line != nil
295+
}
296+
293297
func serializeScannerCache(to path: AbsolutePath) {
294298
api.swiftscan_scanner_cache_serialize(scanner,
295299
path.description.cString(using: String.Encoding.utf8))

Sources/SwiftOptions/Options.swift

+4-2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ extension Option {
6262
public static let bypassBatchModeChecks: Option = Option("-bypass-batch-mode-checks", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Bypass checks for batch-mode errors.")
6363
public static let cacheCompileJob: Option = Option("-cache-compile-job", .flag, attributes: [.frontend], helpText: "Enable compiler caching")
6464
public static let cacheDisableReplay: Option = Option("-cache-disable-replay", .flag, attributes: [.frontend], helpText: "Skip loading the compilation result from cache")
65-
public static let cacheRemarks: Option = Option("-cache-remarks", .flag, attributes: [.frontend], helpText: "Show remarks for compiler caching")
6665
public static let candidateModuleFile: Option = Option("-candidate-module-file", .separate, attributes: [.helpHidden, .frontend, .noDriver], metaVar: "<path>", helpText: "Specify Swift module may be ready to use for an interface")
6766
public static let casFs: Option = Option("-cas-fs", .separate, attributes: [.helpHidden, .frontend, .noDriver], metaVar: "<cas-id>", helpText: "Root CASID for CAS FileSystem")
6867
public static let casPath: Option = Option("-cas-path", .separate, attributes: [.frontend], metaVar: "<path>", helpText: "Path to CAS")
@@ -518,6 +517,7 @@ extension Option {
518517
public static let indexSystemModules: Option = Option("-index-system-modules", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Emit index data for imported serialized swift system modules")
519518
public static let indexUnitOutputPathFilelist: Option = Option("-index-unit-output-path-filelist", .separate, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Specify index unit output paths in a file rather than on the command line")
520519
public static let indexUnitOutputPath: Option = Option("-index-unit-output-path", .separate, attributes: [.frontend, .argumentIsPath], metaVar: "<path>", helpText: "Use <path> as the output path in the produced index data.")
520+
public static let inputFileKey: Option = Option("-input-file-key", .separate, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Cache Key for input file")
521521
public static let inputPaths: Option = Option("-input-paths", .separate, attributes: [.noDriver, .argumentIsPath], metaVar: "<path>", helpText: "The SDK contents under comparison")
522522
public static let inputPaths_: Option = Option("--input-paths", .separate, alias: Option.inputPaths, attributes: [.noDriver, .argumentIsPath], metaVar: "<path>", helpText: "The SDK contents under comparison")
523523
public static let internalizeAtLink: Option = Option("-internalize-at-link", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Allow internalizing public symbols and vtables at link time (assume all client code of public types is part of the same link unit, or that external symbols are explicitly requested via -exported_symbols_list)")
@@ -638,6 +638,7 @@ extension Option {
638638
public static let publicAutolinkLibrary: Option = Option("-public-autolink-library", .separate, attributes: [.frontend, .noDriver], helpText: "Add public dependent library")
639639
public static let RaccessNoteEQ: Option = Option("-Raccess-note=", .joined, alias: Option.RaccessNote, attributes: [.frontend, .noDriver])
640640
public static let RaccessNote: Option = Option("-Raccess-note", .separate, attributes: [.frontend, .noDriver], metaVar: "none|failures|all|all-validate", helpText: "Control access note remarks (default: all)")
641+
public static let cacheRemarks: Option = Option("-Rcache-compile-job", .flag, attributes: [.frontend], helpText: "Show remarks for compiler caching")
641642
public static let emitCrossImportRemarks: Option = Option("-Rcross-import", .flag, attributes: [.frontend, .doesNotAffectIncrementalBuild], helpText: "Emit a remark if a cross-import of a module is triggered.")
642643
public static let dependencyScanCacheRemarks: Option = Option("-Rdependency-scan-cache", .flag, attributes: [.frontend, .noDriver], helpText: "Emit remarks indicating use of the serialized module dependency scanning cache.")
643644
public static let readLegacyTypeInfoPathEQ: Option = Option("-read-legacy-type-info-path=", .joined, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Read legacy type layout from the given path instead of default path")
@@ -854,7 +855,6 @@ extension Option {
854855
Option.bypassBatchModeChecks,
855856
Option.cacheCompileJob,
856857
Option.cacheDisableReplay,
857-
Option.cacheRemarks,
858858
Option.candidateModuleFile,
859859
Option.casFs,
860860
Option.casPath,
@@ -1310,6 +1310,7 @@ extension Option {
13101310
Option.indexSystemModules,
13111311
Option.indexUnitOutputPathFilelist,
13121312
Option.indexUnitOutputPath,
1313+
Option.inputFileKey,
13131314
Option.inputPaths,
13141315
Option.inputPaths_,
13151316
Option.internalizeAtLink,
@@ -1430,6 +1431,7 @@ extension Option {
14301431
Option.publicAutolinkLibrary,
14311432
Option.RaccessNoteEQ,
14321433
Option.RaccessNote,
1434+
Option.cacheRemarks,
14331435
Option.emitCrossImportRemarks,
14341436
Option.dependencyScanCacheRemarks,
14351437
Option.readLegacyTypeInfoPathEQ,

Tests/SwiftDriverTests/CachingBuildTests.swift

+92-1
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,21 @@ final class CachingBuildTests: XCTestCase {
302302
"-cache-compile-job", "-cas-path", casPath.nativePathString(escaped: true),
303303
main.nativePathString(escaped: true)] + sdkArgumentsForTesting)
304304

305+
guard driver.supportExplicitModuleVerifyInterface() else {
306+
throw XCTSkip("-typecheck-module-from-interface doesn't support explicit build.")
307+
}
308+
let dependencyOracle = InterModuleDependencyOracle()
309+
let scanLibPath = try XCTUnwrap(driver.toolchain.lookupSwiftScanLib())
310+
guard try dependencyOracle
311+
.verifyOrCreateScannerInstance(fileSystem: localFileSystem,
312+
swiftScanLibPath: scanLibPath) else {
313+
XCTFail("Dependency scanner library not found")
314+
return
315+
}
316+
guard try dependencyOracle.supportsCaching() else {
317+
throw XCTSkip("libSwiftScan does not support caching.")
318+
}
319+
305320
let jobs = try driver.planBuild()
306321
// Figure out which Triples to use.
307322
let dependencyGraph = try driver.gatherModuleDependencies()
@@ -425,7 +440,7 @@ final class CachingBuildTests: XCTestCase {
425440
var driver = try Driver(args: ["swiftc",
426441
"-I", cHeadersPath.nativePathString(escaped: true),
427442
"-I", swiftModuleInterfacesPath.nativePathString(escaped: true),
428-
"-explicit-module-build", "-v", "-cache-remarks",
443+
"-explicit-module-build", "-v", "-Rcache-compile-job",
429444
"-module-cache-path", moduleCachePath.nativePathString(escaped: true),
430445
"-cache-compile-job", "-cas-path", casPath.nativePathString(escaped: true),
431446
"-working-directory", path.nativePathString(escaped: true),
@@ -448,6 +463,82 @@ final class CachingBuildTests: XCTestCase {
448463
}
449464
}
450465

466+
func testCacheBuildEndToEndWithBinaryHeaderDeps() throws {
467+
try withTemporaryDirectory { path in
468+
try localFileSystem.changeCurrentWorkingDirectory(to: path)
469+
let moduleCachePath = path.appending(component: "ModuleCache")
470+
try localFileSystem.createDirectory(moduleCachePath)
471+
let PCHPath = path.appending(component: "PCH")
472+
try localFileSystem.createDirectory(PCHPath)
473+
let FooInstallPath = path.appending(component: "Foo")
474+
try localFileSystem.createDirectory(FooInstallPath)
475+
let foo = path.appending(component: "foo.swift")
476+
let casPath = path.appending(component: "cas")
477+
try localFileSystem.writeFileContents(foo) {
478+
$0 <<< "extension Profiler {"
479+
$0 <<< " public static let count: Int = 42"
480+
$0 <<< "}"
481+
}
482+
let fooHeader = path.appending(component: "foo.h")
483+
try localFileSystem.writeFileContents(fooHeader) {
484+
$0 <<< "struct Profiler { void* ptr; };"
485+
}
486+
let main = path.appending(component: "main.swift")
487+
try localFileSystem.writeFileContents(main) {
488+
$0 <<< "import Foo"
489+
}
490+
let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? []
491+
492+
var fooBuildDriver = try Driver(args: ["swiftc",
493+
"-explicit-module-build",
494+
"-module-cache-path", moduleCachePath.nativePathString(escaped: true),
495+
"-cache-compile-job", "-cas-path", casPath.nativePathString(escaped: true),
496+
"-working-directory", path.nativePathString(escaped: true),
497+
foo.nativePathString(escaped: true),
498+
"-emit-module", "-wmo", "-module-name", "Foo",
499+
"-emit-module-path", FooInstallPath.nativePathString(escaped: true),
500+
"-import-objc-header", fooHeader.nativePathString(escaped: true),
501+
"-pch-output-dir", PCHPath.nativePathString(escaped: true),
502+
FooInstallPath.appending(component: "Foo.swiftmodule").nativePathString(escaped: true)]
503+
+ sdkArgumentsForTesting,
504+
env: ProcessEnv.vars)
505+
506+
// Ensure this tooling supports this functionality
507+
let dependencyOracle = InterModuleDependencyOracle()
508+
let scanLibPath = try XCTUnwrap(fooBuildDriver.toolchain.lookupSwiftScanLib())
509+
guard try dependencyOracle
510+
.verifyOrCreateScannerInstance(fileSystem: localFileSystem,
511+
swiftScanLibPath: scanLibPath) else {
512+
XCTFail("Dependency scanner library not found")
513+
return
514+
}
515+
guard try dependencyOracle.supportsBinaryModuleHeaderDependencies() else {
516+
throw XCTSkip("libSwiftScan does not support binary module header dependencies.")
517+
}
518+
guard try dependencyOracle.supportsCaching() else {
519+
throw XCTSkip("libSwiftScan does not support caching.")
520+
}
521+
522+
let fooJobs = try fooBuildDriver.planBuild()
523+
try fooBuildDriver.run(jobs: fooJobs)
524+
XCTAssertFalse(fooBuildDriver.diagnosticEngine.hasErrors)
525+
526+
var driver = try Driver(args: ["swiftc",
527+
"-I", FooInstallPath.nativePathString(escaped: true),
528+
"-explicit-module-build", "-emit-module", "-emit-module-path",
529+
path.appending(component: "testEMBETEWBHD.swiftmodule").nativePathString(escaped: true),
530+
"-module-cache-path", moduleCachePath.nativePathString(escaped: true),
531+
"-cache-compile-job", "-cas-path", casPath.nativePathString(escaped: true),
532+
"-working-directory", path.nativePathString(escaped: true),
533+
main.nativePathString(escaped: true)] + sdkArgumentsForTesting,
534+
env: ProcessEnv.vars)
535+
// This is currently not supported.
536+
XCTAssertThrowsError(try driver.planBuild()) {
537+
XCTAssertEqual($0 as? Driver.Error, .unsupportedConfigurationForCaching("module Foo has prebuilt header dependency"))
538+
}
539+
}
540+
}
541+
451542
func testDependencyScanning() throws {
452543
// Create a simple test case.
453544
try withTemporaryDirectory { path in

Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift

+3
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,9 @@ final class ExplicitModuleBuildTests: XCTestCase {
405405
"-enable-library-evolution",
406406
main.nativePathString(escaped: true)] + sdkArgumentsForTesting)
407407

408+
guard driver.supportExplicitModuleVerifyInterface() else {
409+
throw XCTSkip("-typecheck-module-from-interface doesn't support explicit build.")
410+
}
408411
let jobs = try driver.planBuild()
409412
// Figure out which Triples to use.
410413
let dependencyGraph = try driver.gatherModuleDependencies()

0 commit comments

Comments
 (0)