mirror of
https://github.com/yonaskolb/XcodeGen.git
synced 2026-03-18 20:02:25 +00:00
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:
@@ -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
|
||||
|
||||
@@ -830,9 +830,23 @@
|
||||
FED40A89162E446494DDE7C7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
9A259ACEBCE19CC5F22B6DD4 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
ExcludedFile.swift,
|
||||
Info.plist,
|
||||
);
|
||||
target = 0867B0DACEF28C11442DE8F7 /* App_iOS */;
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
AE2AB2772F70DFFF402AA02B /* SyncedFolder */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
9A259ACEBCE19CC5F22B6DD4 /* PBXFileSystemSynchronizedBuildFileExceptionSet */,
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
// excluded
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
</plist>
|
||||
@@ -166,6 +166,9 @@ targets:
|
||||
- String Catalogs/LocalizableStrings.xcstrings
|
||||
- path: SyncedFolder
|
||||
type: syncedFolder
|
||||
excludes:
|
||||
- ExcludedFile.swift
|
||||
- Info.plist
|
||||
settings:
|
||||
INFOPLIST_FILE: App_iOS/Info.plist
|
||||
PRODUCT_BUNDLE_IDENTIFIER: com.project.app
|
||||
|
||||
@@ -120,6 +120,99 @@ class SourceGeneratorTests: XCTestCase {
|
||||
try expect([syncedFolder]) == pbxProj.nativeTargets.first?.fileSystemSynchronizedGroups
|
||||
}
|
||||
|
||||
$0.it("adds excludes as membership exceptions for synced folder") {
|
||||
let directories = """
|
||||
Sources:
|
||||
- a.swift
|
||||
- b.swift
|
||||
- Generated:
|
||||
- c.generated.swift
|
||||
- d.generated.swift
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let source = TargetSource(path: "Sources", excludes: ["b.swift", "Generated/*.generated.swift"], type: .syncedFolder)
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [source])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
let syncedFolders = try pbxProj.getMainGroup().children.compactMap { $0 as? PBXFileSystemSynchronizedRootGroup }
|
||||
let syncedFolder = try unwrap(syncedFolders.first)
|
||||
|
||||
let exceptionSets = syncedFolder.exceptions?.compactMap { $0 as? PBXFileSystemSynchronizedBuildFileExceptionSet }
|
||||
let exceptionSet = try unwrap(exceptionSets?.first)
|
||||
let exceptions = try unwrap(exceptionSet.membershipExceptions)
|
||||
|
||||
try expect(exceptions.contains("b.swift")) == true
|
||||
try expect(exceptions.contains("Generated/c.generated.swift")) == true
|
||||
try expect(exceptions.contains("Generated/d.generated.swift")) == true
|
||||
try expect(exceptions.contains("a.swift")) == false
|
||||
}
|
||||
|
||||
$0.it("auto-excludes Info.plist from synced folder membership") {
|
||||
let directories = """
|
||||
Sources:
|
||||
- a.swift
|
||||
- Info.plist
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let source = TargetSource(path: "Sources", type: .syncedFolder)
|
||||
let target = Target(
|
||||
name: "Test",
|
||||
type: .application,
|
||||
platform: .iOS,
|
||||
settings: try Settings(jsonDictionary: ["INFOPLIST_FILE": "Sources/Info.plist"]),
|
||||
sources: [source]
|
||||
)
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
let syncedFolders = try pbxProj.getMainGroup().children.compactMap { $0 as? PBXFileSystemSynchronizedRootGroup }
|
||||
let syncedFolder = try unwrap(syncedFolders.first)
|
||||
|
||||
let exceptionSets = syncedFolder.exceptions?.compactMap { $0 as? PBXFileSystemSynchronizedBuildFileExceptionSet }
|
||||
let exceptionSet = try unwrap(exceptionSets?.first)
|
||||
let exceptions = try unwrap(exceptionSet.membershipExceptions)
|
||||
|
||||
try expect(exceptions.contains("Info.plist")) == true
|
||||
}
|
||||
|
||||
$0.it("creates no exception set for synced folder without excludes") {
|
||||
let directories = """
|
||||
Sources:
|
||||
- a.swift
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let source = TargetSource(path: "Sources", type: .syncedFolder)
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [source])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
let syncedFolders = try pbxProj.getMainGroup().children.compactMap { $0 as? PBXFileSystemSynchronizedRootGroup }
|
||||
let syncedFolder = try unwrap(syncedFolders.first)
|
||||
|
||||
try expect(syncedFolder.exceptions?.isEmpty ?? true) == true
|
||||
}
|
||||
|
||||
$0.it("adds empty resources build phase for synced folder") {
|
||||
let directories = """
|
||||
Sources:
|
||||
- a.swift
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let source = TargetSource(path: "Sources", type: .syncedFolder)
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [source])
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target])
|
||||
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
let nativeTarget = try unwrap(pbxProj.nativeTargets.first)
|
||||
let hasResourcesPhase = nativeTarget.buildPhases.contains { $0 is PBXResourcesBuildPhase }
|
||||
try expect(hasResourcesPhase) == true
|
||||
}
|
||||
|
||||
$0.it("supports frameworks in sources") {
|
||||
let directories = """
|
||||
Sources:
|
||||
|
||||
Reference in New Issue
Block a user