Re-point plugins and re-enable lint gate (E6/E7)
E6: remove build-tool plugin (architecturally incompatible with the monorepo-only CLI); rewrite the command plugin as a thin pass-through to swift-gtk-gen. E7: add swift-format as a dependency, format generated output in-process before writeIfChanged, re-enable swift format lint --strict in compile-gate.sh. CamelCase renderer identifiers (enum/bitfield cases, signal trampoline names, _sgtk_* helpers, __result/__ptr temporaries, hardcoded G_TYPE_* GValue constants) and drop generated block comments to reach zero lint findings across all 6 tiers.
This commit is contained in:
parent
525aefa7aa
commit
57ff84bce5
9 changed files with 194 additions and 285 deletions
51
Package.resolved
Normal file
51
Package.resolved
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
{
|
||||||
|
"originHash" : "624f276a58a2026f3ee556b257210a71bf3544b029d116f439f19c89bba5fee4",
|
||||||
|
"pins" : [
|
||||||
|
{
|
||||||
|
"identity" : "swift-argument-parser",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/apple/swift-argument-parser.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "6a52f3251125d74daf04fcbd5e6f08a75d074382",
|
||||||
|
"version" : "1.8.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "swift-cmark",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/swiftlang/swift-cmark.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "924936d0427cb25a61169739a7660230bffa6ea6",
|
||||||
|
"version" : "0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "swift-format",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/swiftlang/swift-format.git",
|
||||||
|
"state" : {
|
||||||
|
"branch" : "release/6.3",
|
||||||
|
"revision" : "bd706100808e11192661eac5e12255c020005555"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "swift-markdown",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/swiftlang/swift-markdown.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "3c6f9523da3a1ec2fd829673e472d95b8097a3b8",
|
||||||
|
"version" : "0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "swift-syntax",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/swiftlang/swift-syntax.git",
|
||||||
|
"state" : {
|
||||||
|
"branch" : "release/6.3",
|
||||||
|
"revision" : "79e4b74a295b6eb74a8b585e3a39d29e70c1dbd1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version" : 3
|
||||||
|
}
|
||||||
|
|
@ -17,9 +17,11 @@ let package = Package(
|
||||||
platforms: [.macOS(.v14)],
|
platforms: [.macOS(.v14)],
|
||||||
products: [
|
products: [
|
||||||
.executable(name: "swift-gtk-gen", targets: ["swift-gtk-gen"]),
|
.executable(name: "swift-gtk-gen", targets: ["swift-gtk-gen"]),
|
||||||
.plugin(name: "SwiftGtkGenBuildPlugin", targets: ["SwiftGtkGenBuildPlugin"]),
|
|
||||||
.plugin(name: "SwiftGtkGenCommandPlugin", targets: ["SwiftGtkGenCommandPlugin"]),
|
.plugin(name: "SwiftGtkGenCommandPlugin", targets: ["SwiftGtkGenCommandPlugin"]),
|
||||||
],
|
],
|
||||||
|
dependencies: [
|
||||||
|
.package(url: "https://github.com/swiftlang/swift-format.git", branch: "release/6.3")
|
||||||
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.target(
|
.target(
|
||||||
name: "SwiftGtkGenCore",
|
name: "SwiftGtkGenCore",
|
||||||
|
|
@ -28,15 +30,12 @@ let package = Package(
|
||||||
),
|
),
|
||||||
.executableTarget(
|
.executableTarget(
|
||||||
name: "swift-gtk-gen",
|
name: "swift-gtk-gen",
|
||||||
dependencies: ["SwiftGtkGenCore"],
|
dependencies: [
|
||||||
|
"SwiftGtkGenCore",
|
||||||
|
.product(name: "SwiftFormat", package: "swift-format"),
|
||||||
|
],
|
||||||
swiftSettings: concurrencySettings
|
swiftSettings: concurrencySettings
|
||||||
),
|
),
|
||||||
.plugin(
|
|
||||||
name: "SwiftGtkGenBuildPlugin",
|
|
||||||
capability: .buildTool(),
|
|
||||||
dependencies: ["swift-gtk-gen"],
|
|
||||||
path: "Sources/SwiftGtkGenBuildPlugin"
|
|
||||||
),
|
|
||||||
.plugin(
|
.plugin(
|
||||||
name: "SwiftGtkGenCommandPlugin",
|
name: "SwiftGtkGenCommandPlugin",
|
||||||
capability: .command(
|
capability: .command(
|
||||||
|
|
|
||||||
|
|
@ -1,117 +0,0 @@
|
||||||
import PackagePlugin
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// SwiftPM build tool plugin that automatically generates Swift GIR bindings
|
|
||||||
/// during compilation.
|
|
||||||
///
|
|
||||||
/// Uses a two-phase approach: Phase 1 lists expected outputs via
|
|
||||||
/// `swift-gtk-gen --list-output-files`, applying the same `config.toml`
|
|
||||||
/// filtering as Phase 2. The resulting file list is declared to SwiftPM so
|
|
||||||
/// incremental builds track only files that were actually produced. Targets
|
|
||||||
/// without a `config.toml` or `.gir` files are silently skipped.
|
|
||||||
@main
|
|
||||||
struct SwiftGtkGenBuildPlugin: BuildToolPlugin {
|
|
||||||
/// Creates build commands for GIR code generation for the given target.
|
|
||||||
///
|
|
||||||
/// Returns a single `.buildCommand` that runs `swift-gtk-gen` with the
|
|
||||||
/// target's `config.toml` and GIR files as inputs. Phase 1 discovers
|
|
||||||
/// expected output files via `--list-output-files`. If the target has
|
|
||||||
/// no `config.toml` or no `.gir` files, returns an empty array.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - context: The plugin context providing access to tools and work directories.
|
|
||||||
/// - target: The target to generate bindings for.
|
|
||||||
/// - Returns: An array of build commands (empty if no GIR files present).
|
|
||||||
func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
|
|
||||||
let targetDir = target.directoryURL
|
|
||||||
let configURL = targetDir.appending(path: "config.toml")
|
|
||||||
|
|
||||||
guard FileManager.default.fileExists(atPath: configURL.path()) else {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
let executable = try context.tool(named: "swift-gtk-gen")
|
|
||||||
let girFiles = findGIRFiles(in: targetDir)
|
|
||||||
guard let primaryGIR = girFiles.first else { return [] }
|
|
||||||
|
|
||||||
let outputDir = context.pluginWorkDirectoryURL.appending(path: target.name)
|
|
||||||
|
|
||||||
// Phase 1: List expected outputs (config-filtered to match Phase 2)
|
|
||||||
let listProcess = Process()
|
|
||||||
listProcess.executableURL = executable.url
|
|
||||||
listProcess.arguments = [
|
|
||||||
"--gir-file", primaryGIR.path(),
|
|
||||||
"--config-toml", configURL.path(),
|
|
||||||
"--list-output-files",
|
|
||||||
]
|
|
||||||
let listPipe = Pipe()
|
|
||||||
listProcess.standardOutput = listPipe
|
|
||||||
try listProcess.run()
|
|
||||||
listProcess.waitUntilExit()
|
|
||||||
|
|
||||||
guard listProcess.terminationStatus == 0 else {
|
|
||||||
throw SwiftGtkGenPluginError.listingFailed(target.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
let listData = listPipe.fileHandleForReading.readDataToEndOfFile()
|
|
||||||
let listOutput = String(data: listData, encoding: .utf8) ?? ""
|
|
||||||
let outputFiles = listOutput
|
|
||||||
.components(separatedBy: "\n")
|
|
||||||
.filter { !$0.trimmingCharacters(in: .whitespaces).isEmpty }
|
|
||||||
.map { outputDir.appending(path: $0) }
|
|
||||||
|
|
||||||
return [
|
|
||||||
.buildCommand(
|
|
||||||
displayName: "Generating \(target.name) GIR bindings...",
|
|
||||||
executable: executable.url,
|
|
||||||
arguments: [
|
|
||||||
"--gir-file", primaryGIR.path(),
|
|
||||||
"--config-toml", configURL.path(),
|
|
||||||
"--output", outputDir.path(),
|
|
||||||
],
|
|
||||||
inputFiles: [configURL, primaryGIR] + girFiles.dropFirst(),
|
|
||||||
outputFiles: outputFiles
|
|
||||||
)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Recursively finds all `.gir` files in the given directory.
|
|
||||||
///
|
|
||||||
/// Uses a directory enumerator to walk the file tree and collect URLs
|
|
||||||
/// whose path extension is `"gir"`.
|
|
||||||
///
|
|
||||||
/// - Parameter directory: The root directory to search.
|
|
||||||
/// - Returns: An array of URLs pointing to discovered `.gir` files.
|
|
||||||
private func findGIRFiles(in directory: URL) -> [URL] {
|
|
||||||
guard let enumerator = FileManager.default.enumerator(
|
|
||||||
at: directory, includingPropertiesForKeys: [.isRegularFileKey]
|
|
||||||
) else { return [] }
|
|
||||||
var girFiles: [URL] = []
|
|
||||||
for case let fileURL as URL in enumerator {
|
|
||||||
if fileURL.pathExtension == "gir" {
|
|
||||||
girFiles.append(fileURL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return girFiles
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Errors that can occur during build-tool-plugin GIR generation.
|
|
||||||
enum SwiftGtkGenPluginError: Error {
|
|
||||||
/// Output-file listing failed for the specified target name.
|
|
||||||
case listingFailed(String)
|
|
||||||
/// Generation failed for the specified target name.
|
|
||||||
case generationFailed(String)
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SwiftGtkGenPluginError: CustomStringConvertible {
|
|
||||||
/// A human-readable description of the error.
|
|
||||||
var description: String {
|
|
||||||
switch self {
|
|
||||||
case .listingFailed(let target):
|
|
||||||
return "GIR output listing failed for target: \(target)"
|
|
||||||
case .generationFailed(let target):
|
|
||||||
return "GIR generation failed for target: \(target)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,109 +3,74 @@ import Foundation
|
||||||
|
|
||||||
/// SwiftPM command plugin for manual GIR binding regeneration.
|
/// SwiftPM command plugin for manual GIR binding regeneration.
|
||||||
///
|
///
|
||||||
/// Invoked via `swift package generate-gir-bindings`. Iterates over all
|
/// Invoked via `swift package generate-gir-bindings`. Forwards all
|
||||||
/// package targets, checks for a `config.toml` and `.gir` files, and runs
|
/// user-provided arguments directly to the `swift-gtk-gen` monorepo CLI,
|
||||||
/// the `swift-gtk-gen` executable to produce or update generated Swift
|
/// defaulting `--output` to the package directory when the caller omits it.
|
||||||
/// sources in each target's `Generated/` directory. User-provided arguments
|
/// The generator itself decides what to plan, render, and write from the
|
||||||
/// are passed through to the generator (e.g. `--generate Gtk.Widget`).
|
/// monorepo TOML config passed via `--monorepo-config`.
|
||||||
@main
|
@main
|
||||||
struct SwiftGtkGenCommandPlugin: CommandPlugin {
|
struct SwiftGtkGenCommandPlugin: CommandPlugin {
|
||||||
/// Performs GIR code generation for all eligible targets in the package.
|
/// Runs `swift-gtk-gen` as a subprocess with the caller's arguments.
|
||||||
///
|
///
|
||||||
/// For each target that has a `config.toml` and at least one `.gir` file,
|
/// Stdout and stderr are piped through to the caller. Throws if the
|
||||||
/// launches `swift-gtk-gen` as a subprocess with arguments derived from
|
/// subprocess exits non-zero.
|
||||||
/// the target's directory and metadata. Stdout and stderr are piped
|
|
||||||
/// through to the caller. Throws if any subprocess fails.
|
|
||||||
///
|
///
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - context: The plugin context providing access to tools and package targets.
|
/// - context: The plugin context providing access to tools and the package directory.
|
||||||
/// - arguments: User-provided arguments passed through to the generator.
|
/// - arguments: User-provided arguments forwarded to the generator, e.g.
|
||||||
|
/// `--monorepo-config configs/tier1.toml --format-config .swift-format`.
|
||||||
func performCommand(context: PluginContext, arguments: [String]) async throws {
|
func performCommand(context: PluginContext, arguments: [String]) async throws {
|
||||||
|
guard !arguments.isEmpty else {
|
||||||
|
print("Usage: swift package generate-gir-bindings --monorepo-config <cfg> [--skip-report] [--smoke-target] [--format-config <path>]")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let executable = try context.tool(named: "swift-gtk-gen")
|
let executable = try context.tool(named: "swift-gtk-gen")
|
||||||
|
|
||||||
for target in context.package.targets {
|
var procArgs = arguments
|
||||||
let targetDir = target.directoryURL
|
if !procArgs.contains("--output") {
|
||||||
let configURL = targetDir.appending(path: "config.toml")
|
procArgs.append(contentsOf: ["--output", context.package.directoryURL.path()])
|
||||||
|
|
||||||
guard FileManager.default.fileExists(atPath: configURL.path()) else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
let girFiles = findGIRFiles(in: targetDir)
|
|
||||||
guard let primaryGIR = girFiles.first else { continue }
|
|
||||||
|
|
||||||
let outputDir = targetDir.appending(path: "Generated")
|
|
||||||
|
|
||||||
let process = Process()
|
|
||||||
process.executableURL = executable.url
|
|
||||||
|
|
||||||
var procArgs: [String] = [
|
|
||||||
"--gir-file", primaryGIR.path(),
|
|
||||||
"--config-toml", configURL.path(),
|
|
||||||
"--output", outputDir.path(),
|
|
||||||
]
|
|
||||||
if !arguments.isEmpty {
|
|
||||||
procArgs.append(contentsOf: arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
process.arguments = procArgs
|
|
||||||
|
|
||||||
let stdoutPipe = Pipe()
|
|
||||||
let stderrPipe = Pipe()
|
|
||||||
process.standardOutput = stdoutPipe
|
|
||||||
process.standardError = stderrPipe
|
|
||||||
|
|
||||||
try process.run()
|
|
||||||
process.waitUntilExit()
|
|
||||||
|
|
||||||
let stdoutData = stdoutPipe.fileHandleForReading.readDataToEndOfFile()
|
|
||||||
let stderrData = stderrPipe.fileHandleForReading.readDataToEndOfFile()
|
|
||||||
if let stdoutStr = String(data: stdoutData, encoding: .utf8), !stdoutStr.isEmpty {
|
|
||||||
print(stdoutStr, terminator: "")
|
|
||||||
}
|
|
||||||
if let stderrStr = String(data: stderrData, encoding: .utf8), !stderrStr.isEmpty {
|
|
||||||
FileHandle.standardError.write(Data(stderrStr.utf8))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard process.terminationStatus == 0 else {
|
|
||||||
throw SwiftGtkGenPluginError.generationFailed(target.name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Recursively finds all `.gir` files in the given directory.
|
let process = Process()
|
||||||
///
|
process.executableURL = executable.url
|
||||||
/// Uses a directory enumerator to walk the file tree and collect URLs
|
process.arguments = procArgs
|
||||||
/// whose path extension is `"gir"`.
|
|
||||||
///
|
let stdoutPipe = Pipe()
|
||||||
/// - Parameter directory: The root directory to search.
|
let stderrPipe = Pipe()
|
||||||
/// - Returns: An array of URLs pointing to discovered `.gir` files.
|
process.standardOutput = stdoutPipe
|
||||||
private func findGIRFiles(in directory: URL) -> [URL] {
|
process.standardError = stderrPipe
|
||||||
guard let enumerator = FileManager.default.enumerator(
|
|
||||||
at: directory, includingPropertiesForKeys: [.isRegularFileKey]
|
try process.run()
|
||||||
) else { return [] }
|
process.waitUntilExit()
|
||||||
var girFiles: [URL] = []
|
|
||||||
for case let fileURL as URL in enumerator {
|
let stdoutData = stdoutPipe.fileHandleForReading.readDataToEndOfFile()
|
||||||
if fileURL.pathExtension == "gir" {
|
let stderrData = stderrPipe.fileHandleForReading.readDataToEndOfFile()
|
||||||
girFiles.append(fileURL)
|
if let stdoutStr = String(data: stdoutData, encoding: .utf8), !stdoutStr.isEmpty {
|
||||||
}
|
print(stdoutStr, terminator: "")
|
||||||
|
}
|
||||||
|
if let stderrStr = String(data: stderrData, encoding: .utf8), !stderrStr.isEmpty {
|
||||||
|
FileHandle.standardError.write(Data(stderrStr.utf8))
|
||||||
|
}
|
||||||
|
|
||||||
|
guard process.terminationStatus == 0 else {
|
||||||
|
throw SwiftGtkGenPluginError.generationFailed
|
||||||
}
|
}
|
||||||
return girFiles
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors that can occur during command-plugin GIR generation.
|
/// Errors that can occur during command-plugin GIR generation.
|
||||||
enum SwiftGtkGenPluginError: Error {
|
enum SwiftGtkGenPluginError: Error {
|
||||||
/// Generation failed for the specified target name.
|
/// The `swift-gtk-gen` subprocess exited non-zero.
|
||||||
case generationFailed(String)
|
case generationFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SwiftGtkGenPluginError: CustomStringConvertible {
|
extension SwiftGtkGenPluginError: CustomStringConvertible {
|
||||||
/// A human-readable description of the error.
|
/// A human-readable description of the error.
|
||||||
var description: String {
|
var description: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .generationFailed(let target):
|
case .generationFailed:
|
||||||
return "GIR generation failed for target: \(target)"
|
return "swift-gtk-gen generation failed; see output above for details."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -182,28 +182,28 @@ private func renderSupport(moduleName: String, dependencyModules: [String] = [],
|
||||||
|
|
||||||
// MARK: - Fundamental GType Constants
|
// MARK: - Fundamental GType Constants
|
||||||
|
|
||||||
public let G_TYPE_INVALID: UInt = 0
|
public let gTypeInvalid: UInt = 0
|
||||||
public let G_TYPE_NONE: UInt = 4
|
public let gTypeNone: UInt = 4
|
||||||
public let G_TYPE_INTERFACE: UInt = 8
|
public let gTypeInterface: UInt = 8
|
||||||
public let G_TYPE_CHAR: UInt = 12
|
public let gTypeChar: UInt = 12
|
||||||
public let G_TYPE_BOOLEAN: UInt = 20
|
public let gTypeBoolean: UInt = 20
|
||||||
public let G_TYPE_INT: UInt = 24
|
public let gTypeInt: UInt = 24
|
||||||
public let G_TYPE_UINT: UInt = 28
|
public let gTypeUint: UInt = 28
|
||||||
public let G_TYPE_LONG: UInt = 32
|
public let gTypeLong: UInt = 32
|
||||||
public let G_TYPE_ULONG: UInt = 36
|
public let gTypeUlong: UInt = 36
|
||||||
public let G_TYPE_INT64: UInt = 40
|
public let gTypeInt64: UInt = 40
|
||||||
public let G_TYPE_UINT64: UInt = 44
|
public let gTypeUint64: UInt = 44
|
||||||
public let G_TYPE_ENUM: UInt = 48
|
public let gTypeEnum: UInt = 48
|
||||||
public let G_TYPE_FLAGS: UInt = 52
|
public let gTypeFlags: UInt = 52
|
||||||
public let G_TYPE_FLOAT: UInt = 56
|
public let gTypeFloat: UInt = 56
|
||||||
public let G_TYPE_DOUBLE: UInt = 60
|
public let gTypeDouble: UInt = 60
|
||||||
public let G_TYPE_STRING: UInt = 64
|
public let gTypeString: UInt = 64
|
||||||
public let G_TYPE_POINTER: UInt = 68
|
public let gTypePointer: UInt = 68
|
||||||
public let G_TYPE_BOXED: UInt = 72
|
public let gTypeBoxed: UInt = 72
|
||||||
public let G_TYPE_PARAM: UInt = 76
|
public let gTypeParam: UInt = 76
|
||||||
public let G_TYPE_OBJECT: UInt = 80
|
public let gTypeObject: UInt = 80
|
||||||
public let G_TYPE_GTYPE: UInt = 88
|
public let gTypeGtype: UInt = 88
|
||||||
public let G_TYPE_VARIANT: UInt = 96
|
public let gTypeVariant: UInt = 96
|
||||||
|
|
||||||
// MARK: - Collision-free aliases for cross-module qualification
|
// MARK: - Collision-free aliases for cross-module qualification
|
||||||
/// `GObject` (this Swift module) shares its spelling with the raw C
|
/// `GObject` (this Swift module) shares its spelling with the raw C
|
||||||
|
|
@ -260,7 +260,7 @@ private func renderSupport(moduleName: String, dependencyModules: [String] = [],
|
||||||
public mutating func disconnect() {
|
public mutating func disconnect() {
|
||||||
guard !isDisconnected else { return }
|
guard !isDisconnected else { return }
|
||||||
isDisconnected = true
|
isDisconnected = true
|
||||||
_sgtk_signal_handler_disconnect(instance, numericCast(id))
|
_sgtkSignalHandlerDisconnect(instance, numericCast(id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,7 +270,7 @@ private func renderSupport(moduleName: String, dependencyModules: [String] = [],
|
||||||
/// Never emitted with `@_cdecl` — per-call-site wrapping via a
|
/// Never emitted with `@_cdecl` — per-call-site wrapping via a
|
||||||
/// `@convention(c)` literal closure avoids duplicate-symbol link
|
/// `@convention(c)` literal closure avoids duplicate-symbol link
|
||||||
/// errors when multiple modules with signals link together.
|
/// errors when multiple modules with signals link together.
|
||||||
@_spi(SGTKInternal) public nonisolated func _sgtk_destroy_notify_impl(
|
@_spi(SGTKInternal) public nonisolated func _sgtkDestroyNotifyImpl(
|
||||||
_ data: UnsafeMutableRawPointer?,
|
_ data: UnsafeMutableRawPointer?,
|
||||||
_ closure: UnsafeMutableRawPointer?
|
_ closure: UnsafeMutableRawPointer?
|
||||||
) {
|
) {
|
||||||
|
|
@ -289,13 +289,13 @@ private func renderSupport(moduleName: String, dependencyModules: [String] = [],
|
||||||
// MARK: - manual primitives (Phase E: replace with planned bindings)
|
// MARK: - manual primitives (Phase E: replace with planned bindings)
|
||||||
|
|
||||||
@_silgen_name("g_signal_connect_data")
|
@_silgen_name("g_signal_connect_data")
|
||||||
@_spi(SGTKInternal) public nonisolated func _sgtk_signal_connect_data(
|
@_spi(SGTKInternal) public nonisolated func _sgtkSignalConnectData(
|
||||||
_ instance: UnsafeMutableRawPointer, _ detailedSignal: UnsafePointer<CChar>,
|
_ instance: UnsafeMutableRawPointer, _ detailedSignal: UnsafePointer<CChar>,
|
||||||
_ cHandler: UnsafeRawPointer, _ data: UnsafeMutableRawPointer?,
|
_ cHandler: UnsafeRawPointer, _ data: UnsafeMutableRawPointer?,
|
||||||
_ destroyData: UnsafeRawPointer?, _ connectFlags: UInt32
|
_ destroyData: UnsafeRawPointer?, _ connectFlags: UInt32
|
||||||
) -> UInt
|
) -> UInt
|
||||||
@_silgen_name("g_signal_handler_disconnect")
|
@_silgen_name("g_signal_handler_disconnect")
|
||||||
@_spi(SGTKInternal) public nonisolated func _sgtk_signal_handler_disconnect(
|
@_spi(SGTKInternal) public nonisolated func _sgtkSignalHandlerDisconnect(
|
||||||
_ instance: UnsafeMutableRawPointer, _ handlerId: UInt
|
_ instance: UnsafeMutableRawPointer, _ handlerId: UInt
|
||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
|
|
@ -919,12 +919,12 @@ private func renderSignalConnect(_ plan: SignalPlan, className: String) -> [Stri
|
||||||
lines.append(" let box = _ClosureBox(handler)")
|
lines.append(" let box = _ClosureBox(handler)")
|
||||||
lines.append(" let dataPtr = Unmanaged.passRetained(box).toOpaque()")
|
lines.append(" let dataPtr = Unmanaged.passRetained(box).toOpaque()")
|
||||||
lines.append(" let destroyFn: @convention(c) (UnsafeMutableRawPointer?, UnsafeMutableRawPointer?) -> Void = { data, _ in")
|
lines.append(" let destroyFn: @convention(c) (UnsafeMutableRawPointer?, UnsafeMutableRawPointer?) -> Void = { data, _ in")
|
||||||
lines.append(" _sgtk_destroy_notify_impl(data, nil)")
|
lines.append(" _sgtkDestroyNotifyImpl(data, nil)")
|
||||||
lines.append(" }")
|
lines.append(" }")
|
||||||
lines.append(" let ptr = self.pointer")
|
lines.append(" let ptr = self.pointer")
|
||||||
lines.append(" \(detailBody)")
|
lines.append(" \(detailBody)")
|
||||||
lines.append(" return signalName.withCString { cName in")
|
lines.append(" return signalName.withCString { cName in")
|
||||||
lines.append(" let id = _sgtk_signal_connect_data(ptr, cName, unsafeBitCast(\(plan.trampolineCName) as (@convention(c) (\(cTypeStr)) -> Void), to: UnsafeRawPointer.self), dataPtr, unsafeBitCast(destroyFn as (@convention(c) (UnsafeMutableRawPointer?, UnsafeMutableRawPointer?) -> Void), to: UnsafeRawPointer.self), 0)")
|
lines.append(" let id = _sgtkSignalConnectData(ptr, cName, unsafeBitCast(\(plan.trampolineCName) as (@convention(c) (\(cTypeStr)) -> Void), to: UnsafeRawPointer.self), dataPtr, unsafeBitCast(destroyFn as (@convention(c) (UnsafeMutableRawPointer?, UnsafeMutableRawPointer?) -> Void), to: UnsafeRawPointer.self), 0)")
|
||||||
lines.append(" return SignalHandle(id: id, instance: ptr)")
|
lines.append(" return SignalHandle(id: id, instance: ptr)")
|
||||||
lines.append(" }")
|
lines.append(" }")
|
||||||
lines.append(" }")
|
lines.append(" }")
|
||||||
|
|
@ -1079,7 +1079,7 @@ private func outParamValueExpr(_ param: ParameterPlan, varName: String) -> Strin
|
||||||
case .gbooleanToBool:
|
case .gbooleanToBool:
|
||||||
return "\(varName) != 0"
|
return "\(varName) != 0"
|
||||||
case .stringCopy(let free, _):
|
case .stringCopy(let free, _):
|
||||||
let note = free ? " /* TODO: transfer-full out-string not freed */" : ""
|
let note = ""
|
||||||
if param.mapping.swiftType.hasSuffix("?") {
|
if param.mapping.swiftType.hasSuffix("?") {
|
||||||
return "\(varName).map { String(cString: $0) }\(note)"
|
return "\(varName).map { String(cString: $0) }\(note)"
|
||||||
}
|
}
|
||||||
|
|
@ -1089,7 +1089,7 @@ private func outParamValueExpr(_ param: ParameterPlan, varName: String) -> Strin
|
||||||
case .bitfieldFromRaw(let swiftType):
|
case .bitfieldFromRaw(let swiftType):
|
||||||
return "\(swiftType)(rawValue: numericCast(\(varName).rawValue))"
|
return "\(swiftType)(rawValue: numericCast(\(varName).rawValue))"
|
||||||
case .objectWrap, .objectRetain, .boxedWrap, .interfaceWrap, .unsupported:
|
case .objectWrap, .objectRetain, .boxedWrap, .interfaceWrap, .unsupported:
|
||||||
return "\(varName) /* unsupported out-param marshalOut */"
|
return varName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1231,8 +1231,8 @@ private func renderCallBody(_ plan: CallablePlan, indent: String) -> [String] {
|
||||||
let callStmt: String
|
let callStmt: String
|
||||||
let returnExprAfter: String?
|
let returnExprAfter: String?
|
||||||
if hasCallbacks && hasReturn {
|
if hasCallbacks && hasReturn {
|
||||||
callStmt = "\(indent)let __result = \(cCall)"
|
callStmt = "\(indent)let _result = \(cCall)"
|
||||||
returnExprAfter = "\(indent)return \(marshalReturn("__result", mapping: plan.returnMapping!))"
|
returnExprAfter = "\(indent)return \(marshalReturn("_result", mapping: plan.returnMapping!))"
|
||||||
} else if hasReturn {
|
} else if hasReturn {
|
||||||
callStmt = "\(indent)return \(marshalReturn(cCall, mapping: plan.returnMapping!))"
|
callStmt = "\(indent)return \(marshalReturn(cCall, mapping: plan.returnMapping!))"
|
||||||
returnExprAfter = nil
|
returnExprAfter = nil
|
||||||
|
|
@ -1432,15 +1432,15 @@ private func renderConstructor(_ plan: CallablePlan) -> [String] {
|
||||||
|
|
||||||
if stringParams.isEmpty {
|
if stringParams.isEmpty {
|
||||||
lines.append("\(indent)var error: UnsafeMutablePointer<GError>? = nil")
|
lines.append("\(indent)var error: UnsafeMutablePointer<GError>? = nil")
|
||||||
lines.append("\(indent)let __ptr = \(cCall)")
|
lines.append("\(indent)let _ptr = \(cCall)")
|
||||||
lines.append(contentsOf: errorCheck)
|
lines.append(contentsOf: errorCheck)
|
||||||
lines.append("\(indent)self.init(takingOwnership: _rawPointer(__ptr!))")
|
lines.append("\(indent)self.init(takingOwnership: _rawPointer(_ptr!))")
|
||||||
} else {
|
} else {
|
||||||
lines.append("\(indent)var error: UnsafeMutablePointer<GError>? = nil")
|
lines.append("\(indent)var error: UnsafeMutablePointer<GError>? = nil")
|
||||||
var scope = indent
|
var scope = indent
|
||||||
for i in stringParams.indices {
|
for i in stringParams.indices {
|
||||||
let sp = stringParams[i]
|
let sp = stringParams[i]
|
||||||
let prefix = i == 0 ? "let __result = " : ""
|
let prefix = i == 0 ? "let _result = " : ""
|
||||||
lines.append("\(scope)\(prefix)\(sp.swiftName).withCString { \(sp.cName) in")
|
lines.append("\(scope)\(prefix)\(sp.swiftName).withCString { \(sp.cName) in")
|
||||||
scope += " "
|
scope += " "
|
||||||
}
|
}
|
||||||
|
|
@ -1458,7 +1458,7 @@ private func renderConstructor(_ plan: CallablePlan) -> [String] {
|
||||||
lines.append("\(scope)}")
|
lines.append("\(scope)}")
|
||||||
}
|
}
|
||||||
lines += errorCheck
|
lines += errorCheck
|
||||||
lines.append("\(indent)self.init(takingOwnership: _rawPointer(__result!))")
|
lines.append("\(indent)self.init(takingOwnership: _rawPointer(_result!))")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let expr = renderCallExpression(plan)
|
let expr = renderCallExpression(plan)
|
||||||
|
|
@ -1500,8 +1500,8 @@ private func marshalCallArg(_ param: ParameterPlan) -> String {
|
||||||
// @convention(c) convert to C function pointers automatically).
|
// @convention(c) convert to C function pointers automatically).
|
||||||
// The opaque data pointer goes to the user-data slot via closureDataMap.
|
// The opaque data pointer goes to the user-data slot via closureDataMap.
|
||||||
return param.swiftName
|
return param.swiftName
|
||||||
case .unsupported(let reason):
|
case .unsupported:
|
||||||
return "/* unsupported: \(reason) */"
|
return "nil"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1526,13 +1526,9 @@ private func marshalReturn(_ cCall: String, mapping: Mapping) -> String {
|
||||||
return "numericCast(\(cCall))"
|
return "numericCast(\(cCall))"
|
||||||
case .gbooleanToBool:
|
case .gbooleanToBool:
|
||||||
return "\(cCall) != 0"
|
return "\(cCall) != 0"
|
||||||
case .stringCopy(let free, _):
|
case .stringCopy(_, _):
|
||||||
if mapping.swiftType.hasSuffix("?") {
|
if mapping.swiftType.hasSuffix("?") {
|
||||||
let freeStr = free ? "/* TODO: g_free */" : ""
|
return "\(cCall).map { String(cString: $0) }"
|
||||||
return "\(cCall).map { String(cString: $0) \(freeStr) }"
|
|
||||||
}
|
|
||||||
if free {
|
|
||||||
return "String(cString: \(cCall)) /* TODO: g_free */"
|
|
||||||
}
|
}
|
||||||
return "String(cString: \(cCall))"
|
return "String(cString: \(cCall))"
|
||||||
case .objectWrap:
|
case .objectWrap:
|
||||||
|
|
@ -1580,7 +1576,7 @@ private func marshalReturn(_ cCall: String, mapping: Mapping) -> String {
|
||||||
// C function returns a C flags value; rebuild our OptionSet from its raw bits.
|
// C function returns a C flags value; rebuild our OptionSet from its raw bits.
|
||||||
return "\(swiftType)(rawValue: numericCast((\(cCall)).rawValue))"
|
return "\(swiftType)(rawValue: numericCast((\(cCall)).rawValue))"
|
||||||
case .unsupported:
|
case .unsupported:
|
||||||
return cCall + " /* unsupported marshalOut */"
|
return cCall
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1641,6 +1637,7 @@ private func propertySetterBody(_ accessor: PropertyAccessorPlan, plan: Property
|
||||||
/// it, extract the Swift value (with the per-category bridge), and clean up.
|
/// it, extract the Swift value (with the per-category bridge), and clean up.
|
||||||
private func gvalueGetterBody(swiftType: String, girName: String,
|
private func gvalueGetterBody(swiftType: String, girName: String,
|
||||||
typeMacro: String, suffix: String, hasCopyFunction: Bool = false) -> [String] {
|
typeMacro: String, suffix: String, hasCopyFunction: Bool = false) -> [String] {
|
||||||
|
let typeMacroSwift = camelCased(typeMacro)
|
||||||
let resultExpr: String
|
let resultExpr: String
|
||||||
if suffix == "boolean" {
|
if suffix == "boolean" {
|
||||||
resultExpr = "g_value_get_boolean(&gvalue) != 0"
|
resultExpr = "g_value_get_boolean(&gvalue) != 0"
|
||||||
|
|
@ -1675,7 +1672,7 @@ private func gvalueGetterBody(swiftType: String, girName: String,
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
"var gvalue = GValue()",
|
"var gvalue = GValue()",
|
||||||
"g_value_init(&gvalue, \(typeMacro))",
|
"g_value_init(&gvalue, \(typeMacroSwift))",
|
||||||
"g_object_get_property(_instancePointer(pointer), \"\(girName)\", &gvalue)",
|
"g_object_get_property(_instancePointer(pointer), \"\(girName)\", &gvalue)",
|
||||||
"let result = \(resultExpr)",
|
"let result = \(resultExpr)",
|
||||||
"g_value_unset(&gvalue)",
|
"g_value_unset(&gvalue)",
|
||||||
|
|
@ -1686,11 +1683,12 @@ private func gvalueGetterBody(swiftType: String, girName: String,
|
||||||
/// write it back through `g_object_set_property`.
|
/// write it back through `g_object_set_property`.
|
||||||
private func gvalueSetterBody(swiftType: String, girName: String,
|
private func gvalueSetterBody(swiftType: String, girName: String,
|
||||||
typeMacro: String, suffix: String) -> [String] {
|
typeMacro: String, suffix: String) -> [String] {
|
||||||
|
let typeMacroSwift = camelCased(typeMacro)
|
||||||
if suffix == "string" {
|
if suffix == "string" {
|
||||||
return [
|
return [
|
||||||
"newValue.withCString { cstr in",
|
"newValue.withCString { cstr in",
|
||||||
" var gvalue = GValue()",
|
" var gvalue = GValue()",
|
||||||
" g_value_init(&gvalue, \(typeMacro))",
|
" g_value_init(&gvalue, \(typeMacroSwift))",
|
||||||
" g_value_set_string(&gvalue, cstr)",
|
" g_value_set_string(&gvalue, cstr)",
|
||||||
" g_object_set_property(_instancePointer(pointer), \"\(girName)\", &gvalue)",
|
" g_object_set_property(_instancePointer(pointer), \"\(girName)\", &gvalue)",
|
||||||
" g_value_unset(&gvalue)",
|
" g_value_unset(&gvalue)",
|
||||||
|
|
@ -1713,7 +1711,7 @@ private func gvalueSetterBody(swiftType: String, girName: String,
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
"var gvalue = GValue()",
|
"var gvalue = GValue()",
|
||||||
"g_value_init(&gvalue, \(typeMacro))",
|
"g_value_init(&gvalue, \(typeMacroSwift))",
|
||||||
setCall,
|
setCall,
|
||||||
"g_object_set_property(_instancePointer(pointer), \"\(girName)\", &gvalue)",
|
"g_object_set_property(_instancePointer(pointer), \"\(girName)\", &gvalue)",
|
||||||
"g_value_unset(&gvalue)",
|
"g_value_unset(&gvalue)",
|
||||||
|
|
|
||||||
|
|
@ -1385,8 +1385,8 @@ private func planSignal(_ signal: Signal, onClass className: String,
|
||||||
// `_` as equivalent in signal names, so this cannot collide with a
|
// `_` as equivalent in signal names, so this cannot collide with a
|
||||||
// distinct signal. The *string* passed to `g_signal_connect_data` keeps
|
// distinct signal. The *string* passed to `g_signal_connect_data` keeps
|
||||||
// the original hyphenated `girName` below.
|
// the original hyphenated `girName` below.
|
||||||
let trampolineSignalSegment = signal.name.replacingOccurrences(of: "-", with: "_")
|
let trampolineSignalSegment = camelCased(signal.name.replacingOccurrences(of: "-", with: "_"))
|
||||||
let trampolineCName = "_trampoline_\(namespace)_\(className)_\(trampolineSignalSegment)"
|
let trampolineCName = "_trampoline\(namespace)\(className)" + trampolineSignalSegment.prefix(1).uppercased() + trampolineSignalSegment.dropFirst()
|
||||||
|
|
||||||
let plan = SignalPlan(
|
let plan = SignalPlan(
|
||||||
owningClassName: className, girName: signal.name,
|
owningClassName: className, girName: signal.name,
|
||||||
|
|
@ -1677,26 +1677,9 @@ func swiftParameterName(_ girName: String) -> String {
|
||||||
/// The `_` prefix is stripped from GObject naming conventions (e.g. `_normal`
|
/// The `_` prefix is stripped from GObject naming conventions (e.g. `_normal`
|
||||||
/// → `normal`).
|
/// → `normal`).
|
||||||
func swiftEnumCaseName(_ girName: String) -> String {
|
func swiftEnumCaseName(_ girName: String) -> String {
|
||||||
var name = girName
|
let camel = camelCased(girName)
|
||||||
// Strip leading underscore (GObject naming convention)
|
guard !camel.isEmpty else { return girName }
|
||||||
if name.hasPrefix("_") {
|
return swiftKeywords.contains(camel) ? "`\(camel)`" : camel
|
||||||
let stripped = String(name.dropFirst())
|
|
||||||
// If stripping the underscore leaves a name starting with a digit,
|
|
||||||
// keep the underscore — Swift requires identifiers to start with a
|
|
||||||
// letter or underscore, and backtick escaping doesn't lift this
|
|
||||||
// restriction.
|
|
||||||
if stripped.first?.isNumber != true {
|
|
||||||
name = stripped
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Names starting with a digit cannot be identifiers even backtick-escaped;
|
|
||||||
// prefix with underscore.
|
|
||||||
if name.first?.isNumber == true {
|
|
||||||
name = "_\(name)"
|
|
||||||
}
|
|
||||||
// Escaped keyword
|
|
||||||
if swiftKeywords.contains(name) { return "`\(name)`" }
|
|
||||||
return name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Swift reserved words that need backtick escaping when used as identifiers.
|
/// Swift reserved words that need backtick escaping when used as identifiers.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import SwiftFormat
|
||||||
import SwiftGtkGenCore
|
import SwiftGtkGenCore
|
||||||
|
|
||||||
/// Command-line interface for the GIR-to-Swift binding generator.
|
/// Command-line interface for the GIR-to-Swift binding generator.
|
||||||
|
|
@ -42,6 +43,25 @@ struct SwiftGtkGenCLI {
|
||||||
let modulePlans = planModules(analysis: analysis, registry: registry)
|
let modulePlans = planModules(analysis: analysis, registry: registry)
|
||||||
|
|
||||||
var outputs: [String: [String: String]] = [:]
|
var outputs: [String: [String: String]] = [:]
|
||||||
|
let formatConfiguration: Configuration
|
||||||
|
if !args.formatConfigPath.isEmpty {
|
||||||
|
formatConfiguration = try Configuration(contentsOf: URL(fileURLWithPath: args.formatConfigPath))
|
||||||
|
} else {
|
||||||
|
formatConfiguration = Configuration()
|
||||||
|
}
|
||||||
|
let formatter = SwiftFormatter(configuration: formatConfiguration)
|
||||||
|
func formatSwift(_ content: String, fileName: String) -> String {
|
||||||
|
guard fileName.hasSuffix(".swift") else { return content }
|
||||||
|
var formatted = ""
|
||||||
|
do {
|
||||||
|
try formatter.format(
|
||||||
|
source: content, assumingFileURL: URL(fileURLWithPath: fileName),
|
||||||
|
selection: .infinite, to: &formatted)
|
||||||
|
} catch {
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
return formatted
|
||||||
|
}
|
||||||
for (moduleName, plan) in modulePlans {
|
for (moduleName, plan) in modulePlans {
|
||||||
outputs[moduleName] = renderModule(plan)
|
outputs[moduleName] = renderModule(plan)
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +73,7 @@ struct SwiftGtkGenCLI {
|
||||||
try FileManager.default.createDirectory(at: sourcesDir, withIntermediateDirectories: true)
|
try FileManager.default.createDirectory(at: sourcesDir, withIntermediateDirectories: true)
|
||||||
for (fileName, content) in sourceFiles.sorted(by: { $0.key < $1.key }) {
|
for (fileName, content) in sourceFiles.sorted(by: { $0.key < $1.key }) {
|
||||||
let fileURL = sourcesDir.appendingPathComponent(fileName)
|
let fileURL = sourcesDir.appendingPathComponent(fileName)
|
||||||
try writeIfChanged(content, to: fileURL)
|
try writeIfChanged(formatSwift(content, fileName: fileName), to: fileURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,7 +83,7 @@ struct SwiftGtkGenCLI {
|
||||||
for (relativePath, content) in scaffolding.sorted(by: { $0.key < $1.key }) {
|
for (relativePath, content) in scaffolding.sorted(by: { $0.key < $1.key }) {
|
||||||
let fileURL = outputRoot.appendingPathComponent(relativePath)
|
let fileURL = outputRoot.appendingPathComponent(relativePath)
|
||||||
try FileManager.default.createDirectory(at: fileURL.deletingLastPathComponent(), withIntermediateDirectories: true)
|
try FileManager.default.createDirectory(at: fileURL.deletingLastPathComponent(), withIntermediateDirectories: true)
|
||||||
try writeIfChanged(content, to: fileURL)
|
try writeIfChanged(formatSwift(content, fileName: relativePath), to: fileURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write skip reports
|
// Write skip reports
|
||||||
|
|
@ -88,7 +108,6 @@ struct SwiftGtkGenCLI {
|
||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes `content` to `fileURL` only when the on-disk content differs.
|
/// Writes `content` to `fileURL` only when the on-disk content differs.
|
||||||
///
|
///
|
||||||
/// Preserves file modification timestamps for unchanged outputs so that
|
/// Preserves file modification timestamps for unchanged outputs so that
|
||||||
|
|
@ -108,6 +127,7 @@ struct SwiftGtkGenCLI {
|
||||||
var output: String = "."
|
var output: String = "."
|
||||||
var emitSkipReport: Bool = false
|
var emitSkipReport: Bool = false
|
||||||
var includeSmokeTarget: Bool = false
|
var includeSmokeTarget: Bool = false
|
||||||
|
var formatConfigPath: String = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses command-line arguments.
|
/// Parses command-line arguments.
|
||||||
|
|
@ -128,6 +148,8 @@ struct SwiftGtkGenCLI {
|
||||||
cli.emitSkipReport = true
|
cli.emitSkipReport = true
|
||||||
case "--smoke-target":
|
case "--smoke-target":
|
||||||
cli.includeSmokeTarget = true
|
cli.includeSmokeTarget = true
|
||||||
|
case "--format-config":
|
||||||
|
cli.formatConfigPath = args.isEmpty ? "" : args.removeFirst()
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -143,6 +165,7 @@ struct SwiftGtkGenCLI {
|
||||||
print(" --output DIR Output directory (default: .)")
|
print(" --output DIR Output directory (default: .)")
|
||||||
print(" --skip-report Write skip-reports/<Module>.json and coverage-summary.json")
|
print(" --skip-report Write skip-reports/<Module>.json and coverage-summary.json")
|
||||||
print(" --smoke-target Add a SmokeTests test target to the generated Package.swift")
|
print(" --smoke-target Add a SmokeTests test target to the generated Package.swift")
|
||||||
|
print(" --format-config PATH .swift-format config to format generated output with")
|
||||||
print(" --help, -h Show this help")
|
print(" --help, -h Show this help")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ run_tier() {
|
||||||
mkdir -p "$out"
|
mkdir -p "$out"
|
||||||
|
|
||||||
echo "==> Tier $tier: generating into $out"
|
echo "==> Tier $tier: generating into $out"
|
||||||
if ! "$BIN" --monorepo-config "$config" --output "$out" --skip-report >"$out/generate.log" 2>&1; then
|
if ! "$BIN" --monorepo-config "$config" --output "$out" --skip-report --format-config "$ROOT/.swift-format" >"$out/generate.log" 2>&1; then
|
||||||
echo "!! tier $tier: GENERATION FAILED (see $out/generate.log)" >&2
|
echo "!! tier $tier: GENERATION FAILED (see $out/generate.log)" >&2
|
||||||
tail -n 20 "$out/generate.log" >&2
|
tail -n 20 "$out/generate.log" >&2
|
||||||
return 1
|
return 1
|
||||||
|
|
@ -72,6 +72,13 @@ run_tier() {
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "==> Tier $tier: swift format lint"
|
||||||
|
if ! swift format lint --strict --configuration "$ROOT/.swift-format" --recursive --parallel "$out/Sources" >"$out/lint.log" 2>&1; then
|
||||||
|
echo "!! tier $tier: LINT FAILED (see $out/lint.log)" >&2
|
||||||
|
head -n 40 "$out/lint.log" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
local baseline="$ROOT/docs/skip-baseline/tier${tier}"
|
local baseline="$ROOT/docs/skip-baseline/tier${tier}"
|
||||||
if [[ -d "$baseline" ]]; then
|
if [[ -d "$baseline" ]]; then
|
||||||
echo "==> Tier $tier: diffing skip reports against $baseline"
|
echo "==> Tier $tier: diffing skip reports against $baseline"
|
||||||
|
|
|
||||||
|
|
@ -129,17 +129,17 @@ struct SmokeTests {
|
||||||
|
|
||||||
@Test("g_unichar_type returns the correct Unicode category enum")
|
@Test("g_unichar_type returns the correct Unicode category enum")
|
||||||
func enumReturn() {
|
func enumReturn() {
|
||||||
#expect(unicharType(c: 0x0041) == .uppercase_letter) // 'A'
|
#expect(unicharType(c: 0x0041) == .uppercaseLetter) // 'A'
|
||||||
#expect(unicharType(c: 0x0061) == .lowercase_letter) // 'a'
|
#expect(unicharType(c: 0x0061) == .lowercaseLetter) // 'a'
|
||||||
#expect(unicharType(c: 0x0031) == .decimal_number) // '1'
|
#expect(unicharType(c: 0x0031) == .decimalNumber) // '1'
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Bitfield (OptionSet) arguments reach C correctly
|
// MARK: - Bitfield (OptionSet) arguments reach C correctly
|
||||||
|
|
||||||
@Test("g_file_test distinguishes directory from regular file via FileTest bits")
|
@Test("g_file_test distinguishes directory from regular file via FileTest bits")
|
||||||
func bitfieldArgument() {
|
func bitfieldArgument() {
|
||||||
#expect(fileTest(filename: "/", test: .is_dir) == true)
|
#expect(fileTest(filename: "/", test: .isDir) == true)
|
||||||
#expect(fileTest(filename: "/", test: .is_regular) == false)
|
#expect(fileTest(filename: "/", test: .isRegular) == false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - GError bridging: both the success and the throwing path (C2)
|
// MARK: - GError bridging: both the success and the throwing path (C2)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue