tuist / tuist/XcodeProj

Removing a target referenced by PBXFileSystemSynchronizedBuildFileExceptionSet crashes serialization

Open
#1,093 2 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Swift
Stars
2.2k
Forks
355
Avg merge
5h 48m
Merged PRs (30d)
13

Description

Summary

Calling removeFromProject() on a PBXNativeTarget does not cascade to PBXFileSystemSynchronizedBuildFileExceptionSet objects that reference that target. The orphaned exception sets retain a nil target reference, which crashes during serialization when generating the plist annotation (target.name on nil).

Minimal reproduction

Setup (Xcode 16+):

  1. Create a project with two app targets: AppA and AppB
  2. Create a folder SharedSrc/ containing Shared.swift and Info.plist
  3. In Xcode, drag SharedSrc/ into the project as a synchronized folder reference, with both targets checked
  4. Select Info.plist in the navigator, open the File Inspector, and uncheck AppB from Target Membership

This creates a PBXFileSystemSynchronizedBuildFileExceptionSet with target = AppB inside the SharedSrc synchronized root group — meaning "exclude Info.plist from AppB's build."

Reproduction (Ruby xcodeproj gem v1.27.0):

require "xcodeproj"

proj = Xcodeproj::Project.open("Repro.xcodeproj")

# Removing AppA works fine (not referenced by any exception set)
proj.targets.find { |t| t.name == "AppA" }.remove_from_project
proj.save  # => OK

# Removing AppB crashes (referenced by the exception set)
proj.targets.find { |t| t.name == "AppB" }.remove_from_project
proj.save  # => CRASH

Crash:

PBXFileSystemSynchronizedBuildFileExceptionSet#display_name: undefined method 'name' for nil (NoMethodError)
  "Exceptions for \"#{GroupableHelper.parent(self).display_name}\" in \"#{target.name}\" target"

What happens

  • removeFromProject() on the target removes the PBXNativeTarget object, its build configuration list, and its build phases
  • It does not remove PBXFileSystemSynchronizedBuildFileExceptionSet objects whose target property pointed to the removed target
  • On save, the serializer calls display_name on each exception set, which accesses target.name — now nil — and crashes

What should happen

removeFromProject() on a PBXNativeTarget should also remove any PBXFileSystemSynchronizedBuildFileExceptionSet whose target references the removed target. Xcode's UI does this cleanup when deleting a target — the programmatic API should match.

Additional incomplete cascading

Beyond exception sets, removeFromProject() also leaves behind:

  • PBXFileReference for the target's product (.app, .xctest)
  • The product reference in the Products group
  • PBXGroup entries for the target's source directory

These don't crash serialization but leave orphaned entries in the project file.

Workaround

Clean up orphaned exception sets manually before saving:

proj.objects.select { |o|
  o.isa == "PBXFileSystemSynchronizedBuildFileExceptionSet" &&
  o.respond_to?(:target) && o.target.nil?
}.each { |o| o.remove_from_project }

Environment

  • Ruby xcodeproj gem 1.27.0
  • Project created with Xcode 26 beta (uses synchronized folders / PBXFileSystemSynchronizedRootGroup)
  • Also reproduced via xcodeproj-mcp-server v1.5.0, which uses the Swift tuist/XcodeProj library — same crash (exit code 133 / SIGTRAP, the Swift equivalent of nil access)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at PBXNativeTarget#remove_from_project and PBXFileSystemSynchronizedBuildFileExceptionSet#display_name, then reproduce the AppA/AppB case from the issue with the Ruby xcodeproj gem. Trace how target references are removed and how serialization reaches the exception set. Done means removing a referenced target cleans up its exception sets and saving no longer crashes; consider the listed orphaned entries if they are in scope.

Written by the indexing model from the issue text.

Assessment

Tech stack
swift
Domain
build-system, devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.