1
0
Fork 0

Replace single-package scaffolding with monorepo Package.swift generation

This commit is contained in:
Brendan Szymanski 2026-07-01 20:27:36 -04:00
parent f6adf61167
commit 5278fe90c3

View file

@ -20,6 +20,7 @@ extension CodeGenerator {
/// - repository: The parsed GIR repository containing namespace metadata, /// - repository: The parsed GIR repository containing namespace metadata,
/// `<c:include>`, `<include>`, and `<package>` values. /// `<c:include>`, `<include>`, and `<package>` values.
/// - Returns: A dictionary of relative path file content. /// - Returns: A dictionary of relative path file content.
@available(*, deprecated, message: "Use generateMonorepoScaffolding(analysis:) for monorepo packages.")
public static func generatePackageScaffolding(config: GenerationConfig, repository: Repository) -> [String: String] { public static func generatePackageScaffolding(config: GenerationConfig, repository: Repository) -> [String: String] {
var files: [String: String] = [:] var files: [String: String] = [:]
let cName = "C\(config.library)" let cName = "C\(config.library)"
@ -114,6 +115,122 @@ extension CodeGenerator {
return files return files
} }
// MARK: - Monorepo Package Scaffolding Generation
/// Generates all scaffolding files for the monorepo: Package.swift,
/// C bridge module maps and umbrella headers, and re-export files.
///
/// - Parameter analysis: The resolved multi-package analysis.
/// - Returns: A dictionary of relative path file content.
public static func generateMonorepoScaffolding(analysis: MultiPackageAnalysis) -> [String: String] {
var files: [String: String] = [:]
// Re-export umbrella files
let umbrellas = generateReexportUmbrellas(analysis: analysis)
for (path, content) in umbrellas {
files[path] = content
}
// C bridge files
for (moduleName, repo) in analysis.repositories {
let cName = "C\(moduleName)"
let ns = repo.namespaces.first { $0.name == moduleName } ?? repo.namespaces.first
let sharedLibrary = ns?.cSharedLibrary ?? ""
let libraries = sharedLibrary.split(separator: ",").map(String.init)
let allLinkNames = libraries.compactMap { extractLinkName(from: $0) }
let lowerModName = moduleName.lowercased()
let cHeader = repo.cHeaderPath
let umbrellaHeader = cHeader.isEmpty
? "#include <\(lowerModName)/\(lowerModName).h>"
: "#include <\(cHeader)>"
// External C link names from GIR includes
var externalLinks: [String] = []
for include in repo.includedPackages {
let el = include.name.lowercased()
if !externalLinks.contains(el) {
externalLinks.append(el)
}
}
let linkLines = (allLinkNames + externalLinks).map { " link \"\($0)\"" }.joined(separator: "\n")
let moduleMap = """
module \(cName) [system] {
header "\(cName).h"
\(linkLines)
}
"""
files["Sources/\(cName)/module.modulemap"] = moduleMap
files["Sources/\(cName)/\(cName).h"] = umbrellaHeader + "\n"
}
// Monorepo Package.swift
let packageSwift = generateMonorepoPackage(analysis: analysis)
files["Package.swift"] = packageSwift
return files
}
/// Generates the single monorepo Package.swift containing all targets.
private static func generateMonorepoPackage(analysis: MultiPackageAnalysis) -> String {
let moduleNames = analysis.repositories.keys.sorted()
// Products
var products = ""
for name in moduleNames {
products += " .library(name: \"\(name)\", targets: [\"\(name)\"]),\n"
}
// C system library targets
var cTargets = ""
for name in moduleNames {
let cName = "C\(name)"
cTargets += " .systemLibrary(name: \"\(cName)\", path: \"Sources/\(cName)\"),\n"
}
// Swift wrapper targets
var swiftTargets = ""
for name in moduleNames {
let cName = "C\(name)"
let dirDeps = analysis.directDependencies[name] ?? []
var depList = "\"\(cName)\""
for dep in dirDeps.sorted() {
depList += ", \"\(dep)\""
}
swiftTargets += """
.target(
name: "\(name)",
dependencies: [\(depList)],
swiftSettings: swiftSettings
),
"""
}
return """
// swift-tools-version: 6.2
import PackageDescription
let swiftSettings: [SwiftSetting] = [
.enableExperimentalFeature("StrictConcurrency=complete"),
.defaultIsolation(MainActor.self),
.enableUpcomingFeature("NonisolatedNonsendingByDefault"),
]
let package = Package(
name: "gtk-swift",
platforms: [.macOS(.v14)],
products: [
\(products) ],
targets: [
\(cTargets)
\(swiftTargets) ]
)
"""
}
/// Extracts link name from a shared-library string like "libgtk-4.so.1" -> "gtk-4". /// Extracts link name from a shared-library string like "libgtk-4.so.1" -> "gtk-4".
private static func extractLinkName(from sharedLib: String) -> String? { private static func extractLinkName(from sharedLib: String) -> String? {
guard sharedLib.hasPrefix("lib") else { return nil } guard sharedLib.hasPrefix("lib") else { return nil }