From 1f86e98bdc75d17132cc1643b83fc5179d996f46 Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Thu, 12 Dec 2024 13:08:13 -0800 Subject: [PATCH 1/5] Group supplementary module outputs This should have no actual impact on compilation. The idea is that we are grouping the supplementary outputs that are specific to a given target. This will allow us to store two of them, one for the target and one for the target-variant. The end goal being that we can emit the target variant interface bits in the same driver invocation that generates the zippered object files. --- Sources/SwiftDriver/Driver/Driver.swift | 208 +++++++++++------- .../SwiftDriver/Jobs/APIDigesterJobs.swift | 26 ++- Sources/SwiftDriver/Jobs/CompileJob.swift | 2 + Sources/SwiftDriver/Jobs/EmitModuleJob.swift | 13 +- .../SwiftDriver/Jobs/FrontendJobHelpers.swift | 24 +- Sources/SwiftDriver/Jobs/MergeModuleJob.swift | 2 +- 6 files changed, 174 insertions(+), 101 deletions(-) diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index 4a62ce149..95277886b 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -376,17 +376,123 @@ public struct Driver { /// Path to the TBD file (text-based dylib). let tbdPath: VirtualPath.Handle? - /// Path to the module documentation file. - let moduleDocOutputPath: VirtualPath.Handle? + /// Target-specific supplemental output file paths + struct SupplementalModuleTargetOutputPaths { + /// Path to the module documentation file. + let moduleDocOutputPath: VirtualPath.Handle? - /// Path to the Swift interface file. - let swiftInterfacePath: VirtualPath.Handle? + /// Path to the Swift interface file. + let swiftInterfacePath: VirtualPath.Handle? - /// Path to the Swift private interface file. - let swiftPrivateInterfacePath: VirtualPath.Handle? + /// Path to the Swift private interface file. + let swiftPrivateInterfacePath: VirtualPath.Handle? - /// Path to the Swift package interface file. - let swiftPackageInterfacePath: VirtualPath.Handle? + /// Path to the Swift package interface file. + let swiftPackageInterfacePath: VirtualPath.Handle? + + /// Path to the Swift module source information file. + let moduleSourceInfoPath: VirtualPath.Handle? + } + + private static func computeModuleOutputPaths( + _ parsedOptions: inout ParsedOptions, + moduleName: String, + packageName: String?, + moduleOutputInfo: ModuleOutputInfo, + compilerOutputType: FileType?, + compilerMode: CompilerMode, + emitModuleSeparately: Bool, + outputFileMap: OutputFileMap?, + projectDirectory: VirtualPath.Handle?) throws -> SupplementalModuleTargetOutputPaths { + let moduleDocOutputPath = try Self.computeModuleDocOutputPath( + &parsedOptions, + moduleOutputPath: moduleOutputInfo.output?.outputPath, + compilerOutputType: compilerOutputType, + compilerMode: compilerMode, + outputFileMap: outputFileMap, + moduleName: moduleOutputInfo.name) + + let moduleSourceInfoPath = try Self.computeModuleSourceInfoOutputPath( + &parsedOptions, + moduleOutputPath: moduleOutputInfo.output?.outputPath, + compilerOutputType: compilerOutputType, + compilerMode: compilerMode, + outputFileMap: outputFileMap, + moduleName: moduleOutputInfo.name, + projectDirectory: projectDirectory) + + // --------------------- + // Swift interface paths + let swiftInterfacePath = try Self.computeSupplementaryOutputPath( + &parsedOptions, type: .swiftInterface, isOutputOptions: [.emitModuleInterface], + outputPath: .emitModuleInterfacePath, + compilerOutputType: compilerOutputType, + compilerMode: compilerMode, + emitModuleSeparately: emitModuleSeparately, + outputFileMap: outputFileMap, + moduleName: moduleOutputInfo.name) + + let givenPrivateInterfacePath = try Self.computeSupplementaryOutputPath( + &parsedOptions, type: .privateSwiftInterface, isOutputOptions: [], + outputPath: .emitPrivateModuleInterfacePath, + compilerOutputType: compilerOutputType, + compilerMode: compilerMode, + emitModuleSeparately: emitModuleSeparately, + outputFileMap: outputFileMap, + moduleName: moduleOutputInfo.name) + let givenPackageInterfacePath = try Self.computeSupplementaryOutputPath( + &parsedOptions, type: .packageSwiftInterface, isOutputOptions: [], + outputPath: .emitPackageModuleInterfacePath, + compilerOutputType: compilerOutputType, + compilerMode: compilerMode, + emitModuleSeparately: emitModuleSeparately, + outputFileMap: outputFileMap, + moduleName: moduleOutputInfo.name) + + // Always emit the private swift interface if a public interface is emitted. + // With the introduction of features like @_spi_available, we may print + // public and private interfaces differently even from the same codebase. + // For this reason, we should always print private interfaces so that we + // don’t mix the public interfaces with private Clang modules. + let swiftPrivateInterfacePath: VirtualPath.Handle? + if let privateInterfacePath = givenPrivateInterfacePath { + swiftPrivateInterfacePath = privateInterfacePath + } else if let swiftInterfacePath = swiftInterfacePath { + swiftPrivateInterfacePath = try VirtualPath.lookup(swiftInterfacePath) + .replacingExtension(with: .privateSwiftInterface).intern() + } else { + swiftPrivateInterfacePath = nil + } + + let swiftPackageInterfacePath: VirtualPath.Handle? + if let packageName = packageName, + !packageName.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 givenPackageInterfacePath = givenPackageInterfacePath { + swiftPackageInterfacePath = givenPackageInterfacePath + } else if let swiftInterfacePath = swiftInterfacePath { + swiftPackageInterfacePath = try VirtualPath.lookup(swiftInterfacePath) + .replacingExtension(with: .packageSwiftInterface).intern() + } else { + swiftPackageInterfacePath = nil + } + } else { + swiftPackageInterfacePath = nil + } + + return SupplementalModuleTargetOutputPaths( + moduleDocOutputPath: moduleDocOutputPath, + swiftInterfacePath: swiftInterfacePath, + swiftPrivateInterfacePath: swiftPrivateInterfacePath, + swiftPackageInterfacePath: swiftPackageInterfacePath, + moduleSourceInfoPath: moduleSourceInfoPath) + } + + /// Structure storing paths to supplemental outputs for the target module + let moduleOutputPaths: SupplementalModuleTargetOutputPaths /// File type for the optimization record. let optimizationRecordFileType: FileType? @@ -394,9 +500,6 @@ public struct Driver { /// Path to the optimization record. let optimizationRecordPath: VirtualPath.Handle? - /// Path to the Swift module source information file. - let moduleSourceInfoPath: VirtualPath.Handle? - /// Path to the module's digester baseline file. let digesterBaselinePath: VirtualPath.Handle? @@ -1023,24 +1126,22 @@ public struct Driver { emitModuleSeparately: emitModuleSeparately, outputFileMap: self.outputFileMap, moduleName: moduleOutputInfo.name) - self.moduleDocOutputPath = try Self.computeModuleDocOutputPath( - &parsedOptions, moduleOutputPath: self.moduleOutputInfo.output?.outputPath, - compilerOutputType: compilerOutputType, - compilerMode: compilerMode, - outputFileMap: self.outputFileMap, - moduleName: moduleOutputInfo.name) let projectDirectory = Self.computeProjectDirectoryPath( - moduleOutputPath: self.moduleOutputInfo.output?.outputPath, + moduleOutputPath: moduleOutputInfo.output?.outputPath, fileSystem: self.fileSystem) - self.moduleSourceInfoPath = try Self.computeModuleSourceInfoOutputPath( - &parsedOptions, - moduleOutputPath: self.moduleOutputInfo.output?.outputPath, - compilerOutputType: compilerOutputType, - compilerMode: compilerMode, - outputFileMap: self.outputFileMap, - moduleName: moduleOutputInfo.name, - projectDirectory: projectDirectory) + + self.moduleOutputPaths = try Self.computeModuleOutputPaths( + &parsedOptions, + moduleName: moduleOutputInfo.name, + packageName: self.packageName, + moduleOutputInfo: self.moduleOutputInfo, + compilerOutputType: compilerOutputType, + compilerMode: compilerMode, + emitModuleSeparately: emitModuleSeparately, + outputFileMap: self.outputFileMap, + projectDirectory: projectDirectory) + self.digesterBaselinePath = try Self.computeDigesterBaselineOutputPath( &parsedOptions, moduleOutputPath: self.moduleOutputInfo.output?.outputPath, @@ -1050,59 +1151,6 @@ public struct Driver { outputFileMap: self.outputFileMap, moduleName: moduleOutputInfo.name, projectDirectory: projectDirectory) - self.swiftInterfacePath = try Self.computeSupplementaryOutputPath( - &parsedOptions, type: .swiftInterface, isOutputOptions: [.emitModuleInterface], - outputPath: .emitModuleInterfacePath, - compilerOutputType: compilerOutputType, - compilerMode: compilerMode, - emitModuleSeparately: emitModuleSeparately, - outputFileMap: self.outputFileMap, - moduleName: moduleOutputInfo.name) - let givenPrivateInterfacePath = try Self.computeSupplementaryOutputPath( - &parsedOptions, type: .privateSwiftInterface, isOutputOptions: [], - outputPath: .emitPrivateModuleInterfacePath, - compilerOutputType: compilerOutputType, - compilerMode: compilerMode, - 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 - // and private interfaces differently even from the same codebase. For this reason, - // we should always print private interfaces so that we don’t mix the public interfaces - // with private Clang modules. - if let swiftInterfacePath = self.swiftInterfacePath, - givenPrivateInterfacePath == nil { - self.swiftPrivateInterfacePath = try VirtualPath.lookup(swiftInterfacePath) - .replacingExtension(with: .privateSwiftInterface).intern() - } else { - 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 { @@ -1151,7 +1199,7 @@ public struct Driver { Self.validateDigesterArgs(&parsedOptions, moduleOutputInfo: moduleOutputInfo, digesterMode: self.digesterMode, - swiftInterfacePath: self.swiftInterfacePath, + swiftInterfacePath: self.moduleOutputPaths.swiftInterfacePath, diagnosticEngine: diagnosticsEngine) try verifyOutputOptions() diff --git a/Sources/SwiftDriver/Jobs/APIDigesterJobs.swift b/Sources/SwiftDriver/Jobs/APIDigesterJobs.swift index 3d8a60f1c..b09c9072c 100644 --- a/Sources/SwiftDriver/Jobs/APIDigesterJobs.swift +++ b/Sources/SwiftDriver/Jobs/APIDigesterJobs.swift @@ -46,7 +46,10 @@ extension Driver { var commandLine = [Job.ArgTemplate]() commandLine.appendFlag("-dump-sdk") - try addCommonDigesterOptions(&commandLine, modulePath: modulePath, mode: mode) + try addCommonDigesterOptions(&commandLine, + modulePath: modulePath, + swiftModuleInterfacePath: self.moduleOutputPaths.swiftInterfacePath, + mode: mode) commandLine.appendFlag(.o) commandLine.appendPath(VirtualPath.lookup(outputPath)) @@ -74,7 +77,11 @@ extension Driver { } guard let currentABI = getDescriptorPath(for: mode) else { // we don't have existing descriptor to use so we have to load the module from interface/swiftmodule - return try digesterCompareToBaselineJob(modulePath: modulePath, baselinePath: baselinePath, mode: digesterMode) + return try digesterCompareToBaselineJob( + modulePath: modulePath, + swiftModuleInterfacePath: self.moduleOutputPaths.swiftInterfacePath, + baselinePath: baselinePath, + mode: digesterMode) } var commandLine = [Job.ArgTemplate]() commandLine.appendFlag("-diagnose-sdk") @@ -105,14 +112,20 @@ extension Driver { ) } - mutating func digesterCompareToBaselineJob(modulePath: VirtualPath.Handle, baselinePath: VirtualPath.Handle, mode: DigesterMode) throws -> Job { + mutating func digesterCompareToBaselineJob(modulePath: VirtualPath.Handle, + swiftModuleInterfacePath: VirtualPath.Handle?, + baselinePath: VirtualPath.Handle, + mode: DigesterMode) throws -> Job { var commandLine = [Job.ArgTemplate]() commandLine.appendFlag("-diagnose-sdk") commandLine.appendFlag("-disable-fail-on-error") commandLine.appendFlag("-baseline-path") commandLine.appendPath(VirtualPath.lookup(baselinePath)) - try addCommonDigesterOptions(&commandLine, modulePath: modulePath, mode: mode) + try addCommonDigesterOptions(&commandLine, + modulePath: modulePath, + swiftModuleInterfacePath: swiftModuleInterfacePath, + mode: mode) var serializedDiagnosticsPath: VirtualPath.Handle? if let arg = parsedOptions.getLastArgument(.serializeBreakingChangesPath)?.asSingle { @@ -130,7 +143,7 @@ extension Driver { var inputs: [TypedVirtualPath] = [.init(file: modulePath, type: .swiftModule), .init(file: baselinePath, type: mode.baselineFileType)] // If a module interface was emitted, treat it as an input in ABI mode. - if let interfacePath = self.swiftInterfacePath, mode == .abi { + if let interfacePath = swiftModuleInterfacePath, mode == .abi { inputs.append(.init(file: interfacePath, type: .swiftInterface)) } @@ -147,6 +160,7 @@ extension Driver { private mutating func addCommonDigesterOptions(_ commandLine: inout [Job.ArgTemplate], modulePath: VirtualPath.Handle, + swiftModuleInterfacePath: VirtualPath.Handle?, mode: DigesterMode) throws { commandLine.appendFlag("-module") commandLine.appendFlag(moduleOutputInfo.name) @@ -160,7 +174,7 @@ extension Driver { let searchPath = VirtualPath.lookup(modulePath).parentDirectory commandLine.appendFlag(.I) commandLine.appendPath(searchPath) - if let interfacePath = self.swiftInterfacePath { + if let interfacePath = swiftModuleInterfacePath { let interfaceSearchPath = VirtualPath.lookup(interfacePath).parentDirectory if interfaceSearchPath != searchPath { commandLine.appendFlag(.I) diff --git a/Sources/SwiftDriver/Jobs/CompileJob.swift b/Sources/SwiftDriver/Jobs/CompileJob.swift index f307f9846..6f2aa1051 100644 --- a/Sources/SwiftDriver/Jobs/CompileJob.swift +++ b/Sources/SwiftDriver/Jobs/CompileJob.swift @@ -277,6 +277,8 @@ extension Driver { primaryInputs: primaryInputs, inputsGeneratingCodeCount: inputsGeneratingCodeCount, inputOutputMap: &inputOutputMap, + moduleOutputInfo: self.moduleOutputInfo, + moduleOutputPaths: self.moduleOutputPaths, includeModuleTracePath: emitModuleTrace, indexFilePath: indexFilePath) diff --git a/Sources/SwiftDriver/Jobs/EmitModuleJob.swift b/Sources/SwiftDriver/Jobs/EmitModuleJob.swift index 489e70d07..9408c8dee 100644 --- a/Sources/SwiftDriver/Jobs/EmitModuleJob.swift +++ b/Sources/SwiftDriver/Jobs/EmitModuleJob.swift @@ -17,6 +17,7 @@ extension Driver { mutating func addCommonModuleOptions( commandLine: inout [Job.ArgTemplate], outputs: inout [TypedVirtualPath], + moduleOutputPaths: SupplementalModuleTargetOutputPaths, isMergeModule: Bool ) throws { // Add supplemental outputs. @@ -28,12 +29,12 @@ extension Driver { outputs.append(.init(file: path, type: type)) } - addSupplementalOutput(path: moduleDocOutputPath, flag: "-emit-module-doc-path", type: .swiftDocumentation) - 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) + addSupplementalOutput(path: moduleOutputPaths.moduleDocOutputPath, flag: "-emit-module-doc-path", type: .swiftDocumentation) + addSupplementalOutput(path: moduleOutputPaths.moduleSourceInfoPath, flag: "-emit-module-source-info-path", type: .swiftSourceInfoFile) + addSupplementalOutput(path: moduleOutputPaths.swiftInterfacePath, flag: "-emit-module-interface-path", type: .swiftInterface) + addSupplementalOutput(path: moduleOutputPaths.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: moduleOutputPaths.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) @@ -102,7 +103,7 @@ extension Driver { try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .emitModule) // FIXME: Add MSVC runtime library flags - try addCommonModuleOptions(commandLine: &commandLine, outputs: &outputs, isMergeModule: false) + try addCommonModuleOptions(commandLine: &commandLine, outputs: &outputs, moduleOutputPaths: moduleOutputPaths,isMergeModule: false) try addCommonSymbolGraphOptions(commandLine: &commandLine) try commandLine.appendLast(.checkApiAvailabilityOnly, from: &parsedOptions) diff --git a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift index d82e38529..f9f66bd04 100644 --- a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift +++ b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift @@ -546,6 +546,8 @@ extension Driver { primaryInputs: [TypedVirtualPath], inputsGeneratingCodeCount: Int, inputOutputMap: inout [TypedVirtualPath: [TypedVirtualPath]], + moduleOutputInfo: ModuleOutputInfo, + moduleOutputPaths: SupplementalModuleTargetOutputPaths, includeModuleTracePath: Bool, indexFilePath: TypedVirtualPath?) throws -> [TypedVirtualPath] { var flaggedInputOutputPairs: [(flag: String, input: TypedVirtualPath?, output: TypedVirtualPath)] = [] @@ -592,7 +594,9 @@ extension Driver { } /// Add all of the outputs needed for a given input. - func addAllOutputsFor(input: TypedVirtualPath?) throws { + func addAllOutputsFor(input: TypedVirtualPath?, + moduleOutputInfo: ModuleOutputInfo, + moduleOutputPaths: SupplementalModuleTargetOutputPaths) throws { if !emitModuleSeparately { // Generate the module files with the main job. try addOutputOfType( @@ -602,12 +606,12 @@ extension Driver { flag: "-emit-module-path") try addOutputOfType( outputType: .swiftDocumentation, - finalOutputPath: moduleDocOutputPath, + finalOutputPath: moduleOutputPaths.moduleDocOutputPath, input: input, flag: "-emit-module-doc-path") try addOutputOfType( outputType: .swiftSourceInfoFile, - finalOutputPath: moduleSourceInfoPath, + finalOutputPath: moduleOutputPaths.moduleSourceInfoPath, input: input, flag: "-emit-module-source-info-path") } @@ -645,10 +649,14 @@ extension Driver { if compilerMode.usesPrimaryFileInputs { for input in primaryInputs { - try addAllOutputsFor(input: input) + try addAllOutputsFor(input: input, + moduleOutputInfo: moduleOutputInfo, + moduleOutputPaths: moduleOutputPaths) } } else { - try addAllOutputsFor(input: nil) + try addAllOutputsFor(input: nil, + moduleOutputInfo: moduleOutputInfo, + moduleOutputPaths: moduleOutputPaths) if !emitModuleSeparately { // Outputs that only make sense when the whole module is processed @@ -661,20 +669,20 @@ extension Driver { try addOutputOfType( outputType: .swiftInterface, - finalOutputPath: swiftInterfacePath, + finalOutputPath: moduleOutputPaths.swiftInterfacePath, input: nil, flag: "-emit-module-interface-path") try addOutputOfType( outputType: .privateSwiftInterface, - finalOutputPath: swiftPrivateInterfacePath, + finalOutputPath: moduleOutputPaths.swiftPrivateInterfacePath, input: nil, flag: "-emit-private-module-interface-path") if let pkgName = packageName, !pkgName.isEmpty { try addOutputOfType( outputType: .packageSwiftInterface, - finalOutputPath: swiftPackageInterfacePath, + finalOutputPath: moduleOutputPaths.swiftPackageInterfacePath, input: nil, flag: "-emit-package-module-interface-path") } diff --git a/Sources/SwiftDriver/Jobs/MergeModuleJob.swift b/Sources/SwiftDriver/Jobs/MergeModuleJob.swift index 49f8d37f6..dd6717dbf 100644 --- a/Sources/SwiftDriver/Jobs/MergeModuleJob.swift +++ b/Sources/SwiftDriver/Jobs/MergeModuleJob.swift @@ -57,7 +57,7 @@ extension Driver { try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .mergeModule, bridgingHeaderHandling: .parsed) // FIXME: Add MSVC runtime library flags - try addCommonModuleOptions(commandLine: &commandLine, outputs: &outputs, isMergeModule: true) + try addCommonModuleOptions(commandLine: &commandLine, outputs: &outputs, moduleOutputPaths: moduleOutputPaths, isMergeModule: true) try addCommonSymbolGraphOptions(commandLine: &commandLine) From fd7fa723afadbc60126323eb0e5776db0db1c176 Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Tue, 17 Dec 2024 16:20:53 -0800 Subject: [PATCH 2/5] Emit variant module This patch wires up emitting the interface information for the target variant in the same swiftc invocation that emits the zippered object file. --- Sources/SwiftDriver/Driver/Driver.swift | 73 +++++++++++++++++-- Sources/SwiftDriver/Jobs/EmitModuleJob.swift | 16 +++- Sources/SwiftDriver/Jobs/Planning.swift | 29 ++++++-- Sources/SwiftOptions/Options.swift | 6 ++ Tests/SwiftDriverTests/SwiftDriverTests.swift | 30 ++++++++ 5 files changed, 139 insertions(+), 15 deletions(-) diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index 95277886b..9f4d2cb21 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -259,6 +259,9 @@ public struct Driver { /// The information about the module to produce. @_spi(Testing) public let moduleOutputInfo: ModuleOutputInfo + /// Information about the target variant module to produce if applicable + @_spi(Testing) public let variantModuleOutputInfo: ModuleOutputInfo? + /// Name of the package containing a target module or file. @_spi(Testing) public let packageName: String? @@ -494,6 +497,9 @@ public struct Driver { /// Structure storing paths to supplemental outputs for the target module let moduleOutputPaths: SupplementalModuleTargetOutputPaths + /// Structure storing paths to supplemental outputs for the target variant + let variantModuleOutputPaths: SupplementalModuleTargetOutputPaths? + /// File type for the optimization record. let optimizationRecordFileType: FileType? @@ -946,10 +952,24 @@ public struct Driver { // Determine the module we're building and whether/how the module file itself will be emitted. self.moduleOutputInfo = try Self.computeModuleInfo( - &parsedOptions, compilerOutputType: compilerOutputType, compilerMode: compilerMode, linkerOutputType: linkerOutputType, - debugInfoLevel: debugInfo.level, diagnosticsEngine: diagnosticEngine, + &parsedOptions, + modulePath: parsedOptions.getLastArgument(.emitModulePath)?.asSingle, + compilerOutputType: compilerOutputType, + compilerMode: compilerMode, + linkerOutputType: linkerOutputType, + debugInfoLevel: debugInfo.level, + diagnosticsEngine: diagnosticEngine, workingDirectory: self.workingDirectory) + self.variantModuleOutputInfo = try Self.computeVariantModuleInfo( + &parsedOptions, + compilerOutputType: compilerOutputType, + compilerMode: compilerMode, + linkerOutputType: linkerOutputType, + debugInfoLevel: debugInfo.level, + diagnosticsEngine: diagnosticsEngine, + workingDirectory: workingDirectory) + // Should we schedule a separate emit-module job? self.emitModuleSeparately = Self.computeEmitModuleSeparately(parsedOptions: &parsedOptions, compilerMode: compilerMode, @@ -1142,6 +1162,21 @@ public struct Driver { outputFileMap: self.outputFileMap, projectDirectory: projectDirectory) + if let variantModuleOutputInfo = self.variantModuleOutputInfo { + self.variantModuleOutputPaths = try Self.computeModuleOutputPaths( + &parsedOptions, + moduleName: variantModuleOutputInfo.name, + packageName: self.packageName, + moduleOutputInfo: variantModuleOutputInfo, + compilerOutputType: compilerOutputType, + compilerMode: compilerMode, + emitModuleSeparately: true, // variant module is always independent + outputFileMap: self.outputFileMap, + projectDirectory: projectDirectory) + } else { + self.variantModuleOutputPaths = nil + } + self.digesterBaselinePath = try Self.computeDigesterBaselineOutputPath( &parsedOptions, moduleOutputPath: self.moduleOutputInfo.output?.outputPath, @@ -2708,9 +2743,37 @@ extension Driver { return "" } + private static func computeVariantModuleInfo( + _ parsedOptions: inout ParsedOptions, + compilerOutputType: FileType?, + compilerMode: CompilerMode, + linkerOutputType: LinkOutputType?, + debugInfoLevel: DebugInfo.Level?, + diagnosticsEngine: DiagnosticsEngine, + workingDirectory: AbsolutePath? + ) throws -> ModuleOutputInfo? { + // If there is no target variant, then there is no target variant module. + // If there is no emit-variant-module, then there is not target variant + // module. + guard let variantModulePath = parsedOptions.getLastArgument(.emitVariantModulePath), + parsedOptions.hasArgument(.targetVariant) else { + return nil + } + return try computeModuleInfo(&parsedOptions, + modulePath: variantModulePath.asSingle, + compilerOutputType: compilerOutputType, + compilerMode: compilerMode, + linkerOutputType: linkerOutputType, + debugInfoLevel: debugInfoLevel, + diagnosticsEngine: diagnosticsEngine, + workingDirectory: workingDirectory) + return nil + } + /// Determine how the module will be emitted and the name of the module. private static func computeModuleInfo( _ parsedOptions: inout ParsedOptions, + modulePath: String?, compilerOutputType: FileType?, compilerMode: CompilerMode, linkerOutputType: LinkOutputType?, @@ -2725,7 +2788,7 @@ extension Driver { } var moduleOutputKind: ModuleOutputKind? - if parsedOptions.hasArgument(.emitModule, .emitModulePath) { + if parsedOptions.hasArgument(.emitModule) || modulePath != nil { // The user has requested a module, so generate one and treat it as // top-level output. moduleOutputKind = .topLevel @@ -2807,9 +2870,9 @@ extension Driver { // FIXME: Look in the output file map. It looks like it is weirdly // anchored to the first input? - if let modulePathArg = parsedOptions.getLastArgument(.emitModulePath) { + if let modulePathArg = modulePath { // The module path was specified. - moduleOutputPath = try VirtualPath(path: modulePathArg.asSingle) + moduleOutputPath = try VirtualPath(path: modulePathArg) } else if moduleOutputKind == .topLevel { // FIXME: Logic to infer from primary outputs, etc. let moduleFilename = moduleName.appendingFileTypeExtension(.swiftModule) diff --git a/Sources/SwiftDriver/Jobs/EmitModuleJob.swift b/Sources/SwiftDriver/Jobs/EmitModuleJob.swift index 9408c8dee..ee00c3310 100644 --- a/Sources/SwiftDriver/Jobs/EmitModuleJob.swift +++ b/Sources/SwiftDriver/Jobs/EmitModuleJob.swift @@ -79,8 +79,13 @@ extension Driver { } /// Form a job that emits a single module - @_spi(Testing) public mutating func emitModuleJob(pchCompileJob: Job?) throws -> Job { - let moduleOutputPath = moduleOutputInfo.output!.outputPath + @_spi(Testing) public mutating func emitModuleJob(pchCompileJob: Job?, isVariantJob: Bool = false) throws -> Job { + precondition(!isVariantJob || (isVariantJob && + variantModuleOutputInfo != nil && variantModuleOutputPaths != nil), + "target variant module requested without a target variant") + + let moduleOutputPath = isVariantJob ? variantModuleOutputInfo!.output!.outputPath : moduleOutputInfo.output!.outputPath + let moduleOutputPaths = isVariantJob ? variantModuleOutputPaths! : moduleOutputPaths var commandLine: [Job.ArgTemplate] = swiftCompilerPrefixArgs.map { Job.ArgTemplate.flag($0) } var inputs: [TypedVirtualPath] = [] var outputs: [TypedVirtualPath] = [ @@ -103,7 +108,12 @@ extension Driver { try addCommonFrontendOptions(commandLine: &commandLine, inputs: &inputs, kind: .emitModule) // FIXME: Add MSVC runtime library flags - try addCommonModuleOptions(commandLine: &commandLine, outputs: &outputs, moduleOutputPaths: moduleOutputPaths,isMergeModule: false) + try addCommonModuleOptions( + commandLine: &commandLine, + outputs: &outputs, + moduleOutputPaths: moduleOutputPaths, + isMergeModule: false) + try addCommonSymbolGraphOptions(commandLine: &commandLine) try commandLine.appendLast(.checkApiAvailabilityOnly, from: &parsedOptions) diff --git a/Sources/SwiftDriver/Jobs/Planning.swift b/Sources/SwiftDriver/Jobs/Planning.swift index 4a351ae02..0fa248544 100644 --- a/Sources/SwiftDriver/Jobs/Planning.swift +++ b/Sources/SwiftDriver/Jobs/Planning.swift @@ -256,13 +256,21 @@ extension Driver { ) } - private mutating func addEmitModuleJob(addJobBeforeCompiles: (Job) -> Void, pchCompileJob: Job?) throws -> Job? { - if emitModuleSeparately { - let emitJob = try emitModuleJob(pchCompileJob: pchCompileJob) - addJobBeforeCompiles(emitJob) - return emitJob - } - return nil + private mutating func addEmitModuleJob( + addJobBeforeCompiles: (Job) -> Void, + pchCompileJob: Job?, + isVariantModule: Bool = false) throws -> Job? { + // The target variant module is always emitted separately, so we need to + // add an explicit job regardless of whether the primary target was + // emitted separately + if emitModuleSeparately || isVariantModule { + let emitJob = try emitModuleJob( + pchCompileJob: pchCompileJob, + isVariantJob: isVariantModule) + addJobBeforeCompiles(emitJob) + return emitJob + } + return nil } private mutating func addJobsFeedingLinker( @@ -337,6 +345,13 @@ extension Driver { addLinkerInput: addLinkerInput) } + if variantModuleOutputInfo != nil { + _ = try addEmitModuleJob( + addJobBeforeCompiles: addJobBeforeCompiles, + pchCompileJob: jobCreatingPch, + isVariantModule: true) + } + try addJobsForPrimaryInputs( addCompileJobGroup: addCompileJobGroup, addModuleInput: addModuleInput, diff --git a/Sources/SwiftOptions/Options.swift b/Sources/SwiftOptions/Options.swift index c7adb8ae7..4d0c39cca 100644 --- a/Sources/SwiftOptions/Options.swift +++ b/Sources/SwiftOptions/Options.swift @@ -923,6 +923,10 @@ extension Option { public static let Xlinker: Option = Option("-Xlinker", .separate, attributes: [.doesNotAffectIncrementalBuild], helpText: "Specifies an option which should be passed to the linker") public static let Xllvm: Option = Option("-Xllvm", .separate, attributes: [.helpHidden, .frontend], metaVar: "", helpText: "Pass to LLVM.") public static let DASHDASH: Option = Option("--", .remaining, attributes: [.frontend, .doesNotAffectIncrementalBuild]) + + public static let emitVariantModulePath: Option = Option("-emit-variant-module-path", .separate, attributes: [.noInteractive, .supplementaryOutput, .argumentIsPath], helpText: "Emit an importable module for the target variant at the specified path") + public static let emitVariantModuleInterface: Option = Option("-emit-variant-module-interface", .flag, attributes: [.noInteractive, .supplementaryOutput], helpText: "Emit an importable module for the target variant") + } extension Option { @@ -1832,6 +1836,8 @@ extension Option { Option.Xlinker, Option.Xllvm, Option.DASHDASH, + Option.emitVariantModulePath, + Option.emitVariantModuleInterface, ] } } diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index 1bf42ae71..5c5c52bcd 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -4068,6 +4068,36 @@ final class SwiftDriverTests: XCTestCase { } } + func testTargetVariantEmitModule() throws { + do { + var driver = try Driver(args: ["swiftc", + "-target", "x86_64-apple-macosx10.14", + "-target-variant", "x86_64-apple-ios13.1-macabi", + "-enable-library-evolution", + "-emit-module", + "-emit-module-path", "foo.swiftmodule/target.swiftmodule", + "-emit-variant-module-path", "foo.swiftmodule/variant.swiftmodule", + "foo.swift"]) + + let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() + XCTAssertEqual(plannedJobs.count, 3) + + let targetModuleJob = plannedJobs[0] + let variantModuleJob = plannedJobs[1] + + XCTAssert(targetModuleJob.commandLine.contains(.flag("-emit-module"))) + XCTAssert(variantModuleJob.commandLine.contains(.flag("-emit-module"))) + + XCTAssert(targetModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/target.swiftdoc"))))) + XCTAssert(targetModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/target.swiftsourceinfo"))))) + XCTAssertTrue(targetModuleJob.commandLine.contains(subsequence: [.flag("-o"), .path(.relative(try .init(validating: "foo.swiftmodule/target.swiftmodule")))])) + + XCTAssert(variantModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftdoc"))))) + XCTAssert(variantModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftsourceinfo"))))) + XCTAssertTrue(variantModuleJob.commandLine.contains(subsequence: [.flag("-o"), .path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftmodule")))])) + } + } + func testValidDeprecatedTargetiOS() throws { var driver = try Driver(args: ["swiftc", "-emit-module", "-target", "armv7-apple-ios13.0", "foo.swift"]) let plannedJobs = try driver.planBuild() From 8ad234da98da34df6074c5fef14927f0a57a41db Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Wed, 18 Dec 2024 13:55:56 -0800 Subject: [PATCH 3/5] Add target variant api description This patch adds the api description to the set of supplemental outputs emitted for both the target and target variant. --- Sources/SwiftDriver/Driver/Driver.swift | 59 +++++++++++-------- Sources/SwiftDriver/Jobs/EmitModuleJob.swift | 2 +- .../SwiftDriver/Jobs/FrontendJobHelpers.swift | 2 +- Tests/SwiftDriverTests/SwiftDriverTests.swift | 30 ++++++++++ 4 files changed, 67 insertions(+), 26 deletions(-) diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index 9f4d2cb21..57573f6c0 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -395,6 +395,9 @@ public struct Driver { /// Path to the Swift module source information file. let moduleSourceInfoPath: VirtualPath.Handle? + + /// Path to the emitted API descriptor file. + let apiDescriptorFilePath: VirtualPath.Handle? } private static func computeModuleOutputPaths( @@ -424,6 +427,24 @@ public struct Driver { moduleName: moduleOutputInfo.name, projectDirectory: projectDirectory) + // --------------------- + // API Descriptor Path + let apiDescriptorFilePath: VirtualPath.Handle? + if let apiDescriptorDirectory = apiDescriptorDirectory { + apiDescriptorFilePath = apiDescriptorDirectory + .appending(component: "\(moduleOutputInfo.name).\(target.moduleTriple.triple).swift.sdkdb") + .intern() + } else { + apiDescriptorFilePath = try Self.computeSupplementaryOutputPath( + &parsedOptions, type: .jsonAPIDescriptor, isOutputOptions: [], + outputPath: .emitApiDescriptorPath, + compilerOutputType: compilerOutputType, + compilerMode: compilerMode, + emitModuleSeparately: emitModuleSeparately, + outputFileMap: outputFileMap, + moduleName: moduleOutputInfo.name) + } + // --------------------- // Swift interface paths let swiftInterfacePath = try Self.computeSupplementaryOutputPath( @@ -492,6 +513,7 @@ public struct Driver { swiftPrivateInterfacePath: swiftPrivateInterfacePath, swiftPackageInterfacePath: swiftPackageInterfacePath, moduleSourceInfoPath: moduleSourceInfoPath) + apiDescriptorFilePath: apiDescriptorFilePath) } /// Structure storing paths to supplemental outputs for the target module @@ -509,8 +531,6 @@ public struct Driver { /// Path to the module's digester baseline file. let digesterBaselinePath: VirtualPath.Handle? - /// Path to the emitted API descriptor file. - let apiDescriptorFilePath: VirtualPath.Handle? /// The mode the API digester should run in. let digesterMode: DigesterMode @@ -1151,6 +1171,13 @@ public struct Driver { moduleOutputPath: moduleOutputInfo.output?.outputPath, fileSystem: self.fileSystem) + var apiDescriptorDirectory: VirtualPath? = nil + if let apiDescriptorDirectoryEnvVar = env["TAPI_SDKDB_OUTPUT_PATH"] { + apiDescriptorDirectory = try VirtualPath(path: apiDescriptorDirectoryEnvVar) + } else if let ldTraceFileEnvVar = env["LD_TRACE_FILE"] { + apiDescriptorDirectory = try VirtualPath(path: ldTraceFileEnvVar).parentDirectory.appending(component: "SDKDB") + } + self.moduleOutputPaths = try Self.computeModuleOutputPaths( &parsedOptions, moduleName: moduleOutputInfo.name, @@ -1161,8 +1188,11 @@ public struct Driver { emitModuleSeparately: emitModuleSeparately, outputFileMap: self.outputFileMap, projectDirectory: projectDirectory) + apiDescriptorDirectory: apiDescriptorDirectory, + target: frontendTargetInfo.target) if let variantModuleOutputInfo = self.variantModuleOutputInfo { + let targetVariant = self.frontendTargetInfo.targetVariant { self.variantModuleOutputPaths = try Self.computeModuleOutputPaths( &parsedOptions, moduleName: variantModuleOutputInfo.name, @@ -1172,7 +1202,9 @@ public struct Driver { compilerMode: compilerMode, emitModuleSeparately: true, // variant module is always independent outputFileMap: self.outputFileMap, - projectDirectory: projectDirectory) + projectDirectory: projectDirectory, + apiDescriptorDirectory: apiDescriptorDirectory, + target: targetVariant) } else { self.variantModuleOutputPaths = nil } @@ -1210,27 +1242,6 @@ public struct Driver { outputFileMap: self.outputFileMap, moduleName: moduleOutputInfo.name) - var apiDescriptorDirectory: VirtualPath? = nil - if let apiDescriptorDirectoryEnvVar = env["TAPI_SDKDB_OUTPUT_PATH"] { - apiDescriptorDirectory = try VirtualPath(path: apiDescriptorDirectoryEnvVar) - } else if let ldTraceFileEnvVar = env["LD_TRACE_FILE"] { - apiDescriptorDirectory = try VirtualPath(path: ldTraceFileEnvVar).parentDirectory.appending(component: "SDKDB") - } - if let apiDescriptorDirectory = apiDescriptorDirectory { - self.apiDescriptorFilePath = apiDescriptorDirectory - .appending(component: "\(moduleOutputInfo.name).\(frontendTargetInfo.target.moduleTriple.triple).swift.sdkdb") - .intern() - } else { - self.apiDescriptorFilePath = try Self.computeSupplementaryOutputPath( - &parsedOptions, type: .jsonAPIDescriptor, isOutputOptions: [], - outputPath: .emitApiDescriptorPath, - compilerOutputType: compilerOutputType, - compilerMode: compilerMode, - emitModuleSeparately: emitModuleSeparately, - outputFileMap: self.outputFileMap, - moduleName: moduleOutputInfo.name) - } - Self.validateDigesterArgs(&parsedOptions, moduleOutputInfo: moduleOutputInfo, digesterMode: self.digesterMode, diff --git a/Sources/SwiftDriver/Jobs/EmitModuleJob.swift b/Sources/SwiftDriver/Jobs/EmitModuleJob.swift index ee00c3310..1cc990474 100644 --- a/Sources/SwiftDriver/Jobs/EmitModuleJob.swift +++ b/Sources/SwiftDriver/Jobs/EmitModuleJob.swift @@ -38,7 +38,7 @@ extension Driver { } 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) + addSupplementalOutput(path: moduleOutputPaths.apiDescriptorFilePath, flag: "-emit-api-descriptor-path", type: .jsonAPIDescriptor) if isMergeModule { return diff --git a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift index f9f66bd04..bb3b60e83 100644 --- a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift +++ b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift @@ -701,7 +701,7 @@ extension Driver { try addOutputOfType( outputType: .jsonAPIDescriptor, - finalOutputPath: apiDescriptorFilePath, + finalOutputPath: moduleOutputPaths.apiDescriptorFilePath, input: nil, flag: "-emit-api-descriptor-path") } diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index 5c5c52bcd..2e0287732 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -4096,6 +4096,36 @@ final class SwiftDriverTests: XCTestCase { XCTAssert(variantModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftsourceinfo"))))) XCTAssertTrue(variantModuleJob.commandLine.contains(subsequence: [.flag("-o"), .path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftmodule")))])) } + +#if os(macOS) + do { + try withTemporaryDirectory { path in + var env = ProcessEnv.vars + env["LD_TRACE_FILE"] = path.appending(component: ".LD_TRACE").nativePathString(escaped: false) + var driver = try Driver(args: ["swiftc", + "-target", "x86_64-apple-macosx10.14", + "-target-variant", "x86_64-apple-ios13.1-macabi", + "-emit-variant-module-path", "foo.swiftmodule/x86_64-apple-ios13.1-macabi.swiftmodule", + "-enable-library-evolution", + "-emit-module", + "foo.swift"], env: env) + + let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() + let targetModuleJob = plannedJobs[0] + let variantModuleJob = plannedJobs[1] + + XCTAssert(targetModuleJob.commandLine.contains(subsequence: [ + .flag("-emit-api-descriptor-path"), + .path(.absolute(path.appending(components: "SDKDB", "foo.\(driver.frontendTargetInfo.target.moduleTriple.triple).swift.sdkdb"))), + ])) + + XCTAssert(variantModuleJob.commandLine.contains(subsequence: [ + .flag("-emit-api-descriptor-path"), + .path(.absolute(path.appending(components: "SDKDB", "foo.\(driver.frontendTargetInfo.targetVariant!.moduleTriple.triple).swift.sdkdb"))), + ])) + } + } +#endif } func testValidDeprecatedTargetiOS() throws { From f882b3d6440b398fa200aa4b829d76982f394f37 Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Thu, 19 Dec 2024 14:14:48 -0800 Subject: [PATCH 4/5] Wire up all the flags This adds flags for setting the variant module doc path, source info path, api descriptor path, module interface path, private interface path, and package interface path. --- Sources/SwiftDriver/Driver/Driver.swift | 60 +++++++++++++++---- Sources/SwiftOptions/Options.swift | 20 +++++-- Tests/SwiftDriverTests/SwiftDriverTests.swift | 26 ++++++++ 3 files changed, 88 insertions(+), 18 deletions(-) diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index 57573f6c0..57e2f0185 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -409,10 +409,41 @@ public struct Driver { compilerMode: CompilerMode, emitModuleSeparately: Bool, outputFileMap: OutputFileMap?, - projectDirectory: VirtualPath.Handle?) throws -> SupplementalModuleTargetOutputPaths { + projectDirectory: VirtualPath.Handle?, + apiDescriptorDirectory: VirtualPath?, + target: FrontendTargetInfo.Target, + isVariant: Bool) throws -> SupplementalModuleTargetOutputPaths { + struct SupplementalPathOptions { + let moduleDocPath: Option + let sourceInfoPath: Option + let apiDescriptorPath: Option + let moduleInterfacePath: Option + let privateInterfacePath: Option + let packageInterfacePath: Option + + static let targetPathOptions = SupplementalPathOptions( + moduleDocPath: .emitModuleDocPath, + sourceInfoPath: .emitModuleSourceInfoPath, + apiDescriptorPath: .emitApiDescriptorPath, + moduleInterfacePath: .emitModuleInterfacePath, + privateInterfacePath: .emitPrivateModuleInterfacePath, + packageInterfacePath: .emitPackageModuleInterfacePath) + + static let variantTargetPathOptions = SupplementalPathOptions( + moduleDocPath: .emitVariantModuleDocPath, + sourceInfoPath: .emitVariantModuleSourceInfoPath, + apiDescriptorPath: .emitVariantApiDescriptorPath, + moduleInterfacePath: .emitVariantModuleInterfacePath, + privateInterfacePath: .emitVariantPrivateModuleInterfacePath, + packageInterfacePath: .emitVariantPackageModuleInterfacePath) + } + + let pathOptions: SupplementalPathOptions = isVariant ? .variantTargetPathOptions : .targetPathOptions + let moduleDocOutputPath = try Self.computeModuleDocOutputPath( &parsedOptions, moduleOutputPath: moduleOutputInfo.output?.outputPath, + outputOption: pathOptions.moduleDocPath, compilerOutputType: compilerOutputType, compilerMode: compilerMode, outputFileMap: outputFileMap, @@ -421,6 +452,7 @@ public struct Driver { let moduleSourceInfoPath = try Self.computeModuleSourceInfoOutputPath( &parsedOptions, moduleOutputPath: moduleOutputInfo.output?.outputPath, + outputOption: pathOptions.sourceInfoPath, compilerOutputType: compilerOutputType, compilerMode: compilerMode, outputFileMap: outputFileMap, @@ -437,7 +469,7 @@ public struct Driver { } else { apiDescriptorFilePath = try Self.computeSupplementaryOutputPath( &parsedOptions, type: .jsonAPIDescriptor, isOutputOptions: [], - outputPath: .emitApiDescriptorPath, + outputPath: pathOptions.apiDescriptorPath, compilerOutputType: compilerOutputType, compilerMode: compilerMode, emitModuleSeparately: emitModuleSeparately, @@ -449,7 +481,7 @@ public struct Driver { // Swift interface paths let swiftInterfacePath = try Self.computeSupplementaryOutputPath( &parsedOptions, type: .swiftInterface, isOutputOptions: [.emitModuleInterface], - outputPath: .emitModuleInterfacePath, + outputPath: pathOptions.moduleInterfacePath, compilerOutputType: compilerOutputType, compilerMode: compilerMode, emitModuleSeparately: emitModuleSeparately, @@ -458,7 +490,7 @@ public struct Driver { let givenPrivateInterfacePath = try Self.computeSupplementaryOutputPath( &parsedOptions, type: .privateSwiftInterface, isOutputOptions: [], - outputPath: .emitPrivateModuleInterfacePath, + outputPath: pathOptions.privateInterfacePath, compilerOutputType: compilerOutputType, compilerMode: compilerMode, emitModuleSeparately: emitModuleSeparately, @@ -466,7 +498,7 @@ public struct Driver { moduleName: moduleOutputInfo.name) let givenPackageInterfacePath = try Self.computeSupplementaryOutputPath( &parsedOptions, type: .packageSwiftInterface, isOutputOptions: [], - outputPath: .emitPackageModuleInterfacePath, + outputPath: pathOptions.packageInterfacePath, compilerOutputType: compilerOutputType, compilerMode: compilerMode, emitModuleSeparately: emitModuleSeparately, @@ -512,7 +544,7 @@ public struct Driver { swiftInterfacePath: swiftInterfacePath, swiftPrivateInterfacePath: swiftPrivateInterfacePath, swiftPackageInterfacePath: swiftPackageInterfacePath, - moduleSourceInfoPath: moduleSourceInfoPath) + moduleSourceInfoPath: moduleSourceInfoPath, apiDescriptorFilePath: apiDescriptorFilePath) } @@ -1187,11 +1219,12 @@ public struct Driver { compilerMode: compilerMode, emitModuleSeparately: emitModuleSeparately, outputFileMap: self.outputFileMap, - projectDirectory: projectDirectory) + projectDirectory: projectDirectory, apiDescriptorDirectory: apiDescriptorDirectory, - target: frontendTargetInfo.target) + target: frontendTargetInfo.target, + isVariant: false) - if let variantModuleOutputInfo = self.variantModuleOutputInfo { + if let variantModuleOutputInfo = self.variantModuleOutputInfo, let targetVariant = self.frontendTargetInfo.targetVariant { self.variantModuleOutputPaths = try Self.computeModuleOutputPaths( &parsedOptions, @@ -1204,7 +1237,8 @@ public struct Driver { outputFileMap: self.outputFileMap, projectDirectory: projectDirectory, apiDescriptorDirectory: apiDescriptorDirectory, - target: targetVariant) + target: targetVariant, + isVariant: true) } else { self.variantModuleOutputPaths = nil } @@ -3779,6 +3813,7 @@ extension Driver { static func computeModuleDocOutputPath( _ parsedOptions: inout ParsedOptions, moduleOutputPath: VirtualPath.Handle?, + outputOption: Option, compilerOutputType: FileType?, compilerMode: CompilerMode, outputFileMap: OutputFileMap?, @@ -3788,7 +3823,7 @@ extension Driver { moduleOutputPath: moduleOutputPath, type: .swiftDocumentation, isOutput: .emitModuleDoc, - outputPath: .emitModuleDocPath, + outputPath: outputOption, compilerOutputType: compilerOutputType, compilerMode: compilerMode, outputFileMap: outputFileMap, @@ -3799,6 +3834,7 @@ extension Driver { static func computeModuleSourceInfoOutputPath( _ parsedOptions: inout ParsedOptions, moduleOutputPath: VirtualPath.Handle?, + outputOption: Option, compilerOutputType: FileType?, compilerMode: CompilerMode, outputFileMap: OutputFileMap?, @@ -3810,7 +3846,7 @@ extension Driver { moduleOutputPath: moduleOutputPath, type: .swiftSourceInfoFile, isOutput: .emitModuleSourceInfo, - outputPath: .emitModuleSourceInfoPath, + outputPath: outputOption, compilerOutputType: compilerOutputType, compilerMode: compilerMode, outputFileMap: outputFileMap, diff --git a/Sources/SwiftOptions/Options.swift b/Sources/SwiftOptions/Options.swift index 4d0c39cca..29ceef11d 100644 --- a/Sources/SwiftOptions/Options.swift +++ b/Sources/SwiftOptions/Options.swift @@ -384,6 +384,13 @@ extension Option { public static let emitTbdPathEQ: Option = Option("-emit-tbd-path=", .joined, alias: Option.emitTbdPath, attributes: [.frontend, .noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant]) public static let emitTbdPath: Option = Option("-emit-tbd-path", .separate, attributes: [.frontend, .noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant], metaVar: "", helpText: "Emit the TBD file to ") public static let emitTbd: Option = Option("-emit-tbd", .flag, attributes: [.frontend, .noInteractive, .supplementaryOutput], helpText: "Emit a TBD file") + public static let emitVariantApiDescriptorPath: Option = Option("-emit-variant-api-descriptor-path", .separate, attributes: [.frontend, .noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant], metaVar: "", helpText: "Output a JSON file describing the target variant module's API to ") + public static let emitVariantModuleDocPath: Option = Option("-emit-variant-module-doc-path", .separate, attributes: [.frontend, .noDriver, .cacheInvariant], metaVar: "", helpText: "Output module documentation file for the target variant to ") + public static let emitVariantModuleInterfacePath: Option = Option("-emit-variant-module-interface-path", .separate, attributes: [.frontend, .noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant], metaVar: "", helpText: "Output module interface file for the target variant to ") + public static let emitVariantModulePath: Option = Option("-emit-variant-module-path", .separate, attributes: [.noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant], metaVar: "", helpText: "Emit an importable module for the target variant at the specified path") + public static let emitVariantModuleSourceInfoPath: Option = Option("-emit-variant-module-source-info-path", .separate, attributes: [.frontend, .noInteractive, .argumentIsPath, .supplementaryOutput], metaVar: "", helpText: "Output module source info file for the target variant to ") + public static let emitVariantPackageModuleInterfacePath: Option = Option("-emit-variant-package-module-interface-path", .separate, attributes: [.frontend, .noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant], metaVar: "", helpText: "Output package module interface file for the target variant to ") + public static let emitVariantPrivateModuleInterfacePath: Option = Option("-emit-variant-private-module-interface-path", .separate, attributes: [.frontend, .noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant], metaVar: "", helpText: "Output private module interface file for the target variant to ") public static let emitVerboseSil: Option = Option("-emit-verbose-sil", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Emit locations during SIL emission") public static let emptyAbiDescriptor: Option = Option("-empty-abi-descriptor", .flag, attributes: [.frontend, .noDriver], helpText: "Avoid printing actual module content into ABI descriptor file") public static let emptyBaseline: Option = Option("-empty-baseline", .flag, attributes: [.noDriver], helpText: "Use empty baseline for diagnostics") @@ -923,10 +930,6 @@ extension Option { public static let Xlinker: Option = Option("-Xlinker", .separate, attributes: [.doesNotAffectIncrementalBuild], helpText: "Specifies an option which should be passed to the linker") public static let Xllvm: Option = Option("-Xllvm", .separate, attributes: [.helpHidden, .frontend], metaVar: "", helpText: "Pass to LLVM.") public static let DASHDASH: Option = Option("--", .remaining, attributes: [.frontend, .doesNotAffectIncrementalBuild]) - - public static let emitVariantModulePath: Option = Option("-emit-variant-module-path", .separate, attributes: [.noInteractive, .supplementaryOutput, .argumentIsPath], helpText: "Emit an importable module for the target variant at the specified path") - public static let emitVariantModuleInterface: Option = Option("-emit-variant-module-interface", .flag, attributes: [.noInteractive, .supplementaryOutput], helpText: "Emit an importable module for the target variant") - } extension Option { @@ -1298,6 +1301,13 @@ extension Option { Option.emitTbdPathEQ, Option.emitTbdPath, Option.emitTbd, + Option.emitVariantApiDescriptorPath, + Option.emitVariantModuleDocPath, + Option.emitVariantModuleInterfacePath, + Option.emitVariantModulePath, + Option.emitVariantModuleSourceInfoPath, + Option.emitVariantPackageModuleInterfacePath, + Option.emitVariantPrivateModuleInterfacePath, Option.emitVerboseSil, Option.emptyAbiDescriptor, Option.emptyBaseline, @@ -1836,8 +1846,6 @@ extension Option { Option.Xlinker, Option.Xllvm, Option.DASHDASH, - Option.emitVariantModulePath, - Option.emitVariantModuleInterface, ] } } diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index 2e0287732..fdcc1328f 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -4125,6 +4125,32 @@ final class SwiftDriverTests: XCTestCase { ])) } } + + do { + var driver = try Driver(args: ["swiftc", + "-target", "x86_64-apple-macosx10.14", + "-target-variant", "x86_64-apple-ios13.1-macabi", + "-emit-variant-module-path", "foo.swiftmodule/x86_64-apple-ios13.1-macabi.swiftmodule", + "-enable-library-evolution", + "-emit-module", + "-emit-api-descriptor-path", "foo.swiftmodule/target.api.json", + "-emit-variant-api-descriptor-path", "foo.swiftmodule/variant.api.json", + "foo.swift"]) + + let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() + let targetModuleJob = plannedJobs[0] + let variantModuleJob = plannedJobs[1] + + XCTAssert(targetModuleJob.commandLine.contains(subsequence: [ + .flag("-emit-api-descriptor-path"), + .path(.relative(try .init(validating: "foo.swiftmodule/target.api.json"))) + ])) + + XCTAssert(variantModuleJob.commandLine.contains(subsequence: [ + .flag("-emit-api-descriptor-path"), + .path(.relative(try .init(validating: "foo.swiftmodule/variant.api.json"))) + ])) + } #endif } From b3c88534de05a41f06eeff2c1c0402e9b5e6e7c4 Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Fri, 20 Dec 2024 15:49:23 -0800 Subject: [PATCH 5/5] Add target variant ABI digester file Now teaching the compiler to emit the ABI JSON file for the target variant module. --- Sources/SwiftDriver/Driver/Driver.swift | 55 ++++++++++----- .../SwiftDriver/Jobs/APIDigesterJobs.swift | 2 +- Sources/SwiftDriver/Jobs/EmitModuleJob.swift | 2 +- .../SwiftDriver/Jobs/FrontendJobHelpers.swift | 2 +- Sources/SwiftDriver/Jobs/MergeModuleJob.swift | 2 +- Sources/SwiftOptions/Options.swift | 2 + Tests/SwiftDriverTests/SwiftDriverTests.swift | 70 +++++++++++++++++++ 7 files changed, 112 insertions(+), 23 deletions(-) diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index 57e2f0185..b8382dbb7 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -398,6 +398,9 @@ public struct Driver { /// Path to the emitted API descriptor file. let apiDescriptorFilePath: VirtualPath.Handle? + + /// Path to the emitted ABI descriptor file. + let abiDescriptorFilePath: TypedVirtualPath? } private static func computeModuleOutputPaths( @@ -411,12 +414,14 @@ public struct Driver { outputFileMap: OutputFileMap?, projectDirectory: VirtualPath.Handle?, apiDescriptorDirectory: VirtualPath?, + supportedFrontendFeatures: Set, target: FrontendTargetInfo.Target, isVariant: Bool) throws -> SupplementalModuleTargetOutputPaths { struct SupplementalPathOptions { let moduleDocPath: Option let sourceInfoPath: Option let apiDescriptorPath: Option + let abiDescriptorPath: Option let moduleInterfacePath: Option let privateInterfacePath: Option let packageInterfacePath: Option @@ -425,6 +430,7 @@ public struct Driver { moduleDocPath: .emitModuleDocPath, sourceInfoPath: .emitModuleSourceInfoPath, apiDescriptorPath: .emitApiDescriptorPath, + abiDescriptorPath: .emitAbiDescriptorPath, moduleInterfacePath: .emitModuleInterfacePath, privateInterfacePath: .emitPrivateModuleInterfacePath, packageInterfacePath: .emitPackageModuleInterfacePath) @@ -433,6 +439,7 @@ public struct Driver { moduleDocPath: .emitVariantModuleDocPath, sourceInfoPath: .emitVariantModuleSourceInfoPath, apiDescriptorPath: .emitVariantApiDescriptorPath, + abiDescriptorPath: .emitVariantAbiDescriptorPath, moduleInterfacePath: .emitVariantModuleInterfacePath, privateInterfacePath: .emitVariantPrivateModuleInterfacePath, packageInterfacePath: .emitVariantPackageModuleInterfacePath) @@ -459,6 +466,31 @@ public struct Driver { moduleName: moduleOutputInfo.name, projectDirectory: projectDirectory) + // --------------------- + // ABI Descriptor Path + func computeABIDescriptorFilePath(target: FrontendTargetInfo.Target, + features: Set) -> TypedVirtualPath? { + guard features.contains(KnownCompilerFeature.emit_abi_descriptor.rawValue) else { + return nil + } + // Emit the descriptor only on platforms where Library Evolution is + // supported + guard target.triple.isDarwin || parsedOptions.hasArgument(.enableLibraryEvolution) else { + return nil + } + guard let moduleOutput = moduleOutputInfo.output else { + return nil + } + + guard let path = try? VirtualPath.lookup(moduleOutput.outputPath) + .replacingExtension(with: .jsonABIBaseline) else { + return nil + } + return TypedVirtualPath(file: path.intern(), type: .jsonABIBaseline) + } + let abiDescriptorFilePath = computeABIDescriptorFilePath(target: target, + features: supportedFrontendFeatures) + // --------------------- // API Descriptor Path let apiDescriptorFilePath: VirtualPath.Handle? @@ -545,7 +577,8 @@ public struct Driver { swiftPrivateInterfacePath: swiftPrivateInterfacePath, swiftPackageInterfacePath: swiftPackageInterfacePath, moduleSourceInfoPath: moduleSourceInfoPath, - apiDescriptorFilePath: apiDescriptorFilePath) + apiDescriptorFilePath: apiDescriptorFilePath, + abiDescriptorFilePath: abiDescriptorFilePath) } /// Structure storing paths to supplemental outputs for the target module @@ -622,24 +655,6 @@ public struct Driver { .appending(component: "Frameworks") } () - lazy var abiDescriptorPath: TypedVirtualPath? = { - guard isFeatureSupported(.emit_abi_descriptor) else { - return nil - } - // Emit the descriptor only on platforms where Library Evolution is supported, - // or opted-into explicitly. - guard targetTriple.isDarwin || parsedOptions.hasArgument(.enableLibraryEvolution) else { - return nil - } - guard let moduleOutput = moduleOutputInfo.output else { - return nil - } - guard let path = try? VirtualPath.lookup(moduleOutput.outputPath).replacingExtension(with: .jsonABIBaseline) else { - return nil - } - return TypedVirtualPath(file: path.intern(), type: .jsonABIBaseline) - }() - public static func isOptionFound(_ opt: String, allOpts: Set) -> Bool { var current = opt while(true) { @@ -1221,6 +1236,7 @@ public struct Driver { outputFileMap: self.outputFileMap, projectDirectory: projectDirectory, apiDescriptorDirectory: apiDescriptorDirectory, + supportedFrontendFeatures: self.supportedFrontendFeatures, target: frontendTargetInfo.target, isVariant: false) @@ -1237,6 +1253,7 @@ public struct Driver { outputFileMap: self.outputFileMap, projectDirectory: projectDirectory, apiDescriptorDirectory: apiDescriptorDirectory, + supportedFrontendFeatures: self.supportedFrontendFeatures, target: targetVariant, isVariant: true) } else { diff --git a/Sources/SwiftDriver/Jobs/APIDigesterJobs.swift b/Sources/SwiftDriver/Jobs/APIDigesterJobs.swift index b09c9072c..84dd11846 100644 --- a/Sources/SwiftDriver/Jobs/APIDigesterJobs.swift +++ b/Sources/SwiftDriver/Jobs/APIDigesterJobs.swift @@ -72,7 +72,7 @@ extension Driver { case .api: return nil case .abi: - return abiDescriptorPath + return moduleOutputPaths.abiDescriptorFilePath } } guard let currentABI = getDescriptorPath(for: mode) else { diff --git a/Sources/SwiftDriver/Jobs/EmitModuleJob.swift b/Sources/SwiftDriver/Jobs/EmitModuleJob.swift index 1cc990474..0a8fde6db 100644 --- a/Sources/SwiftDriver/Jobs/EmitModuleJob.swift +++ b/Sources/SwiftDriver/Jobs/EmitModuleJob.swift @@ -125,7 +125,7 @@ extension Driver { let outputPath = VirtualPath.lookup(moduleOutputPath) commandLine.appendFlag(.o) commandLine.appendPath(outputPath) - if let abiPath = abiDescriptorPath { + if let abiPath = moduleOutputPaths.abiDescriptorFilePath { commandLine.appendFlag(.emitAbiDescriptorPath) commandLine.appendPath(abiPath.file) outputs.append(abiPath) diff --git a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift index bb3b60e83..a2b42c989 100644 --- a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift +++ b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift @@ -692,7 +692,7 @@ extension Driver { input: nil, flag: "-emit-tbd-path") - if let abiDescriptorPath = abiDescriptorPath { + if let abiDescriptorPath = moduleOutputPaths.abiDescriptorFilePath { try addOutputOfType(outputType: .jsonABIBaseline, finalOutputPath: abiDescriptorPath.fileHandle, input: nil, diff --git a/Sources/SwiftDriver/Jobs/MergeModuleJob.swift b/Sources/SwiftDriver/Jobs/MergeModuleJob.swift index dd6717dbf..6b9d89e4b 100644 --- a/Sources/SwiftDriver/Jobs/MergeModuleJob.swift +++ b/Sources/SwiftDriver/Jobs/MergeModuleJob.swift @@ -75,7 +75,7 @@ extension Driver { commandLine.appendFlag(.o) commandLine.appendPath(outputPath) - if let abiPath = abiDescriptorPath { + if let abiPath = moduleOutputPaths.abiDescriptorFilePath { commandLine.appendFlag(.emitAbiDescriptorPath) commandLine.appendPath(abiPath.file) outputs.append(abiPath) diff --git a/Sources/SwiftOptions/Options.swift b/Sources/SwiftOptions/Options.swift index 29ceef11d..4d6f126bf 100644 --- a/Sources/SwiftOptions/Options.swift +++ b/Sources/SwiftOptions/Options.swift @@ -384,6 +384,7 @@ extension Option { public static let emitTbdPathEQ: Option = Option("-emit-tbd-path=", .joined, alias: Option.emitTbdPath, attributes: [.frontend, .noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant]) public static let emitTbdPath: Option = Option("-emit-tbd-path", .separate, attributes: [.frontend, .noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant], metaVar: "", helpText: "Emit the TBD file to ") public static let emitTbd: Option = Option("-emit-tbd", .flag, attributes: [.frontend, .noInteractive, .supplementaryOutput], helpText: "Emit a TBD file") + public static let emitVariantAbiDescriptorPath: Option = Option("-emit-variant-abi-descriptor-path", .separate, attributes: [.frontend, .noDriver, .cacheInvariant], metaVar: "", helpText: "Output the ABI descriptor of current target variant module to ") public static let emitVariantApiDescriptorPath: Option = Option("-emit-variant-api-descriptor-path", .separate, attributes: [.frontend, .noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant], metaVar: "", helpText: "Output a JSON file describing the target variant module's API to ") public static let emitVariantModuleDocPath: Option = Option("-emit-variant-module-doc-path", .separate, attributes: [.frontend, .noDriver, .cacheInvariant], metaVar: "", helpText: "Output module documentation file for the target variant to ") public static let emitVariantModuleInterfacePath: Option = Option("-emit-variant-module-interface-path", .separate, attributes: [.frontend, .noInteractive, .argumentIsPath, .supplementaryOutput, .cacheInvariant], metaVar: "", helpText: "Output module interface file for the target variant to ") @@ -1301,6 +1302,7 @@ extension Option { Option.emitTbdPathEQ, Option.emitTbdPath, Option.emitTbd, + Option.emitVariantAbiDescriptorPath, Option.emitVariantApiDescriptorPath, Option.emitVariantModuleDocPath, Option.emitVariantModuleInterfacePath, diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index fdcc1328f..34e30c14f 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -4090,13 +4090,83 @@ final class SwiftDriverTests: XCTestCase { XCTAssert(targetModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/target.swiftdoc"))))) XCTAssert(targetModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/target.swiftsourceinfo"))))) + XCTAssert(targetModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/target.abi.json"))))) XCTAssertTrue(targetModuleJob.commandLine.contains(subsequence: [.flag("-o"), .path(.relative(try .init(validating: "foo.swiftmodule/target.swiftmodule")))])) XCTAssert(variantModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftdoc"))))) XCTAssert(variantModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftsourceinfo"))))) + XCTAssert(variantModuleJob.commandLine.contains(.path(.relative(try .init(validating: "foo.swiftmodule/variant.abi.json"))))) XCTAssertTrue(variantModuleJob.commandLine.contains(subsequence: [.flag("-o"), .path(.relative(try .init(validating: "foo.swiftmodule/variant.swiftmodule")))])) } + do { + // explicitly emit variant supplemental outputs + var driver = try Driver(args: ["swiftc", + "-target", "x86_64-apple-macosx10.14", + "-target-variant", "x86_64-apple-ios13.1-macabi", + "-enable-library-evolution", + "-package-name", "Susan", + "-emit-module", + "-emit-module-path", "target.swiftmodule", + "-emit-variant-module-path", "variant.swiftmodule", + "-Xfrontend", "-emit-module-doc-path", "-Xfrontend", "target.swiftdoc", + "-Xfrontend", "-emit-variant-module-doc-path", "variant.swiftdoc", + "-emit-module-source-info-path", "target.sourceinfo", + "-emit-variant-module-source-info-path", "variant.sourceinfo", + "-emit-package-module-interface-path", "target.package.swiftinterface", + "-emit-variant-package-module-interface-path", "variant.package.swiftinterface", + "-emit-private-module-interface-path", "target.private.swiftinterface", + "-emit-variant-private-module-interface-path", "variant.private.swiftinterface", + "-emit-module-interface-path", "target.swiftinterface", + "-emit-variant-module-interface-path", "variant.swiftinterface", + "foo.swift"]) + + let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() + // emit module, emit module, compile foo.swift, + // verify target.swiftinterface, + // verify target.private.swiftinterface, + // verify target.package.swiftinterface, + XCTAssertEqual(plannedJobs.count, 6) + + let targetModuleJob: Job = plannedJobs[0] + let variantModuleJob = plannedJobs[1] + + XCTAssertEqual(targetModuleJob.outputs.filter { $0.type == .swiftModule }.last!.file, + try toPath("target.swiftmodule")) + XCTAssertEqual(variantModuleJob.outputs.filter { $0.type == .swiftModule }.last!.file, + try toPath("variant.swiftmodule")) + + XCTAssertEqual(targetModuleJob.outputs.filter { $0.type == .swiftDocumentation }.last!.file, + try toPath("target.swiftdoc")) + XCTAssertEqual(variantModuleJob.outputs.filter { $0.type == .swiftDocumentation }.last!.file, + try toPath("variant.swiftdoc")) + + XCTAssertEqual(targetModuleJob.outputs.filter { $0.type == .swiftSourceInfoFile }.last!.file, + try toPath("target.sourceinfo")) + XCTAssertEqual(variantModuleJob.outputs.filter { $0.type == .swiftSourceInfoFile }.last!.file, + try toPath("variant.sourceinfo")) + + XCTAssertEqual(targetModuleJob.outputs.filter { $0.type == .swiftInterface}.last!.file, + try toPath("target.swiftinterface")) + XCTAssertEqual(variantModuleJob.outputs.filter { $0.type == .swiftInterface}.last!.file, + try toPath("variant.swiftinterface")) + + XCTAssertEqual(targetModuleJob.outputs.filter { $0.type == .privateSwiftInterface}.last!.file, + try toPath("target.private.swiftinterface")) + XCTAssertEqual(variantModuleJob.outputs.filter { $0.type == .privateSwiftInterface}.last!.file, + try toPath("variant.private.swiftinterface")) + + XCTAssertEqual(targetModuleJob.outputs.filter { $0.type == .packageSwiftInterface}.last!.file, + try toPath("target.package.swiftinterface")) + XCTAssertEqual(variantModuleJob.outputs.filter { $0.type == .packageSwiftInterface}.last!.file, + try toPath("variant.package.swiftinterface")) + + XCTAssertEqual(targetModuleJob.outputs.filter { $0.type == .jsonABIBaseline }.last!.file, + try toPath("target.abi.json")) + XCTAssertEqual(variantModuleJob.outputs.filter { $0.type == .jsonABIBaseline}.last!.file, + try toPath("variant.abi.json")) + } + #if os(macOS) do { try withTemporaryDirectory { path in