diff --git a/Sources/SwiftGtkGenCore/MultiPackageAnalysis.swift b/Sources/SwiftGtkGenCore/MultiPackageAnalysis.swift index c86aa00..6cae7bf 100644 --- a/Sources/SwiftGtkGenCore/MultiPackageAnalysis.swift +++ b/Sources/SwiftGtkGenCore/MultiPackageAnalysis.swift @@ -1,8 +1,11 @@ import Foundation /// Result of analyzing multiple GIR repositories together. +/// /// Resolves the full cross-package dependency graph and determines -/// which `import` statements each generated file needs. +/// which `import` statements each generated file needs. Used as input +/// to per-package code generation (via `packageConfigs`) and for +/// producing `@_exported import` umbrella re-exports in each module. public struct MultiPackageAnalysis { /// Maps Swift module name → parsed Repository. public let repositories: [String: Repository] @@ -22,11 +25,22 @@ public struct MultiPackageAnalysis { /// dependencies (i.e., modules accessed transitively through a direct /// dep that re-exports them). Always empty in the re-export approach /// because transitive types come through re-exports. + /// + /// Reserved for non-re-export approaches where transitive types require + /// explicit imports. Currently empty in the re-export lattice design. public let implicitImports: [String: Set] /// Per-package `GenerationConfig` for driving per-type code generation. public let packageConfigs: [String: GenerationConfig] + /// Creates a new multi-package analysis result. + /// + /// - Parameters: + /// - repositories: Parsed GIR repositories keyed by Swift module name. + /// - directDependencies: Direct dependencies derived from `` elements. + /// - transitiveDependencies: Transitive closure for `@_exported import` re-exports. + /// - implicitImports: Implicit imports (empty in the re-export lattice design). + /// - packageConfigs: Per-package `GenerationConfig` for code generation. public init(repositories: [String: Repository], directDependencies: [String: Set], transitiveDependencies: [String: Set], @@ -43,13 +57,27 @@ public struct MultiPackageAnalysis { /// Analyzes multiple GIR `Repository` instances to produce a /// `MultiPackageAnalysis` with resolved cross-package imports. public struct MultiPackageAnalyzer { + /// Configuration describing the monorepo's packages and their GIR paths. public let config: MonorepoConfig + /// Creates a new multi-package analyzer. + /// + /// - Parameter config: The monorepo configuration listing all packages to analyze. public init(config: MonorepoConfig) { self.config = config } /// Parses all GIR files and resolves the dependency graph. + /// + /// Each package's `.gir` file is parsed, direct dependencies are extracted + /// from `` elements, and transitive closures are computed via + /// memoized depth-first traversal assuming a DAG (no dependency cycles). + /// Returns a fully resolved `MultiPackageAnalysis` with per-package + /// `GenerationConfig` instances suitable for code generation. + /// + /// - Throws: `ParserError` if any `.gir` file is malformed or unreadable. + /// - Returns: A `MultiPackageAnalysis` with parsed repositories, + /// dependency mappings, and generation configurations. public func analyze() throws -> MultiPackageAnalysis { var repositories: [String: Repository] = [:] var directDeps: [String: Set] = [:] @@ -72,6 +100,22 @@ public struct MultiPackageAnalyzer { // direct dep's transitive deps, transitively. var transitiveDeps: [String: Set] = [:] + /// The dependency graph is a DAG (no cycles in GIR include chains). + /// If cycles were present, `visited` detection would cut traversal + /// and the cached result would be incomplete. This implementation + /// is correct for the DAG case only. + /// + /// Computes the transitive closure of dependencies for a module. + /// Uses memoization (`transitiveDeps` cache) to avoid recomputation + /// and a `visited` set to detect cycles (belt-and-suspenders for a + /// DAG-only graph). The result is: all modules reachable from + /// `module` by following direct dependencies transitively, minus + /// `module` itself. + /// + /// - Parameters: + /// - module: The Swift module name to compute transitive deps for. + /// - visited: Set of modules currently on the DFS stack (for cycle detection). + /// - Returns: The transitive dependency set for `module`. func computeTransitive(for module: String, visited: inout Set) -> Set { if let cached = transitiveDeps[module] { return cached } visited.insert(module) @@ -93,8 +137,9 @@ public struct MultiPackageAnalyzer { _ = computeTransitive(for: moduleName, visited: &visited) } - // implicitImports: in a re-export lattice there are no implicit - // imports — every needed module is reached via transitive re-exports. + // implicitImports: reserved for non-re-export approaches where + // transitive types require explicit imports. Currently empty in + // the re-export lattice design. var implicitImports: [String: Set] = [:] for moduleName in config.packages.map(\.name) { implicitImports[moduleName] = []