mirror of
https://github.com/yonaskolb/XcodeGen.git
synced 2026-03-18 20:02:25 +00:00
Added new group parameter to target sources, allowing to add them to a custom parent group (resolving #478)
This commit is contained in:
committed by
Romuald CARI
parent
538d1c6804
commit
15e615300e
@@ -309,6 +309,7 @@ A source can be provided via a string (the path) or an object of the form:
|
||||
|
||||
- [x] **path**: **String** - The path to the source file or directory.
|
||||
- [ ] **name**: **String** - Can be used to override the name of the source file or directory. By default the last component of the path is used for the name
|
||||
- [ ] **group**: **String** - Can be used to override the parent group of the source file or directory. By default a group is created at the root with the name of this source file or directory or intermediate groups are created if `createIntermediateGroups` is set to `true`. Multiple groups can be created by separating each one using a `/`. If multiple target sources share the same `group`, they will be put together in the same parent group.
|
||||
- [ ] **compilerFlags**: **[String]** or **String** - A list of compilerFlags to add to files under this specific path provided as a list or a space delimitted string. Defaults to empty.
|
||||
- [ ] **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.
|
||||
- [ ] **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.
|
||||
|
||||
@@ -9,6 +9,7 @@ public struct TargetSource: Equatable {
|
||||
|
||||
public var path: String
|
||||
public var name: String?
|
||||
public var group: String?
|
||||
public var compilerFlags: [String]
|
||||
public var excludes: [String]
|
||||
public var includes: [String]
|
||||
@@ -124,6 +125,7 @@ public struct TargetSource: Equatable {
|
||||
public init(
|
||||
path: String,
|
||||
name: String? = nil,
|
||||
group: String? = nil,
|
||||
compilerFlags: [String] = [],
|
||||
excludes: [String] = [],
|
||||
includes: [String] = [],
|
||||
@@ -136,6 +138,7 @@ public struct TargetSource: Equatable {
|
||||
) {
|
||||
self.path = path
|
||||
self.name = name
|
||||
self.group = group
|
||||
self.compilerFlags = compilerFlags
|
||||
self.excludes = excludes
|
||||
self.includes = includes
|
||||
@@ -168,6 +171,7 @@ extension TargetSource: JSONObjectConvertible {
|
||||
public init(jsonDictionary: JSONDictionary) throws {
|
||||
path = try jsonDictionary.json(atKeyPath: "path")
|
||||
name = jsonDictionary.json(atKeyPath: "name")
|
||||
group = jsonDictionary.json(atKeyPath: "group")
|
||||
|
||||
let maybeCompilerFlagsString: String? = jsonDictionary.json(atKeyPath: "compilerFlags")
|
||||
let maybeCompilerFlagsArray: [String]? = jsonDictionary.json(atKeyPath: "compilerFlags")
|
||||
@@ -198,6 +202,7 @@ extension TargetSource: JSONEncodable {
|
||||
"excludes": excludes,
|
||||
"includes": includes,
|
||||
"name": name,
|
||||
"group": group,
|
||||
"headerVisibility": headerVisibility?.rawValue,
|
||||
"type": type?.rawValue,
|
||||
"buildPhase": buildPhase?.toJSONValue(),
|
||||
|
||||
@@ -143,6 +143,7 @@ class SourceGenerator {
|
||||
path: parentPath,
|
||||
mergingChildren: [fileReference],
|
||||
createIntermediateGroups: createIntermediateGroups,
|
||||
hasCustomParent: false,
|
||||
isBaseGroup: true
|
||||
)
|
||||
|
||||
@@ -270,7 +271,7 @@ class SourceGenerator {
|
||||
|
||||
/// Create a group or return an existing one at the path.
|
||||
/// Any merged children are added to a new group or merged into an existing one.
|
||||
private func getGroup(path: Path, name: String? = nil, mergingChildren children: [PBXFileElement], createIntermediateGroups: Bool, isBaseGroup: Bool) -> PBXGroup {
|
||||
private func getGroup(path: Path, name: String? = nil, mergingChildren children: [PBXFileElement], createIntermediateGroups: Bool, hasCustomParent: Bool, isBaseGroup: Bool) -> PBXGroup {
|
||||
let groupReference: PBXGroup
|
||||
|
||||
if let cachedGroup = groupsByPath[path] {
|
||||
@@ -293,10 +294,11 @@ class SourceGenerator {
|
||||
let isRootPath = (isBaseGroup && isOutOfBasePath) || path.parent() == project.basePath
|
||||
|
||||
// is a top level group in the project
|
||||
let isTopLevelGroup = (isBaseGroup && !createIntermediateGroups) || isRootPath
|
||||
let isTopLevelGroup = !hasCustomParent && ((isBaseGroup && !createIntermediateGroups) || isRootPath)
|
||||
|
||||
let groupName = name ?? path.lastComponent
|
||||
let groupPath = resolveGroupPath(path, isTopLevelGroup: isTopLevelGroup)
|
||||
|
||||
let groupPath = resolveGroupPath(path, isTopLevelGroup: hasCustomParent || isTopLevelGroup)
|
||||
|
||||
let group = PBXGroup(
|
||||
children: children,
|
||||
@@ -387,8 +389,16 @@ class SourceGenerator {
|
||||
}
|
||||
|
||||
/// creates all the source files and groups they belong to for a given targetSource
|
||||
private func getGroupSources(targetType: PBXProductType, targetSource: TargetSource, path: Path, isBaseGroup: Bool, excludePaths: Set<Path>, includePaths: Set<Path>)
|
||||
throws -> (sourceFiles: [SourceFile], groups: [PBXGroup]) {
|
||||
private func getGroupSources(
|
||||
targetType: PBXProductType,
|
||||
targetSource: TargetSource,
|
||||
path: Path,
|
||||
isBaseGroup: Bool,
|
||||
createIntermediateGroups: Bool,
|
||||
hasCustomParent: Bool,
|
||||
excludePaths: Set<Path>,
|
||||
includePaths: Set<Path>
|
||||
) throws -> (sourceFiles: [SourceFile], groups: [PBXGroup]) {
|
||||
|
||||
let children = try getSourceChildren(targetSource: targetSource, dirPath: path, excludePaths: excludePaths, includePaths: includePaths)
|
||||
|
||||
@@ -408,12 +418,17 @@ class SourceGenerator {
|
||||
var groups: [PBXGroup] = []
|
||||
|
||||
for path in directories {
|
||||
let subGroups = try getGroupSources(targetType: targetType,
|
||||
targetSource: targetSource,
|
||||
path: path,
|
||||
isBaseGroup: false,
|
||||
excludePaths: excludePaths,
|
||||
includePaths: includePaths)
|
||||
|
||||
let subGroups = try getGroupSources(
|
||||
targetType: targetType,
|
||||
targetSource: targetSource,
|
||||
path: path,
|
||||
isBaseGroup: false,
|
||||
createIntermediateGroups: createIntermediateGroups,
|
||||
hasCustomParent: false,
|
||||
excludePaths: excludePaths,
|
||||
includePaths: includePaths
|
||||
)
|
||||
|
||||
guard !subGroups.sourceFiles.isEmpty || project.options.generateEmptyDirectories else {
|
||||
continue
|
||||
@@ -499,12 +514,11 @@ class SourceGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
let createIntermediateGroups = targetSource.createIntermediateGroups ?? project.options.createIntermediateGroups
|
||||
|
||||
let group = getGroup(
|
||||
path: path,
|
||||
mergingChildren: groupChildren,
|
||||
createIntermediateGroups: createIntermediateGroups,
|
||||
hasCustomParent: hasCustomParent,
|
||||
isBaseGroup: isBaseGroup
|
||||
)
|
||||
if createIntermediateGroups {
|
||||
@@ -524,6 +538,10 @@ class SourceGenerator {
|
||||
let includePaths = getSourceMatches(targetSource: targetSource, patterns: targetSource.includes)
|
||||
|
||||
let type = targetSource.type ?? (path.isFile || path.extension != nil ? .file : .group)
|
||||
|
||||
let customParentGroups = (targetSource.group ?? "").split(separator: "/")
|
||||
let hasCustomParent = !customParentGroups.isEmpty
|
||||
|
||||
let createIntermediateGroups = targetSource.createIntermediateGroups ?? project.options.createIntermediateGroups
|
||||
|
||||
var sourceFiles: [SourceFile] = []
|
||||
@@ -540,7 +558,7 @@ class SourceGenerator {
|
||||
lastKnownFileType: "folder"
|
||||
)
|
||||
|
||||
if !createIntermediateGroups || path.parent() == project.basePath {
|
||||
if !(createIntermediateGroups || hasCustomParent) || path.parent() == project.basePath {
|
||||
rootGroups.insert(fileReference)
|
||||
}
|
||||
|
||||
@@ -566,7 +584,13 @@ class SourceGenerator {
|
||||
sourceReference = fileReference
|
||||
rootGroups.insert(fileReference)
|
||||
} else {
|
||||
let parentGroup = getGroup(path: parentPath, mergingChildren: [fileReference], createIntermediateGroups: createIntermediateGroups, isBaseGroup: true)
|
||||
let parentGroup = getGroup(
|
||||
path: parentPath,
|
||||
mergingChildren: [fileReference],
|
||||
createIntermediateGroups: createIntermediateGroups,
|
||||
hasCustomParent: hasCustomParent,
|
||||
isBaseGroup: true
|
||||
)
|
||||
sourcePath = parentPath
|
||||
sourceReference = parentGroup
|
||||
}
|
||||
@@ -577,12 +601,18 @@ class SourceGenerator {
|
||||
// This group is missing, so if's optional just return an empty array
|
||||
return []
|
||||
}
|
||||
let (groupSourceFiles, groups) = try getGroupSources(targetType: targetType,
|
||||
targetSource: targetSource,
|
||||
path: path,
|
||||
isBaseGroup: true,
|
||||
excludePaths: excludePaths,
|
||||
includePaths: includePaths)
|
||||
|
||||
let (groupSourceFiles, groups) = try getGroupSources(
|
||||
targetType: targetType,
|
||||
targetSource: targetSource,
|
||||
path: path,
|
||||
isBaseGroup: true,
|
||||
createIntermediateGroups: createIntermediateGroups,
|
||||
hasCustomParent: hasCustomParent,
|
||||
excludePaths: excludePaths,
|
||||
includePaths: includePaths
|
||||
)
|
||||
|
||||
let group = groups.first!
|
||||
if let name = targetSource.name {
|
||||
group.name = name
|
||||
@@ -592,13 +622,40 @@ class SourceGenerator {
|
||||
sourceReference = group
|
||||
}
|
||||
|
||||
if createIntermediateGroups {
|
||||
if hasCustomParent {
|
||||
createParentGroups(customParentGroups, for: sourceReference)
|
||||
} else if createIntermediateGroups {
|
||||
createIntermediaGroups(for: sourceReference, at: sourcePath)
|
||||
}
|
||||
|
||||
return sourceFiles
|
||||
}
|
||||
|
||||
private func createParentGroups(_ parentGroups: [String.SubSequence], for fileElement: PBXFileElement) {
|
||||
guard let parentName = parentGroups.last else {
|
||||
return
|
||||
}
|
||||
|
||||
let parentPath = project.basePath + Path(parentGroups.joined(separator: "/"))
|
||||
|
||||
let hasParentGroup = groupsByPath[parentPath] != nil
|
||||
let parentGroup = getGroup(
|
||||
path: parentPath,
|
||||
mergingChildren: [fileElement],
|
||||
createIntermediateGroups: false,
|
||||
hasCustomParent: false,
|
||||
isBaseGroup: false
|
||||
)
|
||||
|
||||
// As this path is a custom group, remove the path reference
|
||||
parentGroup.name = String(parentName)
|
||||
parentGroup.path = nil
|
||||
|
||||
if !hasParentGroup {
|
||||
createParentGroups(parentGroups.dropLast(), for: parentGroup)
|
||||
}
|
||||
}
|
||||
|
||||
// Add groups for all parents recursively
|
||||
private func createIntermediaGroups(for fileElement: PBXFileElement, at path: Path) {
|
||||
|
||||
@@ -609,7 +666,13 @@ class SourceGenerator {
|
||||
}
|
||||
|
||||
let hasParentGroup = groupsByPath[parentPath] != nil
|
||||
let parentGroup = getGroup(path: parentPath, mergingChildren: [fileElement], createIntermediateGroups: true, isBaseGroup: false)
|
||||
let parentGroup = getGroup(
|
||||
path: parentPath,
|
||||
mergingChildren: [fileElement],
|
||||
createIntermediateGroups: true,
|
||||
hasCustomParent: false,
|
||||
isBaseGroup: false
|
||||
)
|
||||
|
||||
if !hasParentGroup {
|
||||
createIntermediaGroups(for: parentGroup, at: parentPath)
|
||||
|
||||
@@ -469,6 +469,38 @@ class SourceGeneratorTests: XCTestCase {
|
||||
try pbxProj.expectFile(paths: ["../OtherDirectory/C/D", "e.swift"], names: ["D", "e.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["Sources/B", "b.swift"], names: ["B", "b.swift"], buildPhase: .sources)
|
||||
}
|
||||
|
||||
$0.it("generates custom groups") {
|
||||
|
||||
let directories = """
|
||||
Sources:
|
||||
A:
|
||||
- b.swift
|
||||
F:
|
||||
- G:
|
||||
- h.swift
|
||||
B:
|
||||
- b.swift
|
||||
- C:
|
||||
- c.swift
|
||||
"""
|
||||
try createDirectories(directories)
|
||||
|
||||
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
|
||||
TargetSource(path: "Sources/A/b.swift", group: "CustomGroup1"),
|
||||
TargetSource(path: "Sources/F/G/h.swift", group: "CustomGroup1"),
|
||||
TargetSource(path: "Sources/B", group: "CustomGroup2", createIntermediateGroups: false),
|
||||
])
|
||||
|
||||
let options = SpecOptions(createIntermediateGroups: true)
|
||||
let project = Project(basePath: directoryPath, name: "Test", targets: [target], options: options)
|
||||
|
||||
let pbxProj = try project.generatePbxProj()
|
||||
try pbxProj.expectFile(paths: ["CustomGroup1", "Sources/A", "b.swift"], names: ["CustomGroup1", "A", "b.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["CustomGroup1", "Sources/F/G", "h.swift"], names: ["CustomGroup1", "G", "h.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["CustomGroup2", "Sources/B", "b.swift"], names: ["CustomGroup2", "B", "b.swift"], buildPhase: .sources)
|
||||
try pbxProj.expectFile(paths: ["CustomGroup2", "Sources/B", "C", "c.swift"], names: ["CustomGroup2", "B", "C", "c.swift"], buildPhase: .sources)
|
||||
}
|
||||
|
||||
$0.it("generates folder references") {
|
||||
let directories = """
|
||||
@@ -953,18 +985,24 @@ extension PBXProj {
|
||||
}
|
||||
|
||||
private func getFileReference(group: PBXGroup, paths: [String], names: [String]) -> PBXFileReference? {
|
||||
|
||||
guard !paths.isEmpty else { return nil }
|
||||
guard !paths.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let path = paths.first!
|
||||
let name = names.first!
|
||||
let restOfPath = Array(paths.dropFirst())
|
||||
let restOfName = Array(names.dropFirst())
|
||||
if restOfPath.isEmpty {
|
||||
let fileReferences: [PBXFileReference] = group.children.compactMap { $0 as? PBXFileReference }
|
||||
return fileReferences.first { $0.path == path && $0.nameOrPath == name }
|
||||
fileReferences.forEach { print("path: \($0.path ?? "nil"), name: \($0.name ?? "nil")") }
|
||||
return fileReferences.first { ($0.path == nil || $0.path == path) && $0.nameOrPath == name }
|
||||
} else {
|
||||
let groups = group.children.compactMap { $0 as? PBXGroup }
|
||||
guard let group = groups.first(where: { $0.path == path && $0.nameOrPath == name }) else { return nil }
|
||||
groups.forEach { print("path: \($0.path ?? "nil"), name: \($0.name ?? "nil")") }
|
||||
guard let group = groups.first(where: { ($0.path == nil || $0.path == path) && $0.nameOrPath == name }) else {
|
||||
return nil
|
||||
}
|
||||
return getFileReference(group: group, paths: restOfPath, names: restOfName)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user