// // Generation.swift // Adwaita // // Created by david-swift on 14.01.24. // import Foundation /// The generation executable. @main struct Generation { /// The generation configuration. var configuration: GenerationConfiguration = .init() /// The static main function. static func main() throws { let generation = Self() try generation.run() } /// The main function. func run() throws { removeOldFiles() guard let gtkDefinitions = getGtkDefinitions(path: configuration.gtkGirFilePath) else { print("failed to get Gtk definitions") return } guard let adwDefinitions = getAdwDefinitions(path: configuration.adwGirFilePath) else { print("failed to get Adw definitions") return } for `class` in gtkDefinitions.namespace.classes { if let config = configuration.gtkWidgets.first(where: { $0.class == `class`.name }) { createFile( class: `class`, config: config, namespace: gtkDefinitions.namespace, configs: configuration.gtkWidgets ) } } for `class` in adwDefinitions.namespace.classes { if let config = configuration.adwWidgets.first(where: { $0.class == `class`.name }) { createFile( class: `class`, config: config, namespace: adwDefinitions.namespace, configs: configuration.adwWidgets ) } } } /// Remove the old files in the generation directory. func removeOldFiles() { if let fileURLs = try? FileManager.default.contentsOfDirectory( at: .init(fileURLWithPath: configuration.folder), includingPropertiesForKeys: nil, options: .skipsHiddenFiles ) { for fileURL in fileURLs where fileURL.pathExtension == "swift" { print("Removing \(fileURL.lastPathComponent)") try? FileManager.default.removeItem(at: fileURL) } } } /// Get the definitions of a GIR file at a specified path. /// - Parameter path: The path. /// - Returns: The GIR data. func getGtkDefinitions(path: String) -> GIR? { var path = path let girDir = getGtkGirDir() path = path.replacingOccurrences(of: GenerationConfiguration.gtkIncludeDir, with: girDir) let data = (try? Data(contentsOf: .init(fileURLWithPath: path))) ?? .init() return try? GIR.decodeGIR(data) } /// Get the definitions of a GIR file at a specified path. /// - Parameter path: The path. /// - Returns: The GIR data. func getAdwDefinitions(path: String) -> GIR? { var path = path let girDir = getAdwGirDir() path = path.replacingOccurrences(of: GenerationConfiguration.adwIncludeDir, with: girDir) let data = (try? Data(contentsOf: .init(fileURLWithPath: path))) ?? .init() return try? GIR.decodeGIR(data) } /// Get the directory of the gir files. /// - Returns: The path. func getGtkGirDir() -> String { let process = Process() process.executableURL = .init(fileURLWithPath: "/bin/bash") process.arguments = ["-c", "pkg-config --variable=includedir gtk4"] let pipe = Pipe() process.standardOutput = pipe try? process.run() let data = pipe.fileHandleForReading.readDataToEndOfFile() return .init(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" } /// Get the directory of the gir files. /// - Returns: The path. func getAdwGirDir() -> String { let process = Process() process.executableURL = .init(fileURLWithPath: "/bin/bash") process.arguments = ["-c", "pkg-config --variable=includedir libadwaita-1"] let pipe = Pipe() process.standardOutput = pipe try? process.run() let data = pipe.fileHandleForReading.readDataToEndOfFile() return .init(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" } /// Create a file containing a class. /// - Parameters: /// - class: The class. /// - config: The widget configuration. /// - namespace: The namespace. /// - configs: All the available configs. func createFile(`class`: Class, config: WidgetConfiguration, namespace: Namespace, configs: [WidgetConfiguration]) { print("Generating \(config.name ?? `class`.name).swift") let path = "\(configuration.folder)\(config.name ?? config.class).swift" let data = `class` .generate(config: config, genConfig: configuration, namespace: namespace, configs: configs) .data(using: .utf8) try? data?.write(to: .init(fileURLWithPath: path)) } }