Skip to content

Commit d982763

Browse files
authored
Merge branch 'master' into fix-missing-product-ref-group
2 parents 57cb94f + 167d119 commit d982763

13 files changed

Lines changed: 210 additions & 31 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
## Next Version
44

5-
### Fixed
65
- Fix missing productRefGroup in generated projects #1591 @ruslic19
6+
- Adds an `explicitFolders` property to `TargetSource` that is passed through to `PBXFileSystemSynchronizedRootGroup`, to turn entire subfolders into Resources. #1596 @macguru
7+
- Allow synced folders to be sorted using `groupOrdering`. #1596 @macguru
8+
- Fixed synced folders ignoring `createIntermediateGroups=YES` and always beging created at the root level. #1596 @macguru
9+
- Fix membership exceptions not working for nested synced folders with intermediate groups enabled. #1596 @macguru
710

811
## 2.44.1
912

Docs/ProjectSpec.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,7 @@ A source can be provided via a string (the path) or an object of the form:
520520
- [ ] **compilerFlags**: **[String]** or **String** - A list of compilerFlags to add to files under this specific path provided as a list or a space delimited string. Defaults to empty.
521521
- [ ] **excludes**: **[String]** - A list of [global patterns](https://en.wikipedia.org/wiki/Glob_(programming)) representing the files to exclude. These rules are relative to `path` and _not the directory where `project.yml` resides_. XcodeGen uses Bash 4's Glob behaviors where globstar (**) is enabled.
522522
- [ ] **includes**: **[String]** - A list of global patterns in the same format as `excludes` representing the files to include. These rules are relative to `path` and _not the directory where `project.yml` resides_. If **excludes** is present and file conflicts with **includes**, **excludes** will override the **includes** behavior.
523+
- [ ] **explicitFolders**: **[String]** - Only valid for `syncedFolder` type. A list of global patterns in the same format as `excludes` to child folders that Xcode should treat as folder references.
523524
- [ ] **destinationFilters**: **[[Supported Destinations](#supported-destinations)]** - List of supported platform destinations the files should filter to. Defaults to all supported destinations.
524525
- [ ] **inferDestinationFiltersByPath**: **Bool** - This is a convenience filter that helps you to filter the files if their paths match these patterns `**/<supportedDestination>/*` or `*_<supportedDestination>.swift`. Note, if you use `destinationFilters` this flag will be ignored.
525526
- [ ] **createIntermediateGroups**: **Bool** - This overrides the value in [Options](#options).

Sources/ProjectSpec/TargetSource.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public struct TargetSource: Equatable {
1616
public var compilerFlags: [String]
1717
public var excludes: [String]
1818
public var includes: [String]
19+
public var explicitFolders: [String]
1920
public var type: SourceType?
2021
public var optional: Bool
2122
public var buildPhase: BuildPhaseSpec?
@@ -47,6 +48,7 @@ public struct TargetSource: Equatable {
4748
compilerFlags: [String] = [],
4849
excludes: [String] = [],
4950
includes: [String] = [],
51+
explicitFolders: [String] = [],
5052
type: SourceType? = nil,
5153
optional: Bool = optionalDefault,
5254
buildPhase: BuildPhaseSpec? = nil,
@@ -63,6 +65,7 @@ public struct TargetSource: Equatable {
6365
self.compilerFlags = compilerFlags
6466
self.excludes = excludes
6567
self.includes = includes
68+
self.explicitFolders = explicitFolders
6669
self.type = type
6770
self.optional = optional
6871
self.buildPhase = buildPhase
@@ -106,6 +109,7 @@ extension TargetSource: JSONObjectConvertible {
106109
headerVisibility = jsonDictionary.json(atKeyPath: "headerVisibility")
107110
excludes = jsonDictionary.json(atKeyPath: "excludes") ?? []
108111
includes = jsonDictionary.json(atKeyPath: "includes") ?? []
112+
explicitFolders = jsonDictionary.json(atKeyPath: "explicitFolders") ?? []
109113
type = jsonDictionary.json(atKeyPath: "type")
110114
optional = jsonDictionary.json(atKeyPath: "optional") ?? TargetSource.optionalDefault
111115

@@ -133,6 +137,7 @@ extension TargetSource: JSONEncodable {
133137
"compilerFlags": compilerFlags,
134138
"excludes": excludes,
135139
"includes": includes,
140+
"explicitFolders": explicitFolders,
136141
"name": name,
137142
"group": group,
138143
"headerVisibility": headerVisibility?.rawValue,

Sources/XcodeGenKit/PBXProjGenerator.swift

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,29 +1460,30 @@ public class PBXProjGenerator {
14601460
}
14611461

14621462
// add fileSystemSynchronizedGroups
1463-
let synchronizedRootGroups = sourceFiles.compactMap { $0.fileReference as? PBXFileSystemSynchronizedRootGroup }
1463+
let synchronizedRootGroups: [PBXFileSystemSynchronizedRootGroup] = sourceFiles.compactMap { sourceFile in
1464+
guard let syncedGroup = sourceFile.fileReference as? PBXFileSystemSynchronizedRootGroup else { return nil }
1465+
1466+
configureMembershipExceptions(
1467+
for: syncedGroup,
1468+
path: sourceFile.path,
1469+
target: target,
1470+
targetObject: targetObject,
1471+
infoPlistFiles: infoPlistFiles
1472+
)
1473+
return syncedGroup
1474+
}
14641475
if !synchronizedRootGroups.isEmpty {
1465-
for syncedGroup in synchronizedRootGroups {
1466-
configureMembershipExceptions(
1467-
for: syncedGroup,
1468-
target: target,
1469-
targetObject: targetObject,
1470-
infoPlistFiles: infoPlistFiles
1471-
)
1472-
}
14731476
targetObject.fileSystemSynchronizedGroups = synchronizedRootGroups
14741477
}
14751478
}
14761479

14771480
private func configureMembershipExceptions(
14781481
for syncedGroup: PBXFileSystemSynchronizedRootGroup,
1482+
path syncedPath: Path,
14791483
target: Target,
14801484
targetObject: PBXTarget,
14811485
infoPlistFiles: [Config: String]
14821486
) {
1483-
guard let syncedGroupPath = syncedGroup.path else { return }
1484-
let syncedPath = (project.basePath + Path(syncedGroupPath)).normalize()
1485-
14861487
guard let targetSource = target.sources.first(where: {
14871488
(project.basePath + $0.path).normalize() == syncedPath
14881489
}) else { return }
@@ -1693,13 +1694,13 @@ extension Platform {
16931694
}
16941695

16951696
extension PBXFileElement {
1696-
/// - returns: `true` if the element is a group or a folder reference. Likely an SPM package.
1697+
/// - returns: `true` if the element is a group, a folder reference (likely an SPM package), or a synced folder.
16971698
var isGroupOrFolder: Bool {
1698-
self is PBXGroup || (self as? PBXFileReference)?.lastKnownFileType == "folder"
1699+
self is PBXGroup || self is PBXFileSystemSynchronizedRootGroup || (self as? PBXFileReference)?.lastKnownFileType == "folder"
16991700
}
17001701

17011702
public func getSortOrder(groupSortPosition: SpecOptions.GroupSortPosition) -> Int {
1702-
if type(of: self).isa == "PBXGroup" {
1703+
if self is PBXGroup || self is PBXFileSystemSynchronizedRootGroup {
17031704
switch groupSortPosition {
17041705
case .top: return -1
17051706
case .bottom: return 1

Sources/XcodeGenKit/SourceGenerator.swift

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,20 @@ class SourceGenerator {
399399
)
400400
}
401401

402+
/// Expands glob patterns in `explicitFolders` relative to the synced root path.
403+
private func resolveExplicitFolders(targetSource: TargetSource) -> [String] {
404+
let rootSourcePath = project.basePath + targetSource.path
405+
406+
return targetSource.explicitFolders.flatMap { pattern in
407+
let matches = Glob(pattern: "\(rootSourcePath)/\(pattern)")
408+
.map { Path($0) }
409+
.filter { $0.isDirectory }
410+
.compactMap { try? $0.relativePath(from: rootSourcePath).string }
411+
.sorted()
412+
return matches.isEmpty ? [pattern] : matches
413+
}
414+
}
415+
402416
/// Checks whether the path is not in any default or TargetSource excludes
403417
func isIncludedPath(_ path: Path, excludePaths: Set<Path>, includePaths: SortedArray<Path>?) -> Bool {
404418
return !defaultExcludedFiles.contains(where: { path.lastComponent == $0 })
@@ -695,20 +709,22 @@ class SourceGenerator {
695709
case .syncedFolder:
696710

697711
let relativePath = (try? path.relativePath(from: project.basePath)) ?? path
712+
let resolvedExplicitFolders = resolveExplicitFolders(targetSource: targetSource)
698713

699714
let syncedRootGroup = PBXFileSystemSynchronizedRootGroup(
700715
sourceTree: .group,
701716
path: relativePath.string,
702717
name: targetSource.name,
703718
explicitFileTypes: [:],
704719
exceptions: [],
705-
explicitFolders: []
720+
explicitFolders: resolvedExplicitFolders
706721
)
707722
addObject(syncedRootGroup)
708723
sourceReference = syncedRootGroup
709724

710-
// TODO: adjust if hasCustomParent == true
711-
rootGroups.insert(syncedRootGroup)
725+
if !(createIntermediateGroups || hasCustomParent) || path.parent() == project.basePath {
726+
rootGroups.insert(syncedRootGroup)
727+
}
712728

713729
let sourceFile = generateSourceFile(
714730
targetType: targetType,
@@ -725,6 +741,7 @@ class SourceGenerator {
725741
try makePathRelative(for: sourceReference, at: path)
726742
} else if createIntermediateGroups {
727743
createIntermediaGroups(for: sourceReference, at: sourcePath)
744+
try makePathRelative(for: sourceReference, at: sourcePath)
728745
}
729746

730747
return sourceFiles

Tests/Fixtures/TestProject/Project.xcodeproj/project.pbxproj

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,16 @@
842842
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
843843

844844
/* Begin PBXFileSystemSynchronizedRootGroup section */
845+
A2F1B5386E15A261AC8A4DEE /* SyncedChild */ = {
846+
isa = PBXFileSystemSynchronizedRootGroup;
847+
explicitFileTypes = {
848+
};
849+
explicitFolders = (
850+
);
851+
name = SyncedChild;
852+
path = SyncedChild;
853+
sourceTree = "<group>";
854+
};
845855
AE2AB2772F70DFFF402AA02B /* SyncedFolder */ = {
846856
isa = PBXFileSystemSynchronizedRootGroup;
847857
exceptions = (
@@ -850,6 +860,9 @@
850860
explicitFileTypes = {
851861
};
852862
explicitFolders = (
863+
Resources,
864+
FeatureATests,
865+
FeatureBTests,
853866
);
854867
path = SyncedFolder;
855868
sourceTree = "<group>";
@@ -1012,6 +1025,12 @@
10121025
isa = PBXGroup;
10131026
children = (
10141027
2F80635127D17ECB7748067B /* FolderWithDot2.0 */,
1028+
CE1F06D99242F4223D081F0D /* LaunchScreen.storyboard */,
1029+
9E17D598D98065767A04740F /* Localizable.strings */,
1030+
65C8D6D1DDC1512D396C07B7 /* Localizable.stringsdict */,
1031+
0C6BA0D12467A13EC012C728 /* LocalizedStoryboard.storyboard */,
1032+
814D72C2B921F60B759C2D4B /* Main.storyboard */,
1033+
306796628DD52FA55E833B65 /* Model.xcdatamodeld */,
10151034
AEBCA8CFF769189C0D52031E /* App_iOS.xctestplan */,
10161035
F0D48A913C087D049C8EDDD7 /* App.entitlements */,
10171036
7F1A2F579A6F79C62DDA0571 /* AppDelegate.swift */,
@@ -1020,12 +1039,6 @@
10201039
B5C943D39DD7812CAB94B614 /* Documentation.docc */,
10211040
C9DDE1B06BCC1CDE0ECF1589 /* Info.plist */,
10221041
AAA49985DFFE797EE8416887 /* inputList.xcfilelist */,
1023-
CE1F06D99242F4223D081F0D /* LaunchScreen.storyboard */,
1024-
9E17D598D98065767A04740F /* Localizable.strings */,
1025-
65C8D6D1DDC1512D396C07B7 /* Localizable.stringsdict */,
1026-
0C6BA0D12467A13EC012C728 /* LocalizedStoryboard.storyboard */,
1027-
814D72C2B921F60B759C2D4B /* Main.storyboard */,
1028-
306796628DD52FA55E833B65 /* Model.xcdatamodeld */,
10291042
BF59AC868D227C92CA8B1B57 /* Model.xcmappingmodel */,
10301043
C7809CE9FE9852C2AA87ACE5 /* module.modulemap */,
10311044
553D289724905857912C7A1D /* outputList.xcfilelist */,
@@ -1068,6 +1081,8 @@
10681081
BDA839814AF73F01F7710518 /* StaticLibrary_ObjC */,
10691082
CBDAC144248EE9D3838C6AAA /* StaticLibrary_Swift */,
10701083
6E0D17C5B4E6F01B89254309 /* String Catalogs */,
1084+
AE2AB2772F70DFFF402AA02B /* SyncedFolder */,
1085+
AB527E0D553CE53AF54C39CD /* SyncedParent */,
10711086
8CFD8AD4820FAB9265663F92 /* Tool */,
10721087
4C7F5EB7D6F3E0E9B426AB4A /* Utilities */,
10731088
3FEA12CF227D41EF50E5C2DB /* Vendor */,
@@ -1076,7 +1091,6 @@
10761091
2E1E747C7BC434ADB80CC269 /* Headers */,
10771092
6B1603BA83AA0C7B94E45168 /* ResourceFolder */,
10781093
6BBE762F36D94AB6FFBFE834 /* SomeFile */,
1079-
AE2AB2772F70DFFF402AA02B /* SyncedFolder */,
10801094
79DC4A1E4D2E0D3A215179BC /* Bundles */,
10811095
FC1515684236259C50A7747F /* Frameworks */,
10821096
AC523591AC7BE9275003D2DB /* Products */,
@@ -1317,6 +1331,14 @@
13171331
path = iMessageApp;
13181332
sourceTree = "<group>";
13191333
};
1334+
AB527E0D553CE53AF54C39CD /* SyncedParent */ = {
1335+
isa = PBXGroup;
1336+
children = (
1337+
A2F1B5386E15A261AC8A4DEE /* SyncedChild */,
1338+
);
1339+
path = SyncedParent;
1340+
sourceTree = "<group>";
1341+
};
13201342
AC523591AC7BE9275003D2DB /* Products */ = {
13211343
isa = PBXGroup;
13221344
children = (
@@ -1368,9 +1390,9 @@
13681390
BAE6C12745737019DC9E98BF /* App_watchOS */ = {
13691391
isa = PBXGroup;
13701392
children = (
1393+
C872631362DDBAFCE71E5C66 /* Interface.storyboard */,
13711394
D8A016580A3B8F72B820BFBF /* Assets.xcassets */,
13721395
FED40A89162E446494DDE7C7 /* Info.plist */,
1373-
C872631362DDBAFCE71E5C66 /* Interface.storyboard */,
13741396
);
13751397
path = App_watchOS;
13761398
sourceTree = "<group>";
@@ -1388,9 +1410,9 @@
13881410
BF58996786F85CB77BEE72EF /* iMessageExtension */ = {
13891411
isa = PBXGroup;
13901412
children = (
1413+
753001CDCEAA4C4E1AFF8E87 /* MainInterface.storyboard */,
13911414
1BC32A813B80A53962A1F365 /* Assets.xcassets */,
13921415
40863AE6202CFCD0529D8438 /* Info.plist */,
1393-
753001CDCEAA4C4E1AFF8E87 /* MainInterface.storyboard */,
13941416
B198242976C3395E31FE000A /* MessagesViewController.swift */,
13951417
);
13961418
path = iMessageExtension;
@@ -1399,12 +1421,12 @@
13991421
C81493FAD71E9A9A19E00AD5 /* App_Clip */ = {
14001422
isa = PBXGroup;
14011423
children = (
1424+
79325B44B19B83EC6CEDBCC5 /* LaunchScreen.storyboard */,
1425+
2FC2A8A829CE71B1CF415FF7 /* Main.storyboard */,
14021426
23A2F16890ECF2EE3FED72AE /* AppDelegate.swift */,
14031427
59DA55A04FA2366B5D0BEEFF /* Assets.xcassets */,
14041428
1FA5E208EC184E3030D2A21D /* Clip.entitlements */,
14051429
6F165CDD5BCC13AFF50B65E2 /* Info.plist */,
1406-
79325B44B19B83EC6CEDBCC5 /* LaunchScreen.storyboard */,
1407-
2FC2A8A829CE71B1CF415FF7 /* Main.storyboard */,
14081430
DFE6A6FAAFF701FE729293DE /* ViewController.swift */,
14091431
);
14101432
path = App_Clip;
@@ -1449,10 +1471,10 @@
14491471
EE78B4FBD0137D1975C47D76 /* App_macOS */ = {
14501472
isa = PBXGroup;
14511473
children = (
1474+
74FBDFA5CB063F6001AD8ACD /* Main.storyboard */,
14521475
9528528C989D24FE3E6C533E /* App-Info.plist */,
14531476
09B82F603D981398F38D762E /* AppDelegate.swift */,
14541477
E55F45EACB0F382722D61C8D /* Assets.xcassets */,
1455-
74FBDFA5CB063F6001AD8ACD /* Main.storyboard */,
14561478
A4C3FE6B986506724DAB5D0F /* ViewController.swift */,
14571479
);
14581480
path = App_macOS;
@@ -1714,6 +1736,7 @@
17141736
981D116D40DBA0407D0E0E94 /* PBXTargetDependency */,
17151737
);
17161738
fileSystemSynchronizedGroups = (
1739+
A2F1B5386E15A261AC8A4DEE /* SyncedChild */,
17171740
AE2AB2772F70DFFF402AA02B /* SyncedFolder */,
17181741
);
17191742
name = App_iOS;

Tests/Fixtures/TestProject/SyncedFolder/FeatureATests/__Snapshots__/.gitkeep

Whitespace-only changes.

Tests/Fixtures/TestProject/SyncedFolder/FeatureBTests/__Snapshots__/.gitkeep

Whitespace-only changes.

Tests/Fixtures/TestProject/SyncedFolder/Resources/.gitkeep

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import Foundation

0 commit comments

Comments
 (0)