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") } }