Skip to content

πŸš€ [Feature Request]: Generate the Xcode project from a YAML spec (XcodeGen), keeping project.pbxproj committedΒ #2137

Description

@laconicman

OS

iOS

Description

Follow-up to #2026. That issue proposed two options; option A (buildable folders) is nearly complete β€” #2085 and #2101 converted the tests, Watch App, Widgets and Shared/, and #2102 converts the main tree. This is a concrete proposal for option B, in a form that costs contributors nothing day to day.

The proposal: project.yml becomes the source of truth, and the generated Meshtastic.xcodeproj stays committed to git.

Nobody's clone-and-open workflow changes: git clone, open Meshtastic.xcworkspace, build. XcodeGen is only needed by someone who changes project structure β€” adds a target, changes build settings, adds an SPM dependency. With buildable folders already in place, adding source files needs nothing at all.

Why this is worth doing now, specifically for how this project works. Contributors and maintainers here lean heavily on AI β€” Copilot, Codex, CodeRabbit are all over recent PRs. Agents read and edit YAML reliably and can be reviewed doing it. Nobody, human or model, can meaningfully review a project.pbxproj diff: it's a flat plist of opaque 24-character IDs, which is exactly why #1898 could silently delete a test file's registration and why #1827 spent six weeks in conflict limbo. Moving the project definition into ~200 lines of readable YAML puts it inside the review process for the first time, and gives agents a file they can safely operate on.

Verified feasibility (XcodeGen 2.46.0, tested locally before filing):

  • Emits the modern format β€” objectVersion = 77, newer than the project's current 70.
  • Generates real Xcode 16 synchronized folders (PBXFileSystemSynchronizedRootGroup) via type: syncedFolder, so it preserves and extends the buildable-folder work rather than undoing it.
  • Generates PBXFileSystemSynchronizedBuildFileExceptionSet correctly, which this project needs β€” Info.plist is excluded automatically, and explicit excludes (like the ~60 SVG exceptions on Resources/images) come through as membershipExceptions. explicitFolders is available to keep Resources/docs and Certificates as folder references so the in-app doc browser keeps its directory structure.
    Regeneration is byte-deterministic β€” generating twice produced an identical SHA. This is what makes the committed-pbxproj model work: the file never churns spuriously, two contributors regenerating produce the same bytes, and a conflict is resolved by taking either side and re-running xcodegen, with no hand-merging of plist fragments ever again.
  • Adding a source file produced zero change to the generated pbxproj.

Scope: 4 targets (Meshtastic, WidgetsExtension, MeshtasticTests, Meshtastic Watch App), 9 remote + 1 local SPM package, 3 shared schemes, the Catalyst configuration, and watch/widget embedding.

Suggested acceptance criteria, so this is provable rather than trusted:

  1. xcodebuild -showBuildSettings output is identical between the committed project and the generated one, for every target Γ— configuration.
  2. The built .app bundle is identical file-for-file (catches any bundling or localization regression).
  3. All three shared schemes and the full test suite survive unchanged.
  4. A CI check regenerates and fails if the committed pbxproj has drifted from project.yml.

Docs that would need updating: README.md (step 4) and CONTRIBUTING.md (step 4) keep "open the workspace" as-is, plus a short new section on when regeneration is required; docs/developer/contributing.md and docs/developer/codebase.md get the same note.

Two caveats worth stating up front. XcodeGen 2.45.0 or newer is required β€” 2.44.x silently ignores excludes on synced folders, which would quietly bundle files that are currently excluded (I hit this while testing). And this should land only after #2102, since both rewrite the whole pbxproj and would otherwise collide β€” which would be an ironic way to make the point.

Participation

  • I am willing to pay to sponsor this feature.
  • I am willing to submit a pull request for this feature.

Additional comments

Deliberately filing this as a proposal first β€” it's a change to how everyone interacts with the project, and worth agreeing on before any code. If needed can put up a proof-of-concept project.yml with the -showBuildSettings diff attached, so the decision can be made against real output rather than a description.

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions