diff --git a/Sources/CSwiftScan/include/swiftscan_header.h b/Sources/CSwiftScan/include/swiftscan_header.h index 3505cd8db..6ee0282e5 100644 --- a/Sources/CSwiftScan/include/swiftscan_header.h +++ b/Sources/CSwiftScan/include/swiftscan_header.h @@ -122,6 +122,8 @@ typedef struct { (*swiftscan_swift_textual_detail_get_context_hash)(swiftscan_module_details_t); bool (*swiftscan_swift_textual_detail_get_is_framework)(swiftscan_module_details_t); + swiftscan_string_set_t * + (*swiftscan_swift_textual_detail_get_swift_overlay_dependencies)(swiftscan_module_details_t); //=== Swift Binary Module Details query APIs ------------------------------===// swiftscan_string_ref_t diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index 1bfe8c180..6bdcd7779 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -1332,6 +1332,37 @@ extension Driver { return } + // If we're only supposed to explain a dependency on a given module, do so now. + if let explainModuleName = parsedOptions.getLastArgument(.explainModuleDependency) { + guard let dependencyPlanner = explicitDependencyBuildPlanner else { + fatalError("Cannot explain dependency without Explicit Build Planner") + } + guard let dependencyPaths = try dependencyPlanner.explainDependency(explainModuleName.asSingle) else { + diagnosticEngine.emit(.remark("No such module dependency found: '\(explainModuleName.asSingle)'")) + return + } + diagnosticEngine.emit(.remark("Module '\(moduleOutputInfo.name)' depends on '\(explainModuleName.asSingle)'")) + for path in dependencyPaths { + var pathString:String = "" + for (index, moduleId) in path.enumerated() { + switch moduleId { + case .swift(let moduleName): + pathString = pathString + "[" + moduleName + "]" + case .swiftPrebuiltExternal(let moduleName): + pathString = pathString + "[" + moduleName + "]" + case .clang(let moduleName): + pathString = pathString + "[" + moduleName + "](ObjC)" + case .swiftPlaceholder(_): + fatalError("Unexpected unresolved Placeholder module") + } + if index < path.count - 1 { + pathString = pathString + " -> " + } + } + diagnosticEngine.emit(.note(pathString)) + } + } + if parsedOptions.contains(.driverPrintOutputFileMap) { if let outputFileMap = self.outputFileMap { stderrStream <<< outputFileMap.description diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift index 2b48ac680..638ceffe8 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift @@ -124,7 +124,6 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT } for moduleId in swiftDependencies { let moduleInfo = try dependencyGraph.moduleInfo(of: moduleId) - let pcmArgs = try dependencyGraph.swiftModulePCMArgs(of: moduleId) var inputs: [TypedVirtualPath] = [] let outputs: [TypedVirtualPath] = [ TypedVirtualPath(file: moduleInfo.modulePath.path, type: .swiftModule) @@ -488,6 +487,12 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT } } +internal extension ExplicitDependencyBuildPlanner { + func explainDependency(_ dependencyModuleName: String) throws -> [[ModuleDependencyId]]? { + return try dependencyGraph.explainDependency(dependencyModuleName: dependencyModuleName) + } +} + // InterModuleDependencyGraph printing, useful for debugging internal extension InterModuleDependencyGraph { func prettyPrintString() throws -> String { diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift index b8ad622db..cb3b7cbd6 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/CommonDependencyOperations.swift @@ -75,9 +75,22 @@ extension InterModuleDependencyGraph { } // Traverse the set of modules in reverse topological order, assimilating transitive closures for moduleId in topologicalIdList.reversed() { - for dependencyId in try moduleInfo(of: moduleId).directDependencies! { + let moduleInfo = try moduleInfo(of: moduleId) + for dependencyId in moduleInfo.directDependencies! { transitiveClosureMap[moduleId]!.formUnion(transitiveClosureMap[dependencyId]!) } + // For Swift dependencies, their corresponding Swift Overlay dependencies + // and bridging header dependencies are equivalent to direct dependencies. + if case .swift(let swiftModuleDetails) = moduleInfo.details { + let swiftOverlayDependencies = swiftModuleDetails.swiftOverlayDependencies ?? [] + for dependencyId in swiftOverlayDependencies { + transitiveClosureMap[moduleId]!.formUnion(transitiveClosureMap[dependencyId]!) + } + let bridgingHeaderDependencies = swiftModuleDetails.bridgingHeaderDependencies ?? [] + for dependencyId in bridgingHeaderDependencies { + transitiveClosureMap[moduleId]!.formUnion(transitiveClosureMap[dependencyId]!) + } + } } // For ease of use down-the-line, remove the node's self from its set of reachable nodes for (key, _) in transitiveClosureMap { @@ -222,3 +235,48 @@ extension InterModuleDependencyGraph { details: .clang(combinedModuleDetails)) } } + +internal extension InterModuleDependencyGraph { + func explainDependency(dependencyModuleName: String) throws -> [[ModuleDependencyId]]? { + guard modules.contains(where: { $0.key.moduleName == dependencyModuleName }) else { return nil } + var results = [[ModuleDependencyId]]() + try findAllPaths(source: .swift(mainModuleName), + to: dependencyModuleName, + pathSoFar: [.swift(mainModuleName)], + results: &results) + return Array(results) + } + + + private func findAllPaths(source: ModuleDependencyId, + to moduleName: String, + pathSoFar: [ModuleDependencyId], + results: inout [[ModuleDependencyId]]) throws { + let sourceInfo = try moduleInfo(of: source) + // If the source is our target, we are done + guard source.moduleName != moduleName else { + // If the source is a target Swift module, also check if it + // depends on a corresponding Clang module with the same name. + // If it does, add it to the path as well. + var completePath = pathSoFar + if let dependencies = sourceInfo.directDependencies, + dependencies.contains(.clang(moduleName)) { + completePath.append(.clang(moduleName)) + } + results.append(completePath) + return + } + + // If no more dependencies, this is a leaf node, we are done + guard let dependencies = sourceInfo.directDependencies else { + return + } + + for dependency in dependencies { + try findAllPaths(source: dependency, + to: moduleName, + pathSoFar: pathSoFar + [dependency], + results: &results) + } + } +} diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift index 0cce48890..93325319a 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift @@ -79,7 +79,9 @@ extension ModuleDependencyId: Codable { /// Bridging header public struct BridgingHeader: Codable { var path: TextualVirtualPath + /// The source files referenced by the bridging header. var sourceFiles: [TextualVirtualPath] + /// Modules that the bridging header specifically depends on var moduleDependencies: [String] } @@ -92,13 +94,16 @@ public struct SwiftModuleDetails: Codable { public var compiledModuleCandidates: [TextualVirtualPath]? /// The bridging header, if any. - public var bridgingHeaderPath: TextualVirtualPath? - - /// The source files referenced by the bridging header. - public var bridgingSourceFiles: [TextualVirtualPath]? = [] - - /// Modules that the bridging header specifically depends on - public var bridgingHeaderDependencies: [ModuleDependencyId]? = [] + public var bridgingHeader: BridgingHeader? + public var bridgingHeaderPath: TextualVirtualPath? { + bridgingHeader?.path + } + public var bridgingSourceFiles: [TextualVirtualPath]? { + bridgingHeader?.sourceFiles + } + public var bridgingHeaderDependencies: [ModuleDependencyId]? { + bridgingHeader?.moduleDependencies.map { .clang($0) } + } /// Options to the compile command public var commandLine: [String]? = [] @@ -115,6 +120,9 @@ public struct SwiftModuleDetails: Codable { /// A flag to indicate whether or not this module is a framework. public var isFramework: Bool? + + /// A set of Swift Overlays of Clang Module Dependencies + var swiftOverlayDependencies: [ModuleDependencyId]? } /// Details specific to Swift placeholder dependencies. diff --git a/Sources/SwiftDriver/Jobs/Planning.swift b/Sources/SwiftDriver/Jobs/Planning.swift index bf74de97b..7154fe35d 100644 --- a/Sources/SwiftDriver/Jobs/Planning.swift +++ b/Sources/SwiftDriver/Jobs/Planning.swift @@ -140,7 +140,8 @@ extension Driver { IncrementalCompilationState.InitialStateForPlanning?) throws -> InterModuleDependencyGraph? { let interModuleDependencyGraph: InterModuleDependencyGraph? - if parsedOptions.contains(.driverExplicitModuleBuild) { + if parsedOptions.contains(.driverExplicitModuleBuild) || + parsedOptions.contains(.explainModuleDependency) { interModuleDependencyGraph = try initialIncrementalState?.maybeUpToDatePriorInterModuleDependencyGraph ?? gatherModuleDependencies() @@ -195,13 +196,15 @@ extension Driver { dependencyGraph: InterModuleDependencyGraph?, addJob: (Job) -> Void) throws { - // If asked, add jobs to precompile module dependencies - guard parsedOptions.contains(.driverExplicitModuleBuild) else { return } guard let resolvedDependencyGraph = dependencyGraph else { - fatalError("Attempting to plan explicit dependency build without a dependency graph") + return } let modulePrebuildJobs = try generateExplicitModuleDependenciesJobs(dependencyGraph: resolvedDependencyGraph) + // If asked, add jobs to precompile module dependencies. Otherwise exit. + // We may have a dependency graph but not be required to add pre-compile jobs to the build plan, + // for example when `-explain-dependency` is being used. + guard parsedOptions.contains(.driverExplicitModuleBuild) else { return } modulePrebuildJobs.forEach(addJob) } diff --git a/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift b/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift index de29e5c02..531bf628a 100644 --- a/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift +++ b/Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift @@ -123,7 +123,8 @@ private extension SwiftScan { guard let moduleDetailsRef = api.swiftscan_module_info_get_details(moduleInfoRef) else { throw DependencyScanningError.missingField("modules[\(moduleId)].details") } - let details = try constructModuleDetails(from: moduleDetailsRef) + let details = try constructModuleDetails(from: moduleDetailsRef, + moduleAliases: moduleAliases) return (moduleId, ModuleInfo(modulePath: modulePath, sourceFiles: sourceFiles, directDependencies: directDependencies, @@ -133,12 +134,14 @@ private extension SwiftScan { /// From a reference to a binary-format module info details object info returned by libSwiftScan, /// construct an instance of an `ModuleInfo`.Details as used by the driver. /// The object returned by libSwiftScan is a union so ensure to execute dependency-specific queries. - func constructModuleDetails(from moduleDetailsRef: swiftscan_module_details_t) + func constructModuleDetails(from moduleDetailsRef: swiftscan_module_details_t, + moduleAliases: [String: String]?) throws -> ModuleInfo.Details { let moduleKind = api.swiftscan_module_detail_get_kind(moduleDetailsRef) switch moduleKind { case SWIFTSCAN_DEPENDENCY_INFO_SWIFT_TEXTUAL: - return .swift(try constructSwiftTextualModuleDetails(from: moduleDetailsRef)) + return .swift(try constructSwiftTextualModuleDetails(from: moduleDetailsRef, + moduleAliases: moduleAliases)) case SWIFTSCAN_DEPENDENCY_INFO_SWIFT_BINARY: return .swiftPrebuiltExternal(try constructSwiftBinaryModuleDetails(from: moduleDetailsRef)) case SWIFTSCAN_DEPENDENCY_INFO_SWIFT_PLACEHOLDER: @@ -151,7 +154,8 @@ private extension SwiftScan { } /// Construct a `SwiftModuleDetails` from a `swiftscan_module_details_t` reference - func constructSwiftTextualModuleDetails(from moduleDetailsRef: swiftscan_module_details_t) + func constructSwiftTextualModuleDetails(from moduleDetailsRef: swiftscan_module_details_t, + moduleAliases: [String: String]?) throws -> SwiftModuleDetails { let moduleInterfacePath = try getOptionalPathDetail(from: moduleDetailsRef, @@ -168,6 +172,15 @@ private extension SwiftScan { let bridgingHeaderDependencies = try getOptionalStringArrayDetail(from: moduleDetailsRef, using: api.swiftscan_swift_textual_detail_get_bridging_module_dependencies) + let bridgingHeader: BridgingHeader? + if let resolvedBridgingHeaderPath = bridgingHeaderPath { + bridgingHeader = BridgingHeader(path: resolvedBridgingHeaderPath, + sourceFiles: bridgingSourceFiles ?? [], + moduleDependencies: bridgingHeaderDependencies ?? []) + } else { + bridgingHeader = nil + } + let commandLine = try getOptionalStringArrayDetail(from: moduleDetailsRef, using: api.swiftscan_swift_textual_detail_get_command_line) @@ -180,15 +193,25 @@ private extension SwiftScan { using: api.swiftscan_swift_textual_detail_get_context_hash) let isFramework = api.swiftscan_swift_textual_detail_get_is_framework(moduleDetailsRef) + // Decode all dependencies of this module + let swiftOverlayDependencies: [ModuleDependencyId]? + if supportsSeparateSwiftOverlayDependencies(), + let encodedOverlayDepsRef = api.swiftscan_swift_textual_detail_get_swift_overlay_dependencies(moduleDetailsRef) { + let encodedOverlayDependencies = try toSwiftStringArray(encodedOverlayDepsRef.pointee) + swiftOverlayDependencies = + try encodedOverlayDependencies.map { try decodeModuleNameAndKind(from: $0, moduleAliases: moduleAliases) } + } else { + swiftOverlayDependencies = nil + } + return SwiftModuleDetails(moduleInterfacePath: moduleInterfacePath, compiledModuleCandidates: compiledModuleCandidates, - bridgingHeaderPath: bridgingHeaderPath, - bridgingSourceFiles: bridgingSourceFiles, - bridgingHeaderDependencies: bridgingHeaderDependencies?.map { .clang($0) }, + bridgingHeader: bridgingHeader, commandLine: commandLine, contextHash: contextHash, extraPcmArgs: extraPcmArgs, - isFramework: isFramework) + isFramework: isFramework, + swiftOverlayDependencies: swiftOverlayDependencies) } /// Construct a `SwiftPrebuiltExternalModuleDetails` from a `swiftscan_module_details_t` reference diff --git a/Sources/SwiftDriver/SwiftScan/SwiftScan.swift b/Sources/SwiftDriver/SwiftScan/SwiftScan.swift index 9ddf2c30f..9323de615 100644 --- a/Sources/SwiftDriver/SwiftScan/SwiftScan.swift +++ b/Sources/SwiftDriver/SwiftScan/SwiftScan.swift @@ -263,6 +263,10 @@ internal extension swiftscan_diagnostic_severity_t { func resetScannerCache() { api.swiftscan_scanner_cache_reset(scanner) } + + @_spi(Testing) public func supportsSeparateSwiftOverlayDependencies() -> Bool { + return api.swiftscan_swift_textual_detail_get_swift_overlay_dependencies != nil + } @_spi(Testing) public func supportsScannerDiagnostics() -> Bool { return api.swiftscan_scanner_diagnostics_query != nil && @@ -419,6 +423,10 @@ private extension swiftscan_functions_t { self.swiftscan_swift_binary_detail_get_is_framework = try loadOptional("swiftscan_swift_binary_detail_get_is_framework") + // Swift Overlay Dependencies + self.swiftscan_swift_textual_detail_get_swift_overlay_dependencies = + try loadOptional("swiftscan_swift_textual_detail_get_swift_overlay_dependencies") + // MARK: Required Methods func loadRequired(_ symbol: String) throws -> T { guard let sym: T = Loader.lookup(symbol: symbol, in: swiftscan) else { diff --git a/Sources/SwiftOptions/Options.swift b/Sources/SwiftOptions/Options.swift index 6354da666..e4af9cc3e 100644 --- a/Sources/SwiftOptions/Options.swift +++ b/Sources/SwiftOptions/Options.swift @@ -20,6 +20,7 @@ extension Option { public static let accessNotesPathEQ: Option = Option("-access-notes-path=", .joined, alias: Option.accessNotesPath, attributes: [.frontend, .argumentIsPath]) public static let accessNotesPath: Option = Option("-access-notes-path", .separate, attributes: [.frontend, .argumentIsPath], helpText: "Specify YAML file to override attributes on Swift declarations in this module") public static let aliasModuleNamesInModuleInterface: Option = Option("-alias-module-names-in-module-interface", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "When emitting a module interface, disambiguate modules using distinct alias names") + public static let allowUnstableCacheKeyForTesting: Option = Option("-allow-unstable-cache-key-for-testing", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Allow compilation caching with unstable inputs for testing purpose") public static let allowableClient: Option = Option("-allowable-client", .separate, attributes: [.frontend], metaVar: "", helpText: "Module names that are allowed to import this module") public static let analyzeRequirementMachine: Option = Option("-analyze-requirement-machine", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Print out requirement machine statistics at the end of the compilation job") public static let apiDiffDataDir: Option = Option("-api-diff-data-dir", .separate, attributes: [.frontend, .noInteractive, .doesNotAffectIncrementalBuild, .argumentIsPath], metaVar: "", helpText: "Load platform and version specific API migration data files from . Ignored if -api-diff-data-file is specified.") @@ -329,6 +330,7 @@ extension Option { public static let enableBatchMode: Option = Option("-enable-batch-mode", .flag, attributes: [.helpHidden, .frontend, .noInteractive], helpText: "Enable combining frontend jobs into batches") public static let enableBridgingPch: Option = Option("-enable-bridging-pch", .flag, attributes: [.helpHidden], helpText: "Enable automatic generation of bridging PCH files") public static let enableBuiltinModule: Option = Option("-enable-builtin-module", .flag, attributes: [.frontend, .moduleInterface], helpText: "Enables the explicit import of the Builtin module") + public static let enableCas: Option = Option("-enable-cas", .flag, attributes: [.frontend, .noDriver], helpText: "Enable CAS for swift-frontend") public static let enableCollocateMetadataFunctions: Option = Option("-enable-collocate-metadata-functions", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Enable collocate metadata functions") public static let enableColocateTypeDescriptors: Option = Option("-enable-colocate-type-descriptors", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Enable colocate type descriptors") public static let enableConformanceAvailabilityErrors: Option = Option("-enable-conformance-availability-errors", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Diagnose conformance availability violations as errors") @@ -418,13 +420,14 @@ extension Option { public static let driverExperimentalExplicitModuleBuild: Option = Option("-experimental-explicit-module-build", .flag, alias: Option.driverExplicitModuleBuild, attributes: [.helpHidden], helpText: "Prebuild module dependencies to make them explicit") 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 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 ExperimentalPerformanceAnnotations: Option = Option("-experimental-performance-annotations", .flag, attributes: [.helpHidden, .frontend], helpText: "Enable experimental performance annotations") + public static let ExperimentalPerformanceAnnotations: Option = Option("-experimental-performance-annotations", .flag, attributes: [.helpHidden, .frontend], helpText: "Deprecated, has no effect") public static let experimentalPrintFullConvention: Option = Option("-experimental-print-full-convention", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "When emitting a module interface or SIL, emit additional @convention arguments, regardless of whether they were written in the source. Also requires -use-clang-function-types to be enabled.") public static let experimentalSkipAllFunctionBodies: Option = Option("-experimental-skip-all-function-bodies", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Skip type-checking function bodies and all SIL generation") public static let experimentalSkipNonInlinableFunctionBodiesWithoutTypes: Option = Option("-experimental-skip-non-inlinable-function-bodies-without-types", .flag, attributes: [.helpHidden, .frontend], helpText: "Skip work on non-inlinable function bodies that do not declare nested types") public static let experimentalSkipNonInlinableFunctionBodies: Option = Option("-experimental-skip-non-inlinable-function-bodies", .flag, attributes: [.helpHidden, .frontend], helpText: "Skip type-checking and SIL generation for non-inlinable function bodies") public static let experimentalSpiImports: Option = Option("-experimental-spi-imports", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Enable experimental support for SPI imports") public static let experimentalSpiOnlyImports: Option = Option("-experimental-spi-only-imports", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Enable use of @_spiOnly imports") + public static let explainModuleDependency: Option = Option("-explain-module-dependency", .separate, attributes: [], helpText: "Emit remark/notes describing why compilaiton may depend on a module with a given name.") public static let explicitDependencyGraphFormat: Option = Option("-explicit-dependency-graph-format=", .joined, attributes: [.helpHidden, .doesNotAffectIncrementalBuild], helpText: "Specify the explicit dependency graph output format to either 'json' or 'dot'") public static let explicitInterfaceModuleBuild: Option = Option("-explicit-interface-module-build", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Use the specified command-line to build the module from interface, instead of flags specified in the interface") public static let driverExplicitModuleBuild: Option = Option("-explicit-module-build", .flag, attributes: [.helpHidden], helpText: "Prebuild module dependencies to make them explicit") @@ -792,6 +795,7 @@ extension Option { Option.accessNotesPathEQ, Option.accessNotesPath, Option.aliasModuleNamesInModuleInterface, + Option.allowUnstableCacheKeyForTesting, Option.allowableClient, Option.analyzeRequirementMachine, Option.apiDiffDataDir, @@ -1101,6 +1105,7 @@ extension Option { Option.enableBatchMode, Option.enableBridgingPch, Option.enableBuiltinModule, + Option.enableCas, Option.enableCollocateMetadataFunctions, Option.enableColocateTypeDescriptors, Option.enableConformanceAvailabilityErrors, @@ -1197,6 +1202,7 @@ extension Option { Option.experimentalSkipNonInlinableFunctionBodies, Option.experimentalSpiImports, Option.experimentalSpiOnlyImports, + Option.explainModuleDependency, Option.explicitDependencyGraphFormat, Option.explicitInterfaceModuleBuild, Option.driverExplicitModuleBuild, diff --git a/Sources/makeOptions/makeOptions.cpp b/Sources/makeOptions/makeOptions.cpp index a4ab3d4b5..8dc101d6d 100644 --- a/Sources/makeOptions/makeOptions.cpp +++ b/Sources/makeOptions/makeOptions.cpp @@ -64,6 +64,7 @@ enum SwiftFlags { SwiftAPIDigesterOption = (1 << 17), NewDriverOnlyOption = (1 << 18), ModuleInterfaceOptionIgnorable = (1 << 19), + ModuleInterfaceOptionIgnorablePrivate = (1 << 20) }; static std::set swiftKeywords = { "internal", "static" }; diff --git a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift index f506e360b..690b4e575 100644 --- a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift +++ b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift @@ -1613,6 +1613,53 @@ final class ExplicitModuleBuildTests: XCTestCase { XCTAssertEqual(moduleMap[1].sourceInfoPath!.path.description, "B.swiftsourceinfo") XCTAssertEqual(moduleMap[1].isFramework, false) } + + + func testTraceDependency() throws { + try withTemporaryDirectory { path in + try localFileSystem.changeCurrentWorkingDirectory(to: path) + let moduleCachePath = path.appending(component: "ModuleCache") + try localFileSystem.createDirectory(moduleCachePath) + let main = path.appending(component: "testTraceDependency.swift") + try localFileSystem.writeFileContents(main) { + $0 <<< "import C;" + $0 <<< "import E;" + $0 <<< "import G;" + } + + let cHeadersPath: AbsolutePath = + testInputsPath.appending(component: "ExplicitModuleBuilds") + .appending(component: "CHeaders") + let swiftModuleInterfacesPath: AbsolutePath = + testInputsPath.appending(component: "ExplicitModuleBuilds") + .appending(component: "Swift") + let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? [] + var driver = try Driver(args: ["swiftc", + "-I", cHeadersPath.nativePathString(escaped: true), + "-I", swiftModuleInterfacesPath.nativePathString(escaped: true), + "-explicit-module-build", "-v", + "-module-cache-path", moduleCachePath.nativePathString(escaped: true), + "-working-directory", path.nativePathString(escaped: true), + "-explain-module-dependency", "A", + main.nativePathString(escaped: true)] + sdkArgumentsForTesting, + env: ProcessEnv.vars) + let jobs = try driver.planBuild() + try driver.run(jobs: jobs) + XCTAssertTrue(!driver.diagnosticEngine.diagnostics.isEmpty) + XCTAssertTrue(driver.diagnosticEngine.diagnostics.contains { $0.behavior == .remark && + $0.message.text == "Module 'testTraceDependency' depends on 'A'"}) + + for diag in driver.diagnosticEngine.diagnostics { + print(diag.behavior) + print(diag.message) + } + XCTAssertTrue(driver.diagnosticEngine.diagnostics.contains { $0.behavior == .note && + $0.message.text == "[testTraceDependency] -> [A] -> [A](ObjC)"}) + XCTAssertTrue(driver.diagnosticEngine.diagnostics.contains { $0.behavior == .note && + $0.message.text == "[testTraceDependency] -> [C](ObjC) -> [B](ObjC) -> [A](ObjC)"}) + } + } + // We only care about prebuilt modules in macOS. #if os(macOS) func testPrebuiltModuleGenerationJobs() throws {