From b13f7976cc7b5aac58e66bf46b3f4c04c00ddd7d Mon Sep 17 00:00:00 2001 From: Brendan Szymanski Date: Tue, 21 Jul 2026 20:08:39 -0400 Subject: [PATCH] Preserve user-defined test targets across Package.swift regeneration --- .../CodeGen+Scaffolding.swift | 7 +- Sources/GObjectGeneratorCore/CodeGen.swift | 35 ++++++++++ Sources/generator/Main.swift | 10 ++- .../ScaffoldingTests.swift | 64 +++++++++++++++++++ 4 files changed, 114 insertions(+), 2 deletions(-) diff --git a/Sources/GObjectGeneratorCore/CodeGen+Scaffolding.swift b/Sources/GObjectGeneratorCore/CodeGen+Scaffolding.swift index 4c392fc..949fa6f 100644 --- a/Sources/GObjectGeneratorCore/CodeGen+Scaffolding.swift +++ b/Sources/GObjectGeneratorCore/CodeGen+Scaffolding.swift @@ -256,7 +256,12 @@ extension CodeGenerator { \(products) ], targets: [ \(cTargets) - \(swiftTargets)\(smokeTarget) ] + \(swiftTargets)\(smokeTarget) + // === USER TARGETS — preserved across generations === + // BEGIN_USER_TARGETS + + // END_USER_TARGETS + ] ) """ diff --git a/Sources/GObjectGeneratorCore/CodeGen.swift b/Sources/GObjectGeneratorCore/CodeGen.swift index a80543a..3a357d6 100644 --- a/Sources/GObjectGeneratorCore/CodeGen.swift +++ b/Sources/GObjectGeneratorCore/CodeGen.swift @@ -24,4 +24,39 @@ public struct CodeGenerator { } return files } + + // MARK: - User target preservation + + /// Preserves user-defined targets between `// BEGIN_USER_TARGETS` and + /// `// END_USER_TARGETS` markers when regenerating Package.swift. + /// + /// If the existing file has content between the markers, it is spliced + /// into the freshly generated output. If either file lacks the markers, + /// the generated output is returned unchanged. + /// + /// - Parameters: + /// - generated: The freshly generated Package.swift content. + /// - existing: The existing Package.swift on disk (if any). + /// - Returns: The generated content with user targets spliced in. + public static func preserveUserTargets(generated: String, existing: String) -> String { + let beginMarker = "// BEGIN_USER_TARGETS" + let endMarker = "// END_USER_TARGETS" + + guard let genStart = generated.range(of: beginMarker), + let genEnd = generated.range(of: endMarker, range: genStart.upperBound..") == true) } + +// MARK: - User target preservation + +@Test("generateMonorepoScaffolding includes user targets marker block") +func testMonorepoPackageHasUserTargetMarkers() { + // Build minimal MultiPackageAnalysis with one module + let repo = Repository( + namespaces: [Namespace(name: "Gtk", version: "4.0", + cSharedLibrary: "libgtk-4.so.1", cIdentifierPrefix: "Gtk")] + ) + let analysis = MultiPackageAnalysis( + repositories: ["Gtk": repo], + directDependencies: ["Gtk": []], + transitiveDependencies: ["Gtk": []], + implicitImports: ["Gtk": []], + packageConfigs: [:] + ) + let scaffolding = CodeGenerator.generateMonorepoScaffolding(analysis: analysis) + let pkg = scaffolding["Package.swift"] ?? "" + #expect(pkg.contains("// BEGIN_USER_TARGETS")) + #expect(pkg.contains("// END_USER_TARGETS")) +} + +@Test("preserveUserTargets splices user block from existing into generated") +func testPreserveUserTargets() { + let existing = """ + targets: [ + .systemLibrary(name: "CGLib"), + // BEGIN_USER_TARGETS + .testTarget(name: "MyTests", dependencies: ["GLib"]), + // END_USER_TARGETS + ] + """ + + let generated = """ + targets: [ + .systemLibrary(name: "CGLib"), + // BEGIN_USER_TARGETS + + // END_USER_TARGETS + ] + """ + let result = CodeGenerator.preserveUserTargets(generated: generated, existing: existing) + #expect(result.contains("MyTests")) + #expect(result != generated, "Result should differ from generated since existing has content") + #expect(result.contains("// BEGIN_USER_TARGETS")) + #expect(result.contains("// END_USER_TARGETS")) + #expect(result.contains(".testTarget(name:")) +} + +@Test("preserveUserTargets returns generated unchanged when existing has no markers") +func testPreserveUserTargetsFirstGeneration() { + let generated = """ + targets: [ + .systemLibrary(name: "CGLib"), + // BEGIN_USER_TARGETS + + // END_USER_TARGETS + ] + """ + + let result = CodeGenerator.preserveUserTargets(generated: generated, existing: "") + #expect(result == generated) +}