Skip to content

Research: evaluate user-written Swift as input to a codegen CLI (Tuist-style manifest execution) #154

@leogdion

Description

@leogdion

Background

Tuist lets users describe an Xcode project by writing a Project.swift manifest in plain Swift, then evaluates that manifest at CLI invocation time — without the user having to compile and run a full app themselves. SwiftPM does the same thing with Package.swift.

We should investigate whether SyntaxKit could expose an analogous flow: the user writes a Swift file that uses the SyntaxKit DSL, and a CLI compiles + executes it to emit generated Swift source. This would let people drive code generation declaratively from a checked-in .swift file without setting up an executable target per generator.

Goals of this research

  1. Understand exactly how Tuist (and, as a comparison point, SwiftPM) compiles and executes a user manifest.
  2. Identify which pieces of that pipeline are reusable for a SyntaxKit-driven codegen CLI.
  3. Surface the risks and open questions before we commit to a design.

Phase 1 — How Tuist does it

Source: tuist/tuist, primarily the ProjectDescription target and the manifest-loading subsystem.

Questions to answer (with file/line citations into the Tuist repo):

  • Compilation pipeline. What exact swiftc invocation compiles Project.swift? Flags, framework search paths, linker args, output type (dylib vs. executable)?
  • Execution model. Does Tuist (a) load a compiled dylib and call a known symbol, (b) run a compiled executable that dumps a description on stdout, or (c) something else? (SwiftPM uses (b) — confirm whether Tuist matches.)
  • Bridging format. What's the wire format between the manifest process and the host tool? ProjectDescription types are Codable — is JSON the boundary?
  • Helpers / shared code. How does ProjectDescriptionHelpers/ get compiled and made importable from the manifest?
  • Caching. Does Tuist hash the manifest + ProjectDescription version and skip recompilation? Where is the cache?
  • Failure modes. What happens when the manifest crashes, has a syntax error, or hangs?

Phase 2 — Map the pattern onto SyntaxKit

With Phase 1 in hand, sketch the equivalent for a SyntaxKit CLI:

  • Manifest shape. What does the user write? A top-level expression returning a Group of CodeBlocks? An @main struct?
  • What crosses the process boundary. SyntaxKit's output is generated Swift source, not a graph of Codable types. Two viable options:
    • Manifest executable prints generated Swift to stdout directly (simplest).
    • Manifest exports a Codable IR; host renders via SwiftSyntax (matches Tuist more closely, but duplicates rendering).
  • Linking SyntaxKit into the manifest. The manifest needs import SyntaxKit. Compare (a) shipping a precompiled SyntaxKit binary next to the CLI and passing -F/-I/-L, vs. (b) generating a throwaway SwiftPM package per run.
  • Helpers directory. Mirror Tuist's ProjectDescriptionHelpers so users can factor out reusable codegen.

Phase 3 — Open risks to probe early

  • SwiftSyntax link cost. SwiftSyntax is large; compiling a manifest that transitively links it could be slow. Measure on a hello-world manifest before committing.
  • macOS-only vs. Linux. Tuist is mac-focused. The SyntaxKit CLI should at least not paint itself into a macOS corner.
  • Toolchain resolution. How does Tuist locate swiftc (xcrun --find vs. explicit toolchain paths)?

Deliverables

  1. Phase 1 write-up: how Tuist's manifest pipeline works, with citations.
  2. Design sketch: compile command, execution model, IR boundary (or lack of one), helpers layout, caching strategy.
  3. Smallest possible proof-of-concept steps (enumerated, not yet implemented), e.g. "compile a manifest that imports SyntaxKit and prints a Hello.swift to stdout."

Out of scope (for this issue)

  • Implementation of the CLI itself — this issue is research only.
  • Choosing a CLI name or product surface.

Metadata

Metadata

Assignees

No one assigned

    Labels

    cliCommand-line interfaceenhancementNew feature or requestquestionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions