Skip to content

Commit 72a952d

Browse files
committed
Verify emitted module interfaces by default and introduce blocklist
Verify the emitted modules interfaces automatically unless opted out by the `-no-verify-emitted-module-interface` flag or a mention of the module by name on the blocklist. Blocklisted modules are noted by a remark and can still be verified by locally setting `-verify-emitted-module-interface` or defining the env var `ENABLE_DEFAULT_INTERFACE_VERIFIER`.
1 parent 49daa7e commit 72a952d

File tree

3 files changed

+100
-46
lines changed

3 files changed

+100
-46
lines changed

Sources/SwiftDriver/Jobs/Planning.swift

+21-8
Original file line numberDiff line numberDiff line change
@@ -550,18 +550,14 @@ extension Driver {
550550

551551
private mutating func addVerifyJobs(emitModuleJob: Job, addJob: (Job) -> Void )
552552
throws {
553-
// Turn this flag on by default with the env var or for public frameworks.
554-
let onByDefault = env["ENABLE_DEFAULT_INTERFACE_VERIFIER"] != nil ||
555-
parsedOptions.getLastArgument(.libraryLevel)?.asSingle == "api"
556-
557553
guard
558554
// Only verify modules with library evolution.
559555
parsedOptions.hasArgument(.enableLibraryEvolution),
560556

561557
// Only verify when requested, on by default and not disabled.
562558
parsedOptions.hasFlag(positive: .verifyEmittedModuleInterface,
563559
negative: .noVerifyEmittedModuleInterface,
564-
default: onByDefault),
560+
default: true),
565561

566562
// Don't verify by default modules emitted from a merge-module job
567563
// as it's more likely to be invalid.
@@ -571,8 +567,25 @@ extension Driver {
571567
default: false)
572568
else { return }
573569

574-
let optIn = env["ENABLE_DEFAULT_INTERFACE_VERIFIER"] != nil ||
575-
parsedOptions.hasArgument(.verifyEmittedModuleInterface)
570+
// Downgrade errors to a warning for modules expected to fail this check.
571+
var knownFailingModules: Set = ["TestBlocklistedModule"]
572+
knownFailingModules = knownFailingModules.union(
573+
Driver.getAllConfiguredModules(withKey: "SkipModuleInterfaceVerify",
574+
getAdopterConfigsFromXcodeDefaultToolchain()))
575+
576+
let moduleName = parsedOptions.getLastArgument(.moduleName)?.asSingle
577+
let reportAsError = !knownFailingModules.contains(moduleName ?? "") ||
578+
env["ENABLE_DEFAULT_INTERFACE_VERIFIER"] != nil ||
579+
parsedOptions.hasFlag(positive: .verifyEmittedModuleInterface,
580+
negative: .noVerifyEmittedModuleInterface,
581+
default: false)
582+
583+
if !reportAsError {
584+
diagnosticEngine
585+
.emit(
586+
.remark(
587+
"Verification of module interfaces for '\(moduleName ?? "No module name")' set to warning only by blocklist"))
588+
}
576589

577590
enum InterfaceMode {
578591
case Public, Private, Package
@@ -601,7 +614,7 @@ extension Driver {
601614
"Merge module job should only have one swiftinterface output")
602615
let job = try verifyModuleInterfaceJob(interfaceInput: mergeInterfaceOutputs[0],
603616
emitModuleJob: emitModuleJob,
604-
optIn: optIn)
617+
reportAsError: reportAsError)
605618
addJob(job)
606619
}
607620
try addVerifyJob(for: .Public)

Sources/SwiftDriver/Jobs/VerifyModuleInterfaceJob.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ extension Driver {
3030
return isFrontendArgSupported(.inputFileKey)
3131
}
3232

33-
mutating func verifyModuleInterfaceJob(interfaceInput: TypedVirtualPath, emitModuleJob: Job, optIn: Bool) throws -> Job {
33+
mutating func verifyModuleInterfaceJob(interfaceInput: TypedVirtualPath, emitModuleJob: Job, reportAsError: Bool) throws -> Job {
3434
var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) }
3535
var inputs: [TypedVirtualPath] = [interfaceInput]
3636
commandLine.appendFlags("-frontend", "-typecheck-module-from-interface")
@@ -55,7 +55,7 @@ extension Driver {
5555

5656
// TODO: remove this because we'd like module interface errors to fail the build.
5757
if isFrontendArgSupported(.downgradeTypecheckInterfaceError) &&
58-
(!optIn ||
58+
(!reportAsError ||
5959
// package interface is new and should not be a blocker for now
6060
interfaceInput.type == .packageSwiftInterface) {
6161
commandLine.appendFlag(.downgradeTypecheckInterfaceError)

Tests/SwiftDriverTests/SwiftDriverTests.swift

+77-36
Original file line numberDiff line numberDiff line change
@@ -2855,8 +2855,9 @@ final class SwiftDriverTests: XCTestCase {
28552855
"-enable-library-evolution"])
28562856

28572857
let plannedJobs = try driver1.planBuild()
2858-
XCTAssertEqual(plannedJobs.count, 2)
2859-
let emitInterfaceJob = plannedJobs[0]
2858+
XCTAssertEqual(plannedJobs.count, 3)
2859+
2860+
let emitInterfaceJob = try plannedJobs.findJob(.emitModule)
28602861
XCTAssertTrue(emitInterfaceJob.commandLine.contains(.flag("-emit-module-interface-path")))
28612862
XCTAssertTrue(emitInterfaceJob.commandLine.contains(.flag("-emit-private-module-interface-path")))
28622863
}
@@ -5559,23 +5560,31 @@ final class SwiftDriverTests: XCTestCase {
55595560
func testVerifyEmittedInterfaceJob() throws {
55605561
// Evolution enabled
55615562
var envVars = ProcessEnv.vars
5562-
envVars["ENABLE_DEFAULT_INTERFACE_VERIFIER"] = "YES"
55635563
do {
55645564
var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", "-module-name",
55655565
"foo", "-emit-module-interface",
5566+
"-emit-private-module-interface-path", "foo.private.swiftinterface",
55665567
"-verify-emitted-module-interface",
55675568
"-enable-library-evolution"])
55685569
let plannedJobs = try driver.planBuild()
5569-
XCTAssertEqual(plannedJobs.count, 3)
5570+
XCTAssertEqual(plannedJobs.count, 4)
5571+
5572+
// Emit-module should emit both module interface files
55705573
let emitJob = try plannedJobs.findJob(.emitModule)
5571-
let verifyJob = try plannedJobs.findJob(.verifyModuleInterface)
5572-
let mergeInterfaceOutputs = emitJob.outputs.filter { $0.type == .swiftInterface }
5573-
XCTAssertTrue(mergeInterfaceOutputs.count == 1,
5574-
"Merge module job should only have one swiftinterface output")
5575-
XCTAssertTrue(verifyJob.inputs.count == 1)
5576-
XCTAssertTrue(verifyJob.inputs[0] == mergeInterfaceOutputs[0])
5577-
XCTAssertTrue(verifyJob.outputs.isEmpty)
5578-
XCTAssertTrue(verifyJob.commandLine.contains(.path(mergeInterfaceOutputs[0].file)))
5574+
let publicModuleInterface = emitJob.outputs.filter { $0.type == .swiftInterface }
5575+
XCTAssertEqual(publicModuleInterface.count, 1)
5576+
let privateModuleInterface = emitJob.outputs.filter { $0.type == .privateSwiftInterface }
5577+
XCTAssertEqual(privateModuleInterface.count, 1)
5578+
5579+
// Each verify job should either check the public or the private module interface, not both.
5580+
let verifyJobs = plannedJobs.filter { $0.kind == .verifyModuleInterface }
5581+
XCTAssertEqual(verifyJobs.count, 2)
5582+
for verifyJob in verifyJobs {
5583+
let publicVerify = verifyJob.inputs.contains(try XCTUnwrap(publicModuleInterface.first))
5584+
let privateVerify = verifyJob.inputs.contains(try XCTUnwrap(privateModuleInterface.first))
5585+
XCTAssertNotEqual(publicVerify, privateVerify)
5586+
XCTAssertFalse(verifyJob.commandLine.contains("-downgrade-typecheck-interface-error"))
5587+
}
55795588
}
55805589

55815590
// No Evolution
@@ -5584,6 +5593,7 @@ final class SwiftDriverTests: XCTestCase {
55845593
"foo", "-emit-module-interface", "-verify-emitted-module-interface"], env: envVars)
55855594
let plannedJobs = try driver.planBuild()
55865595
XCTAssertEqual(plannedJobs.count, 2)
5596+
XCTAssertFalse(plannedJobs.containsJob(.verifyModuleInterface))
55875597
}
55885598

55895599
// Explicitly disabled
@@ -5594,6 +5604,7 @@ final class SwiftDriverTests: XCTestCase {
55945604
"-no-verify-emitted-module-interface"], env: envVars)
55955605
let plannedJobs = try driver.planBuild()
55965606
XCTAssertEqual(plannedJobs.count, 2)
5607+
XCTAssertFalse(plannedJobs.containsJob(.verifyModuleInterface))
55975608
let emitJob = try plannedJobs.findJob(.emitModule)
55985609
XCTAssertTrue(emitJob.commandLine.contains("-no-verify-emitted-module-interface"))
55995610
}
@@ -5606,16 +5617,7 @@ final class SwiftDriverTests: XCTestCase {
56065617
"-no-emit-module-separately"], env: envVars)
56075618
let plannedJobs = try driver.planBuild()
56085619
XCTAssertEqual(plannedJobs.count, 2)
5609-
}
5610-
5611-
// Disabled when no "ENABLE_DEFAULT_INTERFACE_VERIFIER" found in the environment
5612-
do {
5613-
var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", "-module-name",
5614-
"foo", "-emit-module-interface",
5615-
"-enable-library-evolution",
5616-
"-experimental-emit-module-separately"])
5617-
let plannedJobs = try driver.planBuild()
5618-
XCTAssertEqual(plannedJobs.count, 2)
5620+
XCTAssertFalse(plannedJobs.containsJob(.verifyModuleInterface))
56195621
}
56205622

56215623
// Emit-module separately
@@ -5634,6 +5636,7 @@ final class SwiftDriverTests: XCTestCase {
56345636
XCTAssertTrue(verifyJob.inputs.count == 1)
56355637
XCTAssertTrue(verifyJob.inputs[0] == emitInterfaceOutput[0])
56365638
XCTAssertTrue(verifyJob.commandLine.contains(.path(emitInterfaceOutput[0].file)))
5639+
XCTAssertFalse(verifyJob.commandLine.contains("-downgrade-typecheck-interface-error"))
56375640
XCTAssertFalse(emitJob.commandLine.contains("-no-verify-emitted-module-interface"))
56385641
XCTAssertFalse(emitJob.commandLine.contains("-verify-emitted-module-interface"))
56395642
}
@@ -5656,6 +5659,7 @@ final class SwiftDriverTests: XCTestCase {
56565659
XCTAssertTrue(verifyJob.inputs.count == 1)
56575660
XCTAssertTrue(verifyJob.inputs[0] == emitInterfaceOutput[0])
56585661
XCTAssertTrue(verifyJob.commandLine.contains(.path(emitInterfaceOutput[0].file)))
5662+
XCTAssertFalse(verifyJob.commandLine.contains("-downgrade-typecheck-interface-error"))
56595663
}
56605664

56615665
// Test the `-no-verify-emitted-module-interface` flag with whole-module
@@ -5680,21 +5684,63 @@ final class SwiftDriverTests: XCTestCase {
56805684
"-library-level", "api"])
56815685
let plannedJobs = try driver.planBuild()
56825686
XCTAssertEqual(plannedJobs.count, 2)
5683-
XCTAssertTrue(plannedJobs.containsJob(.verifyModuleInterface))
5687+
let verifyJob = try plannedJobs.findJob(.verifyModuleInterface)
5688+
XCTAssertFalse(verifyJob.commandLine.contains("-downgrade-typecheck-interface-error"))
56845689
}
56855690

5686-
// Not enabled by default when the library-level is spi.
5691+
// Enabled by default when the library-level is spi.
56875692
do {
56885693
var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", "-module-name",
56895694
"foo", "-emit-module-interface",
56905695
"-enable-library-evolution",
56915696
"-whole-module-optimization",
56925697
"-library-level", "spi"])
56935698
let plannedJobs = try driver.planBuild()
5694-
XCTAssertEqual(plannedJobs.count, 1)
5695-
XCTAssertEqual(plannedJobs[0].kind, .compile)
5696-
let compileJob = try plannedJobs.findJob(.compile)
5697-
XCTAssertFalse(compileJob.commandLine.contains("-no-verify-emitted-module-interface"))
5699+
XCTAssertEqual(plannedJobs.count, 2)
5700+
let verifyJob = try plannedJobs.findJob(.verifyModuleInterface)
5701+
XCTAssertFalse(verifyJob.commandLine.contains("-downgrade-typecheck-interface-error"))
5702+
}
5703+
5704+
// Errors downgraded to a warning when a module is blocklisted.
5705+
try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-emit-module", "-module-name",
5706+
"TestBlocklistedModule", "-emit-module-interface",
5707+
"-enable-library-evolution",
5708+
"-whole-module-optimization",
5709+
"-library-level", "api"]) { driver, verify in
5710+
let plannedJobs = try driver.planBuild()
5711+
XCTAssertEqual(plannedJobs.count, 2)
5712+
let verifyJob = try plannedJobs.findJob(.verifyModuleInterface)
5713+
if driver.isFrontendArgSupported(.downgradeTypecheckInterfaceError) {
5714+
XCTAssertTrue(verifyJob.commandLine.contains("-downgrade-typecheck-interface-error"))
5715+
}
5716+
5717+
verify.expect(.remark("Verification of module interfaces for 'TestBlocklistedModule' set to warning only by blocklist"))
5718+
}
5719+
5720+
// Don't downgrade to error blocklisted modules when the env var is set.
5721+
do {
5722+
envVars["ENABLE_DEFAULT_INTERFACE_VERIFIER"] = "YES"
5723+
var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", "-module-name",
5724+
"TestBlocklistedModule", "-emit-module-interface",
5725+
"-enable-library-evolution",
5726+
"-whole-module-optimization"], env: envVars)
5727+
let plannedJobs = try driver.planBuild()
5728+
XCTAssertEqual(plannedJobs.count, 2)
5729+
let verifyJob = try plannedJobs.findJob(.verifyModuleInterface)
5730+
XCTAssertFalse(verifyJob.commandLine.contains("-downgrade-typecheck-interface-error"))
5731+
}
5732+
5733+
// Don't downgrade to error blocklisted modules if the verify flag is set.
5734+
do {
5735+
var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", "-module-name",
5736+
"TestBlocklistedModule", "-emit-module-interface",
5737+
"-enable-library-evolution",
5738+
"-whole-module-optimization",
5739+
"-verify-emitted-module-interface"])
5740+
let plannedJobs = try driver.planBuild()
5741+
XCTAssertEqual(plannedJobs.count, 2)
5742+
let verifyJob = try plannedJobs.findJob(.verifyModuleInterface)
5743+
XCTAssertFalse(verifyJob.commandLine.contains("-downgrade-typecheck-interface-error"))
56985744
}
56995745

57005746
// The flag -check-api-availability-only is not passed down to the verify job.
@@ -5726,9 +5772,6 @@ final class SwiftDriverTests: XCTestCase {
57265772
}
57275773

57285774
func testVerifyEmittedPackageInterface() throws {
5729-
var envVars = ProcessEnv.vars
5730-
envVars["ENABLE_DEFAULT_INTERFACE_VERIFIER"] = "YES"
5731-
57325775
// Evolution enabled
57335776
do {
57345777
var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module",
@@ -5737,11 +5780,9 @@ final class SwiftDriverTests: XCTestCase {
57375780
"-emit-module-interface",
57385781
"-emit-package-module-interface-path", "foo.package.swiftinterface",
57395782
"-verify-emitted-module-interface",
5740-
"-enable-library-evolution"], env: envVars)
5783+
"-enable-library-evolution"])
57415784

57425785
let plannedJobs = try driver.planBuild()
5743-
let x = plannedJobs.first?.commandLine.joinedUnresolvedArguments ?? ""
5744-
print(x)
57455786
XCTAssertEqual(plannedJobs.count, 4)
57465787
let emitJob = try plannedJobs.findJob(.emitModule)
57475788
let verifyJob = try plannedJobs.findJob(.verifyModuleInterface)
@@ -5764,7 +5805,7 @@ final class SwiftDriverTests: XCTestCase {
57645805
"-emit-module-interface",
57655806
"-emit-package-module-interface-path", "foo.package.swiftinterface",
57665807
"-enable-library-evolution",
5767-
"-no-verify-emitted-module-interface"], env: envVars)
5808+
"-no-verify-emitted-module-interface"])
57685809
let plannedJobs = try driver.planBuild()
57695810
XCTAssertEqual(plannedJobs.count, 2)
57705811
}
@@ -5777,7 +5818,7 @@ final class SwiftDriverTests: XCTestCase {
57775818
"-emit-module-interface",
57785819
"-emit-package-module-interface-path", "foo.package.swiftinterface",
57795820
"-enable-library-evolution",
5780-
"-experimental-emit-module-separately"], env: envVars)
5821+
"-experimental-emit-module-separately"])
57815822
let plannedJobs = try driver.planBuild()
57825823
XCTAssertEqual(plannedJobs.count, 4)
57835824
let emitJob = try plannedJobs.findJob(.emitModule)

0 commit comments

Comments
 (0)