Skip to content

Commit 5ff60d0

Browse files
committed
[xcodegen] Add file references to project first
This requires ensuring we check that we're not ever attempting to add a group under an existing folder reference. This will be required for buildable folders.
1 parent 72b8d22 commit 5ff60d0

File tree

2 files changed

+52
-20
lines changed

2 files changed

+52
-20
lines changed

utils/swift-xcodegen/Sources/SwiftXcodeGen/Generator/ProjectGenerator.swift

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,13 @@ fileprivate final class ProjectGenerator {
2525
private var project = Xcode.Project()
2626
private let allTarget: Xcode.Target
2727

28-
private var groups: [RelativePath: Xcode.Group] = [:]
28+
enum CachedGroup {
29+
/// Covered by a parent folder reference.
30+
case covered
31+
/// Present in the project.
32+
case present(Xcode.Group)
33+
}
34+
private var groups: [RelativePath: CachedGroup] = [:]
2935
private var files: [RelativePath: Xcode.FileReference] = [:]
3036
private var targets: [String: Xcode.Target] = [:]
3137
private var unbuildableSources: [ClangTarget.Source] = []
@@ -103,7 +109,7 @@ fileprivate final class ProjectGenerator {
103109
/// for a file path relative to the project root.
104110
private func parentGroup(
105111
for path: RelativePath
106-
) -> (parentGroup: Xcode.Group, childPath: RelativePath) {
112+
) -> (parentGroup: Xcode.Group, childPath: RelativePath)? {
107113
guard let parent = path.parentDir else {
108114
// We've already handled paths under the repo, so this must be for
109115
// paths outside the repo.
@@ -114,18 +120,31 @@ fileprivate final class ProjectGenerator {
114120
if parent == repoRelativePath || parent == mainRepoDirInProject {
115121
return (project.mainGroup, path)
116122
}
117-
return (group(for: parent), RelativePath(path.fileName))
123+
guard let parentGroup = group(for: parent) else { return nil }
124+
return (parentGroup, RelativePath(path.fileName))
118125
}
119126

120-
private func group(for path: RelativePath) -> Xcode.Group {
121-
if let group = groups[path] {
122-
return group
127+
/// Returns the group for a given path, or `nil` if the path is covered
128+
/// by a parent folder reference.
129+
private func group(for path: RelativePath) -> Xcode.Group? {
130+
if let result = groups[path] {
131+
switch result {
132+
case .covered:
133+
return nil
134+
case .present(let g):
135+
return g
136+
}
137+
}
138+
guard
139+
files[path] == nil, let (parentGroup, childPath) = parentGroup(for: path)
140+
else {
141+
groups[path] = .covered
142+
return nil
123143
}
124-
let (parentGroup, childPath) = parentGroup(for: path)
125144
let group = parentGroup.addGroup(
126145
path: childPath.rawPath, pathBase: .groupDir, name: path.fileName
127146
)
128-
groups[path] = group
147+
groups[path] = .present(group)
129148
return group
130149
}
131150

@@ -167,7 +186,9 @@ fileprivate final class ProjectGenerator {
167186
return nil
168187
}
169188
}
170-
let (parentGroup, childPath) = parentGroup(for: path)
189+
guard let (parentGroup, childPath) = parentGroup(for: path) else {
190+
return nil
191+
}
171192
let file = parentGroup.addFileReference(
172193
path: childPath.rawPath, isDirectory: ref.kind == .folder,
173194
pathBase: .groupDir, name: path.fileName
@@ -190,13 +211,19 @@ fileprivate final class ProjectGenerator {
190211
}
191212

192213
func generateBaseTarget(
193-
_ name: String, productType: Xcode.Target.ProductType?,
194-
includeInAllTarget: Bool
214+
_ name: String, at parentPath: RelativePath?,
215+
productType: Xcode.Target.ProductType?, includeInAllTarget: Bool
195216
) -> Xcode.Target? {
196217
guard targets[name] == nil else {
197218
log.warning("Duplicate target '\(name)', skipping")
198219
return nil
199220
}
221+
// Make sure we can create a group for the parent path, otherwise
222+
// this is nested in a folder reference and there's nothing we can do.
223+
if let parentPath, !parentPath.components.isEmpty,
224+
group(for: repoRelativePath.appending(parentPath)) == nil {
225+
return nil
226+
}
200227
let target = project.addTarget(productType: productType, name: name)
201228
targets[name] = target
202229
if includeInAllTarget {
@@ -263,7 +290,7 @@ fileprivate final class ProjectGenerator {
263290
return
264291
}
265292
let target = generateBaseTarget(
266-
targetInfo.name, productType: .staticArchive,
293+
targetInfo.name, at: targetInfo.parentPath, productType: .staticArchive,
267294
includeInAllTarget: includeInAllTarget
268295
)
269296
guard let target else { return }
@@ -437,7 +464,8 @@ fileprivate final class ProjectGenerator {
437464
)
438465
}
439466
let target = generateBaseTarget(
440-
targetInfo.name, productType: nil, includeInAllTarget: includeInAllTarget
467+
targetInfo.name, at: nil, productType: nil,
468+
includeInAllTarget: includeInAllTarget
441469
)
442470
guard let target else { return nil }
443471

@@ -478,7 +506,7 @@ fileprivate final class ProjectGenerator {
478506
return nil
479507
}
480508
let target = generateBaseTarget(
481-
targetInfo.name, productType: .staticArchive,
509+
targetInfo.name, at: buildRule.parentPath, productType: .staticArchive,
482510
includeInAllTarget: includeInAllTarget
483511
)
484512
guard let target else { return nil }
@@ -599,6 +627,12 @@ fileprivate final class ProjectGenerator {
599627
guard !generated else { return }
600628
generated = true
601629

630+
// First add file/folder references.
631+
for ref in spec.referencesToAdd {
632+
// Allow important references to bypass exclusion checks.
633+
getOrCreateRepoRef(ref, allowExcluded: ref.isImportant)
634+
}
635+
602636
// Gather the Swift targets to generate, including any dependencies.
603637
var swiftTargets: Set<SwiftTarget> = []
604638
for targetSource in spec.swiftTargetSources {
@@ -681,11 +715,6 @@ fileprivate final class ProjectGenerator {
681715
}
682716
}
683717

684-
for ref in spec.referencesToAdd {
685-
// Allow important references to bypass exclusion checks.
686-
getOrCreateRepoRef(ref, allowExcluded: ref.isImportant)
687-
}
688-
689718
// Sort the groups.
690719
sortGroupChildren(project.mainGroup)
691720
}

utils/swift-xcodegen/Sources/SwiftXcodeGen/Path/PathProtocol.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,11 @@ extension PathProtocol {
127127
}
128128

129129
extension Collection where Element: PathProtocol {
130+
/// Computes the common parent for a collection of paths. If there is only
131+
/// a single unique path, this returns the parent for that path.
130132
var commonAncestor: Element? {
131133
guard let first = self.first else { return nil }
132-
return dropFirst().reduce(first, { $0.commonAncestor(with: $1) })
134+
let result = dropFirst().reduce(first, { $0.commonAncestor(with: $1) })
135+
return result == first ? result.parentDir : result
133136
}
134137
}

0 commit comments

Comments
 (0)