1
0
Fork 0
gobject-generator/Sources/swift-gtk-gen/Main.swift
Brendan Szymanski 1330f2bd25 Initial commit: SwiftGtkGen GIR-to-Swift binding generator
Extracted from gtk-swift monorepo. Previous history
contains 38 commits across the full Phase 1 development
including GIR XML parsing, IR model, analysis engine,
code generation, CLI, plugins, and documentation.
2026-07-01 03:27:41 -04:00

154 lines
6.5 KiB
Swift

import Foundation
import SwiftGtkGenCore
/// Command-line interface for the GIR-to-Swift binding generator.
///
/// Parses command-line arguments, invokes the GIR parser, analyzer, and code
/// generator, then writes the resulting Swift source to a file. Designed as
/// the subprocess entry point invoked by both the build tool plugin and the
/// command plugin.
@main
struct SwiftGtkGenCLI {
/// Program entry point. Parses arguments, generates code, and writes output.
static func main() {
let args = parseArguments()
if args.showHelp {
printHelp()
return
}
do {
let parser = GIRParser()
let primaryGIR = try parser.parse(fileURL: URL(fileURLWithPath: args.girFile))
let generateList = args.generateAll ? [] : args.generate
let config = GenerationConfig(
library: args.library,
version: args.version,
girsDirectories: args.girsDirs,
targetDirectory: args.output,
externalLibraries: args.externalLibs,
generate: generateList,
manual: args.manual,
ignore: args.ignore,
objects: []
)
let analyzer = Analyzer(config: config)
let analysis = analyzer.analyze(repository: primaryGIR)
let generator = CodeGenerator(config: config)
let outputDir = URL(fileURLWithPath: args.output)
if args.emitSingleFile {
let output = try generator.generate(repository: primaryGIR, analysis: analysis)
try FileManager.default.createDirectory(at: outputDir, withIntermediateDirectories: true)
let outputFile = outputDir.appendingPathComponent("\(config.library).swift")
try output.write(to: outputFile, atomically: true, encoding: .utf8)
print("Generated \(outputFile.path)")
} else {
let files = try generator.generateFiles(repository: primaryGIR, analysis: analysis)
let sourcesDir = outputDir.appendingPathComponent("Sources/\(config.library)")
try FileManager.default.createDirectory(at: sourcesDir, withIntermediateDirectories: true)
for (fileName, content) in files.sorted(by: { $0.key < $1.key }) {
let fileURL = sourcesDir.appendingPathComponent(fileName)
try content.write(to: fileURL, atomically: true, encoding: .utf8)
print("Generated \(fileURL.path)")
}
}
let scaffolding = CodeGenerator.generatePackageScaffolding(config: config, repository: primaryGIR)
for (relativePath, content) in scaffolding.sorted(by: { $0.key < $1.key }) {
let fileURL = outputDir.appendingPathComponent(relativePath)
try FileManager.default.createDirectory(at: fileURL.deletingLastPathComponent(), withIntermediateDirectories: true)
try content.write(to: fileURL, atomically: true, encoding: .utf8)
print("Generated \(fileURL.path)")
}
} catch {
print("Error: \(error)")
exit(1)
}
}
/// Parsed command-line arguments for the GIR generator CLI.
struct CLIArgs {
var showHelp: Bool = false
var emitSingleFile: Bool = false
var girFile: String = ""
var library: String = ""
var version: String = ""
var output: String = "."
var girsDirs: [String] = []
var externalLibs: [String] = []
var generate: [String] = []
var generateAll: Bool = false
var manual: [String] = []
var ignore: [String] = []
}
/// Parses command-line arguments into a ``CLIArgs`` value.
///
/// Supports the flags `--help`, `--gir-file`, `--library`, `--version`,
/// `--output`, `--girs-dirs`, `--external-libs`, `--generate`, `--manual`,
/// and `--ignore`. Multi-value flags (`--girs-dirs`, etc.) accept
/// comma-separated lists.
///
/// - Returns: A populated ``CLIArgs`` extracted from `CommandLine.arguments`.
static func parseArguments() -> CLIArgs {
var args = Array(CommandLine.arguments.dropFirst())
var cli = CLIArgs()
while let flag = args.first {
args.removeFirst()
switch flag {
case "--emit-single-file":
cli.emitSingleFile = true
case "--help", "-h":
cli.showHelp = true
case "--gir-file":
cli.girFile = args.removeFirst()
case "--library":
cli.library = args.removeFirst()
case "--version":
cli.version = args.removeFirst()
case "--output":
cli.output = args.removeFirst()
case "--girs-dirs":
cli.girsDirs = args.removeFirst().components(separatedBy: ",")
case "--external-libs":
cli.externalLibs = args.removeFirst().components(separatedBy: ",")
case "--generate-all":
cli.generateAll = true
case "--generate":
cli.generate = args.removeFirst().components(separatedBy: ",")
case "--manual":
cli.manual = args.removeFirst().components(separatedBy: ",")
case "--ignore":
cli.ignore = args.removeFirst().components(separatedBy: ",")
default:
break
}
}
return cli
}
/// Prints usage information and the list of accepted flags to stdout.
static func printHelp() {
print("Usage: swift-gtk-gen [options]")
print(" --gir-file PATH Path to the .gir file")
print(" --library NAME GIR library namespace (e.g., GLib)")
print(" --version VER GIR version (e.g., 2.0)")
print(" --output DIR Output directory (default: .)")
print(" --girs-dirs DIRS Comma-separated GIR directories")
print(" --external-libs LIBS Comma-separated external libraries")
print(" --generate TYPES Comma-separated types to generate")
print(" --generate-all Generate all types in the namespace")
print(" --manual TYPES Comma-separated types to mark manual")
print(" --ignore TYPES Comma-separated types to ignore")
print(" --emit-single-file Emit a single file instead of per-type files")
print(" --help, -h Show this help")
}
}