Handle membership exceptions for synchronized root groups (#1587)

* Handle membership exceptions for synchronized root groups

Adds logic to detect and register membership exceptions for PBXFileSystemSynchronizedRootGroup objects, specifically excluding Info.plist files from group membership when necessary. Also ensures resources build phase is added if synchronized root groups are present.

* Refactor synced folder membership exceptions with glob support

Extract configureMembershipExceptions into its own method, use Set for
dedup, resolve excludes via glob expansion, and add a no-op test case.
Incorporates glob support and tests from macguru@baf1108.

* Update UUID

* Comment out excludes in project.yml

Comment out excludes for ExcludedFile.swift due to CI issue.

* Clean up project.pbxproj by removing exception set

Removed PBXFileSystemSynchronizedBuildFileExceptionSet section and its references.

* Remove comment

* Update SourceGeneratorTests.swift

* Update project.pbxproj

* Retrigger CI

* Add info.plist exclusion
This commit is contained in:
mirkokg
2026-03-04 08:22:27 +11:00
committed by GitHub
parent a904543801
commit a0afcefaed
7 changed files with 171 additions and 1 deletions
+50 -1
View File
@@ -1141,7 +1141,8 @@ public class PBXProjGenerator {
func addResourcesBuildPhase() {
let resourcesBuildPhaseFiles = getBuildFilesForPhase(.resources) + copyResourcesReferences
if !resourcesBuildPhaseFiles.isEmpty {
let hasSynchronizedRootGroups = sourceFiles.contains { $0.fileReference is PBXFileSystemSynchronizedRootGroup }
if !resourcesBuildPhaseFiles.isEmpty || hasSynchronizedRootGroups {
let resourcesBuildPhase = addObject(PBXResourcesBuildPhase(files: resourcesBuildPhaseFiles))
buildPhases.append(resourcesBuildPhase)
}
@@ -1460,9 +1461,57 @@ public class PBXProjGenerator {
// add fileSystemSynchronizedGroups
let synchronizedRootGroups = sourceFiles.compactMap { $0.fileReference as? PBXFileSystemSynchronizedRootGroup }
if !synchronizedRootGroups.isEmpty {
for syncedGroup in synchronizedRootGroups {
configureMembershipExceptions(
for: syncedGroup,
target: target,
targetObject: targetObject,
infoPlistFiles: infoPlistFiles
)
}
targetObject.fileSystemSynchronizedGroups = synchronizedRootGroups
}
}
private func configureMembershipExceptions(
for syncedGroup: PBXFileSystemSynchronizedRootGroup,
target: Target,
targetObject: PBXTarget,
infoPlistFiles: [Config: String]
) {
guard let syncedGroupPath = syncedGroup.path else { return }
let syncedPath = (project.basePath + Path(syncedGroupPath)).normalize()
guard let targetSource = target.sources.first(where: {
(project.basePath + $0.path).normalize() == syncedPath
}) else { return }
var exceptions: Set<String> = Set(
sourceGenerator.expandedExcludes(for: targetSource)
.compactMap { try? $0.relativePath(from: syncedPath).string }
)
for infoPlistPath in Set(infoPlistFiles.values) {
let relative = try? (project.basePath + infoPlistPath).normalize()
.relativePath(from: syncedPath)
if let rel = relative?.string, !rel.hasPrefix("..") {
exceptions.insert(rel)
}
}
guard !exceptions.isEmpty else { return }
let exceptionSet = PBXFileSystemSynchronizedBuildFileExceptionSet(
target: targetObject,
membershipExceptions: exceptions.sorted(),
publicHeaders: nil,
privateHeaders: nil,
additionalCompilerFlagsByRelativePath: nil,
attributesByRelativePath: nil
)
addObject(exceptionSet)
syncedGroup.exceptions = (syncedGroup.exceptions ?? []) + [exceptionSet]
}
private func makePlatformFilter(for filter: Dependency.PlatformFilter) -> String? {
switch filter {
@@ -372,6 +372,11 @@ class SourceGenerator {
return variantGroup
}
/// Returns the expanded set of excluded paths for a target source by resolving its exclude glob patterns.
func expandedExcludes(for targetSource: TargetSource) -> Set<Path> {
getSourceMatches(targetSource: targetSource, patterns: targetSource.excludes)
}
/// Collects all the excluded paths within the targetSource
private func getSourceMatches(targetSource: TargetSource, patterns: [String]) -> Set<Path> {
let rootSourcePath = project.basePath + targetSource.path