From 949530c3be41556ac50b03d80e659ba0dd9cb9d9 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 14 Nov 2023 17:18:19 -0800 Subject: [PATCH] Support package interface generation. Generate .package.swiftinterface if -package-name is passed. Add package interface to Module Verification step. Support -experimental-package-interface-load for frontend. Downgrade typecheck interface error for package.swiftinterface. Resolves rdar://118469253 --- Sources/SwiftDriver/Driver/Driver.swift | 29 +++- Sources/SwiftDriver/Jobs/CompileJob.swift | 4 +- Sources/SwiftDriver/Jobs/EmitModuleJob.swift | 3 + .../SwiftDriver/Jobs/FrontendJobHelpers.swift | 8 + Sources/SwiftDriver/Jobs/Planning.swift | 33 +++-- .../SwiftDriver/Jobs/PrebuiltModulesJob.swift | 2 + .../Jobs/VerifyModuleInterfaceJob.swift | 9 +- Sources/SwiftDriver/Utilities/FileType.swift | 16 +- Sources/SwiftOptions/Options.swift | 4 + .../ExplicitModuleBuildTests.swift | 2 + Tests/SwiftDriverTests/SwiftDriverTests.swift | 137 +++++++++++++++++- 11 files changed, 227 insertions(+), 20 deletions(-) diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index e6a5eee6e..f9d9473e6 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -374,6 +374,9 @@ public struct Driver { /// Path to the Swift private interface file. let swiftPrivateInterfacePath: VirtualPath.Handle? + /// Path to the Swift package interface file. + let swiftPackageInterfacePath: VirtualPath.Handle? + /// File type for the optimization record. let optimizationRecordFileType: FileType? @@ -964,6 +967,14 @@ public struct Driver { emitModuleSeparately: emitModuleSeparately, outputFileMap: self.outputFileMap, moduleName: moduleOutputInfo.name) + let givenPackageInterfacePath = try Self.computeSupplementaryOutputPath( + &parsedOptions, type: .packageSwiftInterface, isOutputOptions: [], + outputPath: .emitPackageModuleInterfacePath, + compilerOutputType: compilerOutputType, + compilerMode: compilerMode, + emitModuleSeparately: emitModuleSeparately, + outputFileMap: self.outputFileMap, + moduleName: moduleOutputInfo.name) // Always emitting private swift interfaces if public interfaces are emitted.' // With the introduction of features like @_spi_available, we may print public @@ -978,6 +989,22 @@ public struct Driver { self.swiftPrivateInterfacePath = givenPrivateInterfacePath } + if let packageNameInput = parsedOptions.getLastArgument(Option.packageName), + !packageNameInput.asSingle.isEmpty { + // Generate a package interface if built with `-package-name` required for decls + // with the `package` access level. The .package.swiftinterface contains package + // decls as well as SPI and public decls (superset of a private interface). + if let publicInterfacePath = self.swiftInterfacePath, + givenPackageInterfacePath == nil { + self.swiftPackageInterfacePath = try VirtualPath.lookup(publicInterfacePath) + .replacingExtension(with: .packageSwiftInterface).intern() + } else { + self.swiftPackageInterfacePath = givenPackageInterfacePath + } + } else { + self.swiftPackageInterfacePath = nil + } + var optimizationRecordFileType = FileType.yamlOptimizationRecord if let argument = parsedOptions.getLastArgument(.saveOptimizationRecordEQ)?.asSingle { switch argument { @@ -2546,7 +2573,7 @@ extension Driver { moduleOutputKind = .auxiliary } else if parsedOptions.hasArgument(.emitObjcHeader, .emitObjcHeaderPath, .emitModuleInterface, .emitModuleInterfacePath, - .emitPrivateModuleInterfacePath) { + .emitPrivateModuleInterfacePath, .emitPackageModuleInterfacePath) { // An option has been passed which requires whole-module knowledge, but we // don't have that. Generate a module, but treat it as an intermediate // output. diff --git a/Sources/SwiftDriver/Jobs/CompileJob.swift b/Sources/SwiftDriver/Jobs/CompileJob.swift index c8e68efe5..54540eed1 100644 --- a/Sources/SwiftDriver/Jobs/CompileJob.swift +++ b/Sources/SwiftDriver/Jobs/CompileJob.swift @@ -92,7 +92,7 @@ extension Driver { case .swiftModule: return compilerMode.isSingleCompilation && moduleOutputInfo.output?.isTopLevel ?? false case .swift, .image, .dSYM, .dependencies, .emitModuleDependencies, .autolink, - .swiftDocumentation, .swiftInterface, .privateSwiftInterface, .swiftSourceInfoFile, + .swiftDocumentation, .swiftInterface, .privateSwiftInterface, .packageSwiftInterface, .swiftSourceInfoFile, .diagnostics, .emitModuleDiagnostics, .objcHeader, .swiftDeps, .remap, .tbd, .moduleTrace, .yamlOptimizationRecord, .bitstreamOptimizationRecord, .pcm, .pch, .clangModuleMap, .jsonCompilerFeatures, .jsonTargetInfo, .jsonSwiftArtifacts, @@ -470,7 +470,7 @@ extension FileType { case .swift, .dSYM, .autolink, .dependencies, .emitModuleDependencies, .swiftDocumentation, .pcm, .diagnostics, .emitModuleDiagnostics, .objcHeader, .image, .swiftDeps, .moduleTrace, .tbd, .yamlOptimizationRecord, - .bitstreamOptimizationRecord, .swiftInterface, .privateSwiftInterface, + .bitstreamOptimizationRecord, .swiftInterface, .privateSwiftInterface, .packageSwiftInterface, .swiftSourceInfoFile, .clangModuleMap, .jsonSwiftArtifacts, .indexUnitOutputPath, .modDepCache, .jsonAPIBaseline, .jsonABIBaseline, .swiftConstValues, .jsonAPIDescriptor, .moduleSummary, .moduleSemanticInfo, diff --git a/Sources/SwiftDriver/Jobs/EmitModuleJob.swift b/Sources/SwiftDriver/Jobs/EmitModuleJob.swift index 7f206e90d..1afafd78d 100644 --- a/Sources/SwiftDriver/Jobs/EmitModuleJob.swift +++ b/Sources/SwiftDriver/Jobs/EmitModuleJob.swift @@ -32,6 +32,9 @@ extension Driver { addSupplementalOutput(path: moduleSourceInfoPath, flag: "-emit-module-source-info-path", type: .swiftSourceInfoFile) addSupplementalOutput(path: swiftInterfacePath, flag: "-emit-module-interface-path", type: .swiftInterface) addSupplementalOutput(path: swiftPrivateInterfacePath, flag: "-emit-private-module-interface-path", type: .privateSwiftInterface) + if let pkgName = packageName, !pkgName.isEmpty { + addSupplementalOutput(path: swiftPackageInterfacePath, flag: "-emit-package-module-interface-path", type: .packageSwiftInterface) + } addSupplementalOutput(path: objcGeneratedHeaderPath, flag: "-emit-objc-header-path", type: .objcHeader) addSupplementalOutput(path: tbdPath, flag: "-emit-tbd-path", type: .tbd) addSupplementalOutput(path: apiDescriptorFilePath, flag: "-emit-api-descriptor-path", type: .jsonAPIDescriptor) diff --git a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift index d26e69bb5..96514dbd7 100644 --- a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift +++ b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift @@ -233,6 +233,7 @@ extension Driver { try commandLine.appendLast(.debugDiagnosticNames, from: &parsedOptions) try commandLine.appendLast(.scanDependencies, from: &parsedOptions) try commandLine.appendLast(.enableExperimentalConcisePoundFile, from: &parsedOptions) + try commandLine.appendLast(.experimentalPackageInterfaceLoad, from: &parsedOptions) try commandLine.appendLast(.printEducationalNotes, from: &parsedOptions) try commandLine.appendLast(.diagnosticStyle, from: &parsedOptions) try commandLine.appendLast(.locale, from: &parsedOptions) @@ -587,6 +588,13 @@ extension Driver { input: nil, flag: "-emit-private-module-interface-path") + if let pkgName = packageName, !pkgName.isEmpty { + try addOutputOfType( + outputType: .packageSwiftInterface, + finalOutputPath: swiftPackageInterfacePath, + input: nil, + flag: "-emit-package-module-interface-path") + } try addOutputOfType( outputType: .tbd, finalOutputPath: tbdPath, diff --git a/Sources/SwiftDriver/Jobs/Planning.swift b/Sources/SwiftDriver/Jobs/Planning.swift index b630a7cd1..a7ddf891b 100644 --- a/Sources/SwiftDriver/Jobs/Planning.swift +++ b/Sources/SwiftDriver/Jobs/Planning.swift @@ -571,15 +571,29 @@ extension Driver { let optIn = env["ENABLE_DEFAULT_INTERFACE_VERIFIER"] != nil || parsedOptions.hasArgument(.verifyEmittedModuleInterface) - func addVerifyJob(forPrivate: Bool) throws { - let isNeeded = - forPrivate - ? parsedOptions.hasArgument(.emitPrivateModuleInterfacePath) - : parsedOptions.hasArgument(.emitModuleInterface, .emitModuleInterfacePath) + + enum InterfaceMode { + case Public, Private, Package + } + + func addVerifyJob(for mode: InterfaceMode) throws { + var isNeeded = false + var outputType: FileType + + switch mode { + case .Public: + isNeeded = parsedOptions.hasArgument(.emitModuleInterface, .emitModuleInterfacePath) + outputType = FileType.swiftInterface + case .Private: + isNeeded = parsedOptions.hasArgument(.emitPrivateModuleInterfacePath) + outputType = .privateSwiftInterface + case .Package: + isNeeded = parsedOptions.hasArgument(.emitPackageModuleInterfacePath) + outputType = .packageSwiftInterface + } + guard isNeeded else { return } - let outputType: FileType = - forPrivate ? .privateSwiftInterface : .swiftInterface let mergeInterfaceOutputs = emitModuleJob.outputs.filter { $0.type == outputType } assert(mergeInterfaceOutputs.count == 1, "Merge module job should only have one swiftinterface output") @@ -588,8 +602,9 @@ extension Driver { optIn: optIn) addJob(job) } - try addVerifyJob(forPrivate: false) - try addVerifyJob(forPrivate: true) + try addVerifyJob(for: .Public) + try addVerifyJob(for: .Private) + try addVerifyJob(for: .Package) } private mutating func addAutolinkExtractJob( diff --git a/Sources/SwiftDriver/Jobs/PrebuiltModulesJob.swift b/Sources/SwiftDriver/Jobs/PrebuiltModulesJob.swift index f909d1b75..e5473fac8 100644 --- a/Sources/SwiftDriver/Jobs/PrebuiltModulesJob.swift +++ b/Sources/SwiftDriver/Jobs/PrebuiltModulesJob.swift @@ -353,6 +353,7 @@ public class SwiftAdopter: Codable { public let moduleDir: String public let hasInterface: Bool public let hasPrivateInterface: Bool + public let hasPackageInterface: Bool public let hasModule: Bool public let isFramework: Bool public let isPrivate: Bool @@ -364,6 +365,7 @@ public class SwiftAdopter: Codable { self.moduleDir = SwiftAdopter.relativeToSDK(moduleDir) self.hasInterface = !hasInterface.isEmpty self.hasPrivateInterface = hasInterface.contains { $0.basename.hasSuffix(".private.swiftinterface") } + self.hasPackageInterface = hasInterface.contains { $0.basename.hasSuffix(".package.swiftinterface") } self.hasModule = !hasModule.isEmpty self.isFramework = self.moduleDir.contains("\(name).framework") self.isPrivate = self.moduleDir.contains("PrivateFrameworks") diff --git a/Sources/SwiftDriver/Jobs/VerifyModuleInterfaceJob.swift b/Sources/SwiftDriver/Jobs/VerifyModuleInterfaceJob.swift index 0f1c845d1..896631d25 100644 --- a/Sources/SwiftDriver/Jobs/VerifyModuleInterfaceJob.swift +++ b/Sources/SwiftDriver/Jobs/VerifyModuleInterfaceJob.swift @@ -13,7 +13,7 @@ extension Driver { mutating func computeCacheKeyForInterface(emitModuleJob: Job, interfaceKind: FileType) throws -> String? { - assert(interfaceKind == .swiftInterface || interfaceKind == .privateSwiftInterface, + assert(interfaceKind == .swiftInterface || interfaceKind == .privateSwiftInterface || interfaceKind == .packageSwiftInterface, "only expect interface output kind") let isNeeded = emitModuleJob.outputs.contains { $0.type == interfaceKind } guard isCachingEnabled && isNeeded else { return nil } @@ -54,11 +54,14 @@ extension Driver { } // TODO: remove this because we'd like module interface errors to fail the build. - if !optIn && isFrontendArgSupported(.downgradeTypecheckInterfaceError) { + if isFrontendArgSupported(.downgradeTypecheckInterfaceError) && + (!optIn || + // package interface is new and should not be a blocker for now + interfaceInput.type == .packageSwiftInterface) { commandLine.appendFlag(.downgradeTypecheckInterfaceError) } - let cacheKeys = try computeOutputCacheKeyForJob(commandLine: commandLine, inputs: [interfaceInput]) + let cacheKeys = try computeOutputCacheKeyForJob(commandLine: commandLine, inputs: [interfaceInput]) return Job( moduleName: moduleOutputInfo.name, kind: .verifyModuleInterface, diff --git a/Sources/SwiftDriver/Utilities/FileType.swift b/Sources/SwiftDriver/Utilities/FileType.swift index b55943a1c..ca276e90f 100644 --- a/Sources/SwiftDriver/Utilities/FileType.swift +++ b/Sources/SwiftDriver/Utilities/FileType.swift @@ -54,6 +54,9 @@ public enum FileType: String, Hashable, CaseIterable, Codable { /// An SPI Swift Interface file. case privateSwiftInterface = "private.swiftinterface" + /// An interface file containng package decls as well as SPI and public decls. + case packageSwiftInterface = "package.swiftinterface" + /// Serialized source information. case swiftSourceInfoFile = "swiftsourceinfo" @@ -194,6 +197,9 @@ extension FileType: CustomStringConvertible { case .privateSwiftInterface: return "private-swiftinterface" + case .packageSwiftInterface: + return "package-swiftinterface" + case .objcHeader: return "objc-header" @@ -275,7 +281,7 @@ extension FileType { .emitModuleDependencies, .swiftDocumentation, .pcm, .diagnostics, .emitModuleDiagnostics, .objcHeader, .image, .swiftDeps, .moduleTrace, .tbd, .yamlOptimizationRecord, .bitstreamOptimizationRecord, - .swiftInterface, .privateSwiftInterface, .swiftSourceInfoFile, + .swiftInterface, .privateSwiftInterface, .packageSwiftInterface, .swiftSourceInfoFile, .jsonDependencies, .clangModuleMap, .jsonTargetInfo, .jsonCompilerFeatures, .jsonSwiftArtifacts, .indexUnitOutputPath, .modDepCache, .jsonAPIBaseline, .jsonABIBaseline, .swiftConstValues, .jsonAPIDescriptor, @@ -324,6 +330,8 @@ extension FileType { return "swiftinterface" case .privateSwiftInterface: return "private-swiftinterface" + case .packageSwiftInterface: + return "package-swiftinterface" case .swiftSourceInfoFile: return "swiftsourceinfo" case .clangModuleMap: @@ -401,7 +409,7 @@ extension FileType { switch self { case .swift, .sil, .dependencies, .emitModuleDependencies, .assembly, .ast, .raw_sil, .llvmIR,.objcHeader, .autolink, .importedModules, .tbd, - .moduleTrace, .yamlOptimizationRecord, .swiftInterface, .privateSwiftInterface, + .moduleTrace, .yamlOptimizationRecord, .swiftInterface, .privateSwiftInterface, .packageSwiftInterface, .jsonDependencies, .clangModuleMap, .jsonCompilerFeatures, .jsonTargetInfo, .jsonSwiftArtifacts, .jsonAPIBaseline, .jsonABIBaseline, .swiftConstValues, .jsonAPIDescriptor, .moduleSummary, .moduleSemanticInfo, .cachedDiagnostics: @@ -422,7 +430,7 @@ extension FileType { return true case .swift, .sil, .sib, .ast, .image, .dSYM, .dependencies, .emitModuleDependencies, .autolink, .swiftModule, .swiftDocumentation, .swiftInterface, - .privateSwiftInterface, .swiftSourceInfoFile, .raw_sil, .raw_sib, + .privateSwiftInterface, .packageSwiftInterface, .swiftSourceInfoFile, .raw_sil, .raw_sib, .diagnostics, .emitModuleDiagnostics, .objcHeader, .swiftDeps, .remap, .importedModules, .tbd, .moduleTrace, .indexData, .yamlOptimizationRecord, .modDepCache, .bitstreamOptimizationRecord, .pcm, .pch, .jsonDependencies, @@ -445,7 +453,7 @@ extension FileType { return false case .assembly, .llvmIR, .llvmBitcode, .object, .sil, .sib, .ast, .dependencies, .emitModuleDependencies, .swiftModule, - .swiftDocumentation, .swiftInterface, .privateSwiftInterface, + .swiftDocumentation, .swiftInterface, .privateSwiftInterface, .packageSwiftInterface, .swiftSourceInfoFile, .raw_sil, .raw_sib, .objcHeader, .swiftDeps, .tbd, .moduleTrace, .indexData, .yamlOptimizationRecord, .bitstreamOptimizationRecord, .pcm, .pch, .jsonDependencies, diff --git a/Sources/SwiftOptions/Options.swift b/Sources/SwiftOptions/Options.swift index c580479fc..713152ca2 100644 --- a/Sources/SwiftOptions/Options.swift +++ b/Sources/SwiftOptions/Options.swift @@ -327,6 +327,7 @@ extension Option { public static let emitObjcHeaderPath: Option = Option("-emit-objc-header-path", .separate, attributes: [.frontend, .noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant], metaVar: "", helpText: "Emit an Objective-C header file to ") public static let emitObjcHeader: Option = Option("-emit-objc-header", .flag, attributes: [.frontend, .noInteractive, .supplementaryOutput], helpText: "Emit an Objective-C header file") public static let emitObject: Option = Option("-emit-object", .flag, attributes: [.frontend, .noInteractive, .doesNotAffectIncrementalBuild], helpText: "Emit object file(s) (-c)", group: .modes) + public static let emitPackageModuleInterfacePath: Option = Option("-emit-package-module-interface-path", .separate, attributes: [.helpHidden, .frontend, .noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant], metaVar: "", helpText: "Output package module interface file to ") public static let emitParseableModuleInterfacePath: Option = Option("-emit-parseable-module-interface-path", .separate, alias: Option.emitModuleInterfacePath, attributes: [.helpHidden, .frontend, .noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant]) public static let emitParseableModuleInterface: Option = Option("-emit-parseable-module-interface", .flag, alias: Option.emitModuleInterface, attributes: [.helpHidden, .noInteractive, .supplementaryOutput]) public static let emitParse: Option = Option("-emit-parse", .flag, alias: Option.dumpParse, attributes: [.frontend, .noInteractive, .doesNotAffectIncrementalBuild]) @@ -455,6 +456,7 @@ extension Option { public static let experimentalHermeticSealAtLink: Option = Option("-experimental-hermetic-seal-at-link", .flag, attributes: [.helpHidden, .frontend], helpText: "Library code can assume that all clients are visible at linktime, and aggressively strip unused code") public static let experimentalLazyTypecheck: Option = Option("-experimental-lazy-typecheck", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Type-check lazily as needed to produce requested outputs") public static let experimentalOneWayClosureParams: Option = Option("-experimental-one-way-closure-params", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Enable experimental support for one-way closure parameters") + public static let experimentalPackageInterfaceLoad: Option = Option("-experimental-package-interface-load", .flag, attributes: [.helpHidden, .frontend, .moduleInterface], helpText: "Supports loading a module via .package.swiftinterface in the same package") public static let ExperimentalPerformanceAnnotations: Option = Option("-experimental-performance-annotations", .flag, attributes: [.helpHidden, .frontend], helpText: "Deprecated, has no effect") public static let platformCCallingConventionEQ: Option = Option("-experimental-platform-c-calling-convention=", .joined, alias: Option.platformCCallingConvention, attributes: [.helpHidden, .frontend, .noDriver]) public static let platformCCallingConvention: Option = Option("-experimental-platform-c-calling-convention", .separate, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Which calling convention is used to perform non-swift calls. Defaults to llvm's standard C calling convention.") @@ -1149,6 +1151,7 @@ extension Option { Option.emitObjcHeaderPath, Option.emitObjcHeader, Option.emitObject, + Option.emitPackageModuleInterfacePath, Option.emitParseableModuleInterfacePath, Option.emitParseableModuleInterface, Option.emitParse, @@ -1277,6 +1280,7 @@ extension Option { Option.experimentalHermeticSealAtLink, Option.experimentalLazyTypecheck, Option.experimentalOneWayClosureParams, + Option.experimentalPackageInterfaceLoad, Option.ExperimentalPerformanceAnnotations, Option.platformCCallingConventionEQ, Option.platformCCallingConvention, diff --git a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift index 009c53ee6..7acfe43b3 100644 --- a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift +++ b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift @@ -2086,6 +2086,7 @@ final class ExplicitModuleBuildTests: XCTestCase { XCTAssertFalse(A.isPrivate) XCTAssertFalse(A.hasModule) XCTAssertFalse(A.hasPrivateInterface) + XCTAssertFalse(A.hasPackageInterface) XCTAssertTrue(A.hasInterface) let B = adopters.first {$0.name == "B"}! @@ -2093,6 +2094,7 @@ final class ExplicitModuleBuildTests: XCTestCase { XCTAssertFalse(B.isPrivate) XCTAssertFalse(B.hasModule) XCTAssertTrue(B.hasPrivateInterface) + XCTAssertFalse(B.hasPackageInterface) } func testCollectSwiftAdoptersWhetherMixed() throws { diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index cca573c9e..00d32bda6 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -2783,10 +2783,52 @@ final class SwiftDriverTests: XCTestCase { XCTAssertTrue(emitInterfaceJob.commandLine.contains(.flag("-emit-private-module-interface-path"))) } + func testPackageInterfacePathImplicit() throws { + let envVars = ProcessEnv.vars + + // A .package.swiftinterface should only be generated if package-name is passed. + do { + var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", "-module-name", "foo", + "-package-name", "mypkg", + "-emit-module-interface", "-enable-library-evolution"], env: envVars) + let plannedJobs = try driver.planBuild() + XCTAssertEqual(plannedJobs.count, 2) + let emitInterfaceJob = plannedJobs[0] + XCTAssertTrue(emitInterfaceJob.commandLine.contains(.flag("-emit-module-interface-path"))) + XCTAssertTrue(emitInterfaceJob.commandLine.contains(.flag("-emit-private-module-interface-path"))) + XCTAssertTrue(emitInterfaceJob.commandLine.contains(.flag("-emit-package-module-interface-path"))) + } + + // package-name is not passed, so package interface should not be generated. + do { + var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", "-module-name", "foo", + "-emit-module-interface", "-enable-library-evolution"], env: envVars) + let plannedJobs = try driver.planBuild() + XCTAssertEqual(plannedJobs.count, 2) + let emitInterfaceJob = plannedJobs[0] + XCTAssertTrue(emitInterfaceJob.commandLine.contains(.flag("-emit-module-interface-path"))) + XCTAssertTrue(emitInterfaceJob.commandLine.contains(.flag("-emit-private-module-interface-path"))) + XCTAssertFalse(emitInterfaceJob.commandLine.contains(.flag("-emit-package-module-interface-path"))) + } + + // package-name is not passed, so specifying emit-package-module-interface-path should be a no-op. + do { + var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", "-module-name", "foo", + "-emit-module-interface", + "-emit-package-module-interface-path", "foo.package.swiftinterface", + "-enable-library-evolution"], env: envVars) + let plannedJobs = try driver.planBuild() + XCTAssertEqual(plannedJobs.count, 2) + let emitInterfaceJob = plannedJobs[0] + XCTAssertTrue(emitInterfaceJob.commandLine.contains(.flag("-emit-module-interface-path"))) + XCTAssertTrue(emitInterfaceJob.commandLine.contains(.flag("-emit-private-module-interface-path"))) + XCTAssertFalse(emitInterfaceJob.commandLine.contains(.flag("-emit-package-module-interface-path"))) + } + } + func testSingleThreadedWholeModuleOptimizationCompiles() throws { var envVars = ProcessEnv.vars envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) - var driver1 = try Driver(args: ["swiftc", "-whole-module-optimization", "foo.swift", "bar.swift", "-emit-library", "-emit-module", "-module-name", "Test", "-emit-module-interface", "-emit-objc-header-path", "Test-Swift.h", "-emit-private-module-interface-path", "Test.private.swiftinterface", "-emit-tbd"], env: envVars) let plannedJobs = try driver1.planBuild().removingAutolinkExtractJobs() @@ -5592,6 +5634,99 @@ final class SwiftDriverTests: XCTestCase { } } + func testVerifyEmittedPackageInterface() throws { + var envVars = ProcessEnv.vars + envVars["ENABLE_DEFAULT_INTERFACE_VERIFIER"] = "YES" + + // Evolution enabled + do { + var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", + "-module-name", "foo", + "-package-name", "foopkg", + "-emit-module-interface", + "-emit-package-module-interface-path", "foo.package.swiftinterface", + "-verify-emitted-module-interface", + "-enable-library-evolution"], env: envVars) + + let plannedJobs = try driver.planBuild() + let x = plannedJobs.first?.commandLine.joinedUnresolvedArguments ?? "" + print(x) + XCTAssertEqual(plannedJobs.count, 4) + let emitJob = try plannedJobs.findJob(.emitModule) + let verifyJob = try plannedJobs.findJob(.verifyModuleInterface) + let packageOutputs = emitJob.outputs.filter { $0.type == .packageSwiftInterface } + let publicOutputs = emitJob.outputs.filter { $0.type == .swiftInterface } + XCTAssertTrue(packageOutputs.count == 1, + "There should be one package swiftinterface output") + XCTAssertTrue(publicOutputs.count == 1, + "There should be one public swiftinterface output") + XCTAssertTrue(verifyJob.inputs.count == 1) + XCTAssertTrue(verifyJob.inputs[0] == publicOutputs[0]) + XCTAssertTrue(verifyJob.outputs.isEmpty) + } + + // Explicitly disabled + do { + var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", + "-module-name", "foo", + "-package-name", "foopkg", + "-emit-module-interface", + "-emit-package-module-interface-path", "foo.package.swiftinterface", + "-enable-library-evolution", + "-no-verify-emitted-module-interface"], env: envVars) + let plannedJobs = try driver.planBuild() + XCTAssertEqual(plannedJobs.count, 2) + } + + // Emit-module separately + do { + var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", + "-module-name", "foo", + "-package-name", "foopkg", + "-emit-module-interface", + "-emit-package-module-interface-path", "foo.package.swiftinterface", + "-enable-library-evolution", + "-experimental-emit-module-separately"], env: envVars) + let plannedJobs = try driver.planBuild() + XCTAssertEqual(plannedJobs.count, 4) + let emitJob = try plannedJobs.findJob(.emitModule) + let verifyJob = try plannedJobs.findJob(.verifyModuleInterface) + let packageOutputs = emitJob.outputs.filter { $0.type == .packageSwiftInterface } + let publicOutputs = emitJob.outputs.filter { $0.type == .swiftInterface } + XCTAssertTrue(packageOutputs.count == 1, + "There should be one package swiftinterface output") + XCTAssertTrue(publicOutputs.count == 1, + "There should be one public swiftinterface output") + XCTAssertTrue(verifyJob.inputs.count == 1) + XCTAssertTrue(verifyJob.inputs[0] == publicOutputs[0]) + XCTAssertTrue(verifyJob.outputs.isEmpty) + } + } + + func testLoadPackageInterface() throws { + try withTemporaryDirectory { path in + let envVars = ProcessEnv.vars + let main = path.appending(component: "main.swift") + try localFileSystem.writeFileContents(main) { + $0.send("import Foo;") + } + let swiftModuleInterfacesPath: AbsolutePath = + try testInputsPath.appending(component: "testLoadPackageInterface") + let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? [] + var driver = try Driver(args: ["swiftc", main.nativePathString(escaped: true), + "-typecheck", "-v", + "-package-name", "foopkg", + "-experimental-package-interface-load", + "-I", swiftModuleInterfacesPath.nativePathString(escaped: true), + "-enable-library-evolution"] + sdkArgumentsForTesting, + env: envVars) + + let plannedJobs = try driver.planBuild() + XCTAssertTrue(plannedJobs.count == 1) + XCTAssertTrue(plannedJobs[0].commandLine.contains(.flag("-experimental-package-interface-load"))) + } + } + func testPCHGeneration() throws { do { var driver = try Driver(args: ["swiftc", "-typecheck", "-import-objc-header", "TestInputHeader.h", "foo.swift"])