Skip to content

feat: support new Xcode 16 XCBuildConfiguration format#1037

Merged
fortmarek merged 6 commits intotuist:mainfrom
johnrbent:xcode16-buildconfig
May 5, 2026
Merged

feat: support new Xcode 16 XCBuildConfiguration format#1037
fortmarek merged 6 commits intotuist:mainfrom
johnrbent:xcode16-buildconfig

Conversation

@johnrbent
Copy link
Copy Markdown
Contributor

Resolves #852

Short description 📝

Adds support for changes to the XCBuildConfiguration representation of xcconfig files contained within a file-system synchronized root group.

Solution 📦

Instead of a single baseConfigurationReference, these xcconfig files are represented by a pair of:

  • baseConfigurationReferenceAnchor: reference to the synced root group.
  • baseConfigurationReferenceRelativePath: relative path to the file from the root group.

*config files within a PBXGroup are still represented by the baseConfigurationReference

Implementation 👩‍💻👨‍💻

  • Add the two new optional properties to the XCBuildConfiguration type following existing patterns for baseConfigurationReference
  • Include new unit test on the encoding as well as a fixture project with xcconfig files in a synced root group.
  • I have another PR ready for XcodeGraph to adopt these changes that I'll put up once this is merged.

(This is my first contribution to the project. I'm happy to make changes if needed)

@dosubot dosubot Bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Dec 26, 2025
@dosubot
Copy link
Copy Markdown

dosubot Bot commented Dec 26, 2025

Related Documentation

Checked 1 published document(s) in 1 knowledge base(s). No updates required.

How did I do? Any feedback?  Join Discord

Comment thread .editorconfig Outdated
[*]
indent_style = space
indent_size = 2
indent_size = 4
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure about this, but I had to change this to get the correct indentation locally in Xcode. Maybe someone can explain why it should be 2?

@kimdv
Copy link
Copy Markdown
Contributor

kimdv commented Feb 12, 2026

Is there any progress on this? :D

mokagio added a commit to mokagio/XcodeProj-Tuist that referenced this pull request May 5, 2026
PR tuist#1037 (tuist#1037)
emits the synchronized root group's `path` as the
`baseConfigurationReferenceAnchor` comment.
The sibling `baseConfigurationReference` encoder uses
`PBXFileElement.fileName()`, which returns `name ?? path`,
matching what Xcode emits.

For sync groups carrying both `name` and `path`
(e.g. wcios's `config` group with `name = config`,
`path = ../config`),
the divergence rewrites every anchor comment on round-trip,
producing diff noise that defeats the purpose of vendoring
the encoder fix at all.

The added test reproduces the divergence using a fixture
sync group with both fields set.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 4.7 (1M context) <noreply@anthropic.com>
mokagio added a commit to mokagio/XcodeProj-Tuist that referenced this pull request May 5, 2026
PR tuist#1037 (tuist#1037)
adds two stored properties to `XCBuildConfiguration`
(`baseConfigurationReferenceAnchor`,
`baseConfigurationReferenceRelativePath`)
but does not regenerate `Equality.generated.swift`.

`Templates/Equality.stencil` iterates over `storedVariables`,
so the regenerated output would already include these fields.
Adding the two comparisons by hand mirrors what Sourcery would
emit and avoids reintroducing a Sourcery toolchain dependency
just to update two lines.

Without this, two configurations differing only in anchor or
relativePath compare equal,
which masks structural divergence in callers that rely on
`isEqual` for round-trip checks.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 4.7 (1M context) <noreply@anthropic.com>
mokagio added a commit to mokagio/XcodeProj-Tuist that referenced this pull request May 5, 2026
The existing `baseConfiguration` setter and PR tuist#1037's new
`baseConfigurationAnchor` setter both wrap the assignment in
`if let newValue`, silently dropping `nil`.
That's incomplete behavior:
the public mutable-property contract leads callers to expect
`x.baseConfiguration = nil` to clear,
not no-op.

The gap matters more during sync-folder migrations:
to convert a configuration from `baseConfigurationReference`
to anchor+relativePath (or vice versa),
callers need to clear the obsolete attachment first.
Without nil-as-clear support that's only reachable through
the package-internal stored properties.

Both setters now mirror the optional reference assignment
directly,
matching what the property type already promises.

Reference: tuist#1037

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 4.7 (1M context) <noreply@anthropic.com>
@mokagio
Copy link
Copy Markdown

mokagio commented May 5, 2026

Thank you @johnrbent for working on this. It's unfortunate it's been sitting here all this time, but such is the fate with many open source projects. Maintainers are busy. One has to be thankful for the tools to exist in the first place and for the open source community to be so collaborative.

I cherry picked your changes in a personal fork because I run in an issue that your work addresses. Thanks again.

- Revert unrelated `.editorconfig` indent_size change
- Split XCBuildConfiguration init into two dedicated initializers
  (legacy PBXGroup form + Xcode 16 anchor/relative-path form) to make
  the mutually-exclusive reference styles explicit and document them
- Reject partial anchor data in the decoder with DecodingError.dataCorrupted
  instead of silently dropping the field
- Fix doc-comment typos
- Add tests covering the partial-anchor-data decoder failure paths

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:XL This PR changes 500-999 lines, ignoring generated files. labels May 5, 2026
@fortmarek
Copy link
Copy Markdown
Member

Hey @johnrbent, thanks a lot for the contribution! I pushed a follow-up commit (145093a) to address a few items from my review. Happy to revert any of these if you'd prefer to handle them yourself. Summary of what changed and why:

.editorconfig

Reverted indent_size from 4 back to 2. The change looked unrelated to the feature and would have shifted indentation behavior repo-wide for every contributor.

Split XCBuildConfiguration init into two dedicated initializers

Replaced the single 4-arg init with:

  • init(name:baseConfiguration:buildSettings:) — the pre-PR signature, source-compatible for existing callers using xcconfigs in a PBXGroup.
  • init(name:baseConfigurationAnchor:baseConfigurationRelativePath:buildSettings:) — the Xcode 16+ form, where the anchor and relative path are non-optional since they only make sense as a pair.

Each init carries an - Important: doc block calling out that the two reference styles are mutually exclusive and pointing readers at the other initializer. This is the documentation-only path you suggested in the original implementation, but expressed at the API level so the type system nudges callers toward the right shape.

Decoder: reject partial anchor data

Previously, if a pbxproj had baseConfigurationReferenceAnchor without baseConfigurationReferenceRelativePath (or vice-versa), the decoder would silently fall through to the legacy baseConfigurationReference path and drop the partial field. The decoder now throws DecodingError.dataCorrupted against the missing key, so corrupt input surfaces immediately rather than silently losing data. Xcode always emits both keys together, so this only fires on genuinely malformed input.

Doc typos

"may be reference""may be referenced", and a missing space before a PBXGroup link.

Tests

Added two failure tests covering the new partial-anchor-data decoder paths (anchor-without-relative-path and relative-path-without-anchor), each asserting DecodingError.dataCorrupted with the expected coding key. Skipped a success-case decode test because super.init(from:) requires a parent coding path (existing limitation of the JSON-decoder test pattern in this file) — the encoder fixture test you added already exercises the full decode→encode round-trip end-to-end.

Things I deliberately left alone

The baseConfigurationAnchor setter still ignores nil assignments. This matches the existing baseConfiguration setter pattern, and changing it felt out of scope for this PR.

All 355 tests pass locally. Let me know what you think!

Fall back to the original behaviour: if only one of `baseConfigurationReferenceAnchor`
and `baseConfigurationReferenceRelativePath` is present, silently skip them and
try `baseConfigurationReference` instead, rather than throwing.

Drop the two tests that asserted the removed throwing behaviour.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@fortmarek
Copy link
Copy Markdown
Member

Quick follow-up — pushed bb28852 to drop the partial-anchor-data throwing behaviour and the two tests that asserted it. Decoder is back to the original silent fall-through, so an anchor without its relative-path partner (or vice-versa) is just skipped, same as before.

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label May 5, 2026
@fortmarek
Copy link
Copy Markdown
Member

Thanks for the PR! Sorry that this has been left unattended for this long. LGTM once the CI is green.

@fortmarek fortmarek changed the title Support new Xcode 16 XCBuildConfiguration format feat: support new Xcode 16 XCBuildConfiguration format May 5, 2026
@fortmarek fortmarek merged commit e5c356f into tuist:main May 5, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm This PR has been approved by a maintainer size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Xcode 16 • XCBuildConfiguration Changes

4 participants