Skip to content

Commit 977c2ad

Browse files
authored
Merge pull request #1543 from xymus/verify-swiftinterfaces-by-default
Verify emitted module interfaces by default and introduce blocklist
2 parents 7bf84e3 + 72a952d commit 977c2ad

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
@@ -547,18 +547,14 @@ extension Driver {
547547

548548
private mutating func addVerifyJobs(emitModuleJob: Job, addJob: (Job) -> Void )
549549
throws {
550-
// Turn this flag on by default with the env var or for public frameworks.
551-
let onByDefault = env["ENABLE_DEFAULT_INTERFACE_VERIFIER"] != nil ||
552-
parsedOptions.getLastArgument(.libraryLevel)?.asSingle == "api"
553-
554550
guard
555551
// Only verify modules with library evolution.
556552
parsedOptions.hasArgument(.enableLibraryEvolution),
557553

558554
// Only verify when requested, on by default and not disabled.
559555
parsedOptions.hasFlag(positive: .verifyEmittedModuleInterface,
560556
negative: .noVerifyEmittedModuleInterface,
561-
default: onByDefault),
557+
default: true),
562558

563559
// Don't verify by default modules emitted from a merge-module job
564560
// as it's more likely to be invalid.
@@ -568,8 +564,25 @@ extension Driver {
568564
default: false)
569565
else { return }
570566

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

574587
enum InterfaceMode {
575588
case Public, Private, Package
@@ -598,7 +611,7 @@ extension Driver {
598611
"Merge module job should only have one swiftinterface output")
599612
let job = try verifyModuleInterfaceJob(interfaceInput: mergeInterfaceOutputs[0],
600613
emitModuleJob: emitModuleJob,
601-
optIn: optIn)
614+
reportAsError: reportAsError)
602615
addJob(job)
603616
}
604617
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
@@ -2881,8 +2881,9 @@ final class SwiftDriverTests: XCTestCase {
28812881
"-enable-library-evolution"])
28822882

28832883
let plannedJobs = try driver1.planBuild()
2884-
XCTAssertEqual(plannedJobs.count, 2)
2885-
let emitInterfaceJob = plannedJobs[0]
2884+
XCTAssertEqual(plannedJobs.count, 3)
2885+
2886+
let emitInterfaceJob = try plannedJobs.findJob(.emitModule)
28862887
XCTAssertTrue(emitInterfaceJob.commandLine.contains(.flag("-emit-module-interface-path")))
28872888
XCTAssertTrue(emitInterfaceJob.commandLine.contains(.flag("-emit-private-module-interface-path")))
28882889
}
@@ -5585,23 +5586,31 @@ final class SwiftDriverTests: XCTestCase {
55855586
func testVerifyEmittedInterfaceJob() throws {
55865587
// Evolution enabled
55875588
var envVars = ProcessEnv.vars
5588-
envVars["ENABLE_DEFAULT_INTERFACE_VERIFIER"] = "YES"
55895589
do {
55905590
var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", "-module-name",
55915591
"foo", "-emit-module-interface",
5592+
"-emit-private-module-interface-path", "foo.private.swiftinterface",
55925593
"-verify-emitted-module-interface",
55935594
"-enable-library-evolution"])
55945595
let plannedJobs = try driver.planBuild()
5595-
XCTAssertEqual(plannedJobs.count, 3)
5596+
XCTAssertEqual(plannedJobs.count, 4)
5597+
5598+
// Emit-module should emit both module interface files
55965599
let emitJob = try plannedJobs.findJob(.emitModule)
5597-
let verifyJob = try plannedJobs.findJob(.verifyModuleInterface)
5598-
let mergeInterfaceOutputs = emitJob.outputs.filter { $0.type == .swiftInterface }
5599-
XCTAssertTrue(mergeInterfaceOutputs.count == 1,
5600-
"Merge module job should only have one swiftinterface output")
5601-
XCTAssertTrue(verifyJob.inputs.count == 1)
5602-
XCTAssertTrue(verifyJob.inputs[0] == mergeInterfaceOutputs[0])
5603-
XCTAssertTrue(verifyJob.outputs.isEmpty)
5604-
XCTAssertTrue(verifyJob.commandLine.contains(.path(mergeInterfaceOutputs[0].file)))
5600+
let publicModuleInterface = emitJob.outputs.filter { $0.type == .swiftInterface }
5601+
XCTAssertEqual(publicModuleInterface.count, 1)
5602+
let privateModuleInterface = emitJob.outputs.filter { $0.type == .privateSwiftInterface }
5603+
XCTAssertEqual(privateModuleInterface.count, 1)
5604+
5605+
// Each verify job should either check the public or the private module interface, not both.
5606+
let verifyJobs = plannedJobs.filter { $0.kind == .verifyModuleInterface }
5607+
XCTAssertEqual(verifyJobs.count, 2)
5608+
for verifyJob in verifyJobs {
5609+
let publicVerify = verifyJob.inputs.contains(try XCTUnwrap(publicModuleInterface.first))
5610+
let privateVerify = verifyJob.inputs.contains(try XCTUnwrap(privateModuleInterface.first))
5611+
XCTAssertNotEqual(publicVerify, privateVerify)
5612+
XCTAssertFalse(verifyJob.commandLine.contains("-downgrade-typecheck-interface-error"))
5613+
}
56055614
}
56065615

56075616
// No Evolution
@@ -5610,6 +5619,7 @@ final class SwiftDriverTests: XCTestCase {
56105619
"foo", "-emit-module-interface", "-verify-emitted-module-interface"], env: envVars)
56115620
let plannedJobs = try driver.planBuild()
56125621
XCTAssertEqual(plannedJobs.count, 2)
5622+
XCTAssertFalse(plannedJobs.containsJob(.verifyModuleInterface))
56135623
}
56145624

56155625
// Explicitly disabled
@@ -5620,6 +5630,7 @@ final class SwiftDriverTests: XCTestCase {
56205630
"-no-verify-emitted-module-interface"], env: envVars)
56215631
let plannedJobs = try driver.planBuild()
56225632
XCTAssertEqual(plannedJobs.count, 2)
5633+
XCTAssertFalse(plannedJobs.containsJob(.verifyModuleInterface))
56235634
let emitJob = try plannedJobs.findJob(.emitModule)
56245635
XCTAssertTrue(emitJob.commandLine.contains("-no-verify-emitted-module-interface"))
56255636
}
@@ -5632,16 +5643,7 @@ final class SwiftDriverTests: XCTestCase {
56325643
"-no-emit-module-separately"], env: envVars)
56335644
let plannedJobs = try driver.planBuild()
56345645
XCTAssertEqual(plannedJobs.count, 2)
5635-
}
5636-
5637-
// Disabled when no "ENABLE_DEFAULT_INTERFACE_VERIFIER" found in the environment
5638-
do {
5639-
var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", "-module-name",
5640-
"foo", "-emit-module-interface",
5641-
"-enable-library-evolution",
5642-
"-experimental-emit-module-separately"])
5643-
let plannedJobs = try driver.planBuild()
5644-
XCTAssertEqual(plannedJobs.count, 2)
5646+
XCTAssertFalse(plannedJobs.containsJob(.verifyModuleInterface))
56455647
}
56465648

56475649
// Emit-module separately
@@ -5660,6 +5662,7 @@ final class SwiftDriverTests: XCTestCase {
56605662
XCTAssertTrue(verifyJob.inputs.count == 1)
56615663
XCTAssertTrue(verifyJob.inputs[0] == emitInterfaceOutput[0])
56625664
XCTAssertTrue(verifyJob.commandLine.contains(.path(emitInterfaceOutput[0].file)))
5665+
XCTAssertFalse(verifyJob.commandLine.contains("-downgrade-typecheck-interface-error"))
56635666
XCTAssertFalse(emitJob.commandLine.contains("-no-verify-emitted-module-interface"))
56645667
XCTAssertFalse(emitJob.commandLine.contains("-verify-emitted-module-interface"))
56655668
}
@@ -5682,6 +5685,7 @@ final class SwiftDriverTests: XCTestCase {
56825685
XCTAssertTrue(verifyJob.inputs.count == 1)
56835686
XCTAssertTrue(verifyJob.inputs[0] == emitInterfaceOutput[0])
56845687
XCTAssertTrue(verifyJob.commandLine.contains(.path(emitInterfaceOutput[0].file)))
5688+
XCTAssertFalse(verifyJob.commandLine.contains("-downgrade-typecheck-interface-error"))
56855689
}
56865690

56875691
// Test the `-no-verify-emitted-module-interface` flag with whole-module
@@ -5706,21 +5710,63 @@ final class SwiftDriverTests: XCTestCase {
57065710
"-library-level", "api"])
57075711
let plannedJobs = try driver.planBuild()
57085712
XCTAssertEqual(plannedJobs.count, 2)
5709-
XCTAssertTrue(plannedJobs.containsJob(.verifyModuleInterface))
5713+
let verifyJob = try plannedJobs.findJob(.verifyModuleInterface)
5714+
XCTAssertFalse(verifyJob.commandLine.contains("-downgrade-typecheck-interface-error"))
57105715
}
57115716

5712-
// Not enabled by default when the library-level is spi.
5717+
// Enabled by default when the library-level is spi.
57135718
do {
57145719
var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", "-module-name",
57155720
"foo", "-emit-module-interface",
57165721
"-enable-library-evolution",
57175722
"-whole-module-optimization",
57185723
"-library-level", "spi"])
57195724
let plannedJobs = try driver.planBuild()
5720-
XCTAssertEqual(plannedJobs.count, 1)
5721-
XCTAssertEqual(plannedJobs[0].kind, .compile)
5722-
let compileJob = try plannedJobs.findJob(.compile)
5723-
XCTAssertFalse(compileJob.commandLine.contains("-no-verify-emitted-module-interface"))
5725+
XCTAssertEqual(plannedJobs.count, 2)
5726+
let verifyJob = try plannedJobs.findJob(.verifyModuleInterface)
5727+
XCTAssertFalse(verifyJob.commandLine.contains("-downgrade-typecheck-interface-error"))
5728+
}
5729+
5730+
// Errors downgraded to a warning when a module is blocklisted.
5731+
try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-emit-module", "-module-name",
5732+
"TestBlocklistedModule", "-emit-module-interface",
5733+
"-enable-library-evolution",
5734+
"-whole-module-optimization",
5735+
"-library-level", "api"]) { driver, verify in
5736+
let plannedJobs = try driver.planBuild()
5737+
XCTAssertEqual(plannedJobs.count, 2)
5738+
let verifyJob = try plannedJobs.findJob(.verifyModuleInterface)
5739+
if driver.isFrontendArgSupported(.downgradeTypecheckInterfaceError) {
5740+
XCTAssertTrue(verifyJob.commandLine.contains("-downgrade-typecheck-interface-error"))
5741+
}
5742+
5743+
verify.expect(.remark("Verification of module interfaces for 'TestBlocklistedModule' set to warning only by blocklist"))
5744+
}
5745+
5746+
// Don't downgrade to error blocklisted modules when the env var is set.
5747+
do {
5748+
envVars["ENABLE_DEFAULT_INTERFACE_VERIFIER"] = "YES"
5749+
var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", "-module-name",
5750+
"TestBlocklistedModule", "-emit-module-interface",
5751+
"-enable-library-evolution",
5752+
"-whole-module-optimization"], env: envVars)
5753+
let plannedJobs = try driver.planBuild()
5754+
XCTAssertEqual(plannedJobs.count, 2)
5755+
let verifyJob = try plannedJobs.findJob(.verifyModuleInterface)
5756+
XCTAssertFalse(verifyJob.commandLine.contains("-downgrade-typecheck-interface-error"))
5757+
}
5758+
5759+
// Don't downgrade to error blocklisted modules if the verify flag is set.
5760+
do {
5761+
var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", "-module-name",
5762+
"TestBlocklistedModule", "-emit-module-interface",
5763+
"-enable-library-evolution",
5764+
"-whole-module-optimization",
5765+
"-verify-emitted-module-interface"])
5766+
let plannedJobs = try driver.planBuild()
5767+
XCTAssertEqual(plannedJobs.count, 2)
5768+
let verifyJob = try plannedJobs.findJob(.verifyModuleInterface)
5769+
XCTAssertFalse(verifyJob.commandLine.contains("-downgrade-typecheck-interface-error"))
57245770
}
57255771

57265772
// The flag -check-api-availability-only is not passed down to the verify job.
@@ -5752,9 +5798,6 @@ final class SwiftDriverTests: XCTestCase {
57525798
}
57535799

57545800
func testVerifyEmittedPackageInterface() throws {
5755-
var envVars = ProcessEnv.vars
5756-
envVars["ENABLE_DEFAULT_INTERFACE_VERIFIER"] = "YES"
5757-
57585801
// Evolution enabled
57595802
do {
57605803
var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module",
@@ -5763,11 +5806,9 @@ final class SwiftDriverTests: XCTestCase {
57635806
"-emit-module-interface",
57645807
"-emit-package-module-interface-path", "foo.package.swiftinterface",
57655808
"-verify-emitted-module-interface",
5766-
"-enable-library-evolution"], env: envVars)
5809+
"-enable-library-evolution"])
57675810

57685811
let plannedJobs = try driver.planBuild()
5769-
let x = plannedJobs.first?.commandLine.joinedUnresolvedArguments ?? ""
5770-
print(x)
57715812
XCTAssertEqual(plannedJobs.count, 4)
57725813
let emitJob = try plannedJobs.findJob(.emitModule)
57735814
let verifyJob = try plannedJobs.findJob(.verifyModuleInterface)
@@ -5790,7 +5831,7 @@ final class SwiftDriverTests: XCTestCase {
57905831
"-emit-module-interface",
57915832
"-emit-package-module-interface-path", "foo.package.swiftinterface",
57925833
"-enable-library-evolution",
5793-
"-no-verify-emitted-module-interface"], env: envVars)
5834+
"-no-verify-emitted-module-interface"])
57945835
let plannedJobs = try driver.planBuild()
57955836
XCTAssertEqual(plannedJobs.count, 2)
57965837
}
@@ -5803,7 +5844,7 @@ final class SwiftDriverTests: XCTestCase {
58035844
"-emit-module-interface",
58045845
"-emit-package-module-interface-path", "foo.package.swiftinterface",
58055846
"-enable-library-evolution",
5806-
"-experimental-emit-module-separately"], env: envVars)
5847+
"-experimental-emit-module-separately"])
58075848
let plannedJobs = try driver.planBuild()
58085849
XCTAssertEqual(plannedJobs.count, 4)
58095850
let emitJob = try plannedJobs.findJob(.emitModule)

0 commit comments

Comments
 (0)