Merge branch 'master' into katei/add-bundle-dependency

This commit is contained in:
Yuta Saito 2019-11-20 19:35:38 -08:00
commit 49f29e9156
12 changed files with 276 additions and 52 deletions

View File

@ -25,7 +25,7 @@ jobs:
- name: Build
run: swift build
- name: Test
run: swift test 2>&1 | xcpretty
run: swift test
- name: Gen fixtures
run: scripts/gen-fixtures.sh
- name: Check fixtures

View File

@ -6,12 +6,14 @@
- Add Carthage static framework dependencies support. [#688](https://github.com/yonaskolb/XcodeGen/pull/688) @giginet
- Added `xcodegen dump` command [#710](https://github.com/yonaskolb/XcodeGen/pull/710) @yonaskolb
- Added `--no-env` option to disable environment variables expansion [#704](https://github.com/yonaskolb/XcodeGen/pull/704) @rcari
- Added custom group support for target sources [#621](https://github.com/yonaskolb/XcodeGen/pull/621) @sroebert @rcari
- Added new dependency type, `bundle`. This allows targets to copy bundles from other projects [#616](https://github.com/yonaskolb/XcodeGen/pull/616) @bsmith11
#### Fixed
- Improved variable expansion runtime [#704](https://github.com/yonaskolb/XcodeGen/pull/704) @rcari
- Fixed missing headers for static framework targets [#705](https://github.com/yonaskolb/XcodeGen/pull/705) @wag-miles
- Using more file types from XcodeProj for PBXFileReferences resulting in less project diffs [#715](https://github.com/yonaskolb/XcodeGen/pull/715) @yonaskolb
- Fixed localized `*.intentdefinition` not being added to build source phases [#720](https://github.com/yonaskolb/XcodeGen/pull/720) @giginet
#### Changed
- Deprecated `$old_form` variables in favor of `${new_form}` variables [#704](https://github.com/yonaskolb/XcodeGen/pull/704) @rcari
@ -37,7 +39,7 @@
- Add base localisation by default even if no base localised files were found. Fixes warning in Xcode 11 [#685](https://github.com/yonaskolb/XcodeGen/pull/685) @yonaskolb
- Don't generate CFBundleExecutable in default generated Info.plist for `bundle` target types [#689](https://github.com/yonaskolb/XcodeGen/pull/689) @FranzBusch
- Fixed resolving relative paths with custom project destination [#681](https://github.com/yonaskolb/XcodeGen/pull/681) @giginet
- Fixed resolving relative paths for Info.plist [#683](https://github.com/yonaskolb/XcodeGen/pull/683)
- Fixed resolving relative paths for Info.plist [#683](https://github.com/yonaskolb/XcodeGen/pull/683) @giginet
- Fixed macOS unit test target TEST_HOST [#696](https://github.com/yonaskolb/XcodeGen/pull/696) @mjarvis
#### Internal

View File

@ -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.

View File

@ -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(),

View File

@ -81,8 +81,8 @@ class SourceGenerator {
_ = try getSourceFiles(targetType: .none, targetSource: TargetSource(path: path), path: fullPath)
}
func generateSourceFile(targetType: PBXProductType, targetSource: TargetSource, path: Path, buildPhase: TargetSource.BuildPhase? = nil) -> SourceFile {
let fileReference = fileReferencesByPath[path.string.lowercased()]!
func generateSourceFile(targetType: PBXProductType, targetSource: TargetSource, path: Path, buildPhase: TargetSource.BuildPhase? = nil, fileReference: PBXFileElement? = nil) -> SourceFile {
let fileReference = fileReference ?? fileReferencesByPath[path.string.lowercased()]!
var settings: [String: Any] = [:]
var attributes: [String] = targetSource.attributes
var chosenBuildPhase: TargetSource.BuildPhase?
@ -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] {
@ -280,6 +281,7 @@ class SourceGenerator {
// Check equality by path and sourceTree because XcodeProj.PBXObject.== is very slow.
if !cachedGroupChildren.contains(where: { $0.name == child.name && $0.path == child.path && $0.sourceTree == child.sourceTree }) {
cachedGroupChildren.append(child)
child.parent = cachedGroup
}
}
cachedGroup.children = cachedGroupChildren
@ -293,10 +295,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,11 +390,20 @@ 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,
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)
let createIntermediateGroups = targetSource.createIntermediateGroups ?? project.options.createIntermediateGroups
let directories = children
.filter { $0.isDirectory && $0.extension == nil && $0.extension != "lproj" }
@ -408,12 +420,16 @@ 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,
hasCustomParent: false,
excludePaths: excludePaths,
includePaths: includePaths
)
guard !subGroups.sourceFiles.isEmpty || project.options.generateEmptyDirectories else {
continue
@ -452,12 +468,10 @@ class SourceGenerator {
groupChildren.append(variantGroup)
baseLocalisationVariantGroups.append(variantGroup)
let sourceFile = SourceFile(
path: filePath,
fileReference: variantGroup,
buildFile: PBXBuildFile(file: variantGroup),
buildPhase: .resources
)
let sourceFile = generateSourceFile(targetType: targetType,
targetSource: targetSource,
path: filePath,
fileReference: variantGroup)
allSourceFiles.append(sourceFile)
}
}
@ -491,24 +505,21 @@ class SourceGenerator {
}
} else {
// add SourceFile to group if there is no Base.lproj directory
let sourceFile = SourceFile(
path: filePath,
fileReference: fileReference,
buildFile: PBXBuildFile(file: fileReference),
buildPhase: .resources
)
let sourceFile = generateSourceFile(targetType: targetType,
targetSource: targetSource,
path: filePath,
fileReference: fileReference)
allSourceFiles.append(sourceFile)
groupChildren.append(fileReference)
}
}
}
let createIntermediateGroups = targetSource.createIntermediateGroups ?? project.options.createIntermediateGroups
let group = getGroup(
path: path,
mergingChildren: groupChildren,
createIntermediateGroups: createIntermediateGroups,
hasCustomParent: hasCustomParent,
isBaseGroup: isBaseGroup
)
if createIntermediateGroups {
@ -528,6 +539,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: "/").map{ String($0) }
let hasCustomParent = !customParentGroups.isEmpty
let createIntermediateGroups = targetSource.createIntermediateGroups ?? project.options.createIntermediateGroups
var sourceFiles: [SourceFile] = []
@ -544,7 +559,7 @@ class SourceGenerator {
lastKnownFileType: "folder"
)
if !createIntermediateGroups || path.parent() == project.basePath {
if !(createIntermediateGroups || hasCustomParent) || path.parent() == project.basePath {
rootGroups.insert(fileReference)
}
@ -565,12 +580,21 @@ class SourceGenerator {
let sourceFile = generateSourceFile(targetType: targetType, targetSource: targetSource, path: path)
if parentPath == project.basePath {
if hasCustomParent {
sourcePath = path
sourceReference = fileReference
} else if parentPath == project.basePath {
sourcePath = path
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
}
@ -581,12 +605,17 @@ 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,
hasCustomParent: hasCustomParent,
excludePaths: excludePaths,
includePaths: includePaths
)
let group = groups.first!
if let name = targetSource.name {
group.name = name
@ -596,13 +625,44 @@ class SourceGenerator {
sourceReference = group
}
if createIntermediateGroups {
if hasCustomParent {
createParentGroups(customParentGroups, for: sourceReference)
try makePathRelative(for: sourceReference, at: path)
} else if createIntermediateGroups {
createIntermediaGroups(for: sourceReference, at: sourcePath)
}
return sourceFiles
}
private func createParentGroups(_ parentGroups: [String], for fileElement: PBXFileElement) {
guard let parentName = parentGroups.last else {
return
}
let parentPath = project.basePath + Path(parentGroups.joined(separator: "/"))
let parentPathExists = parentPath.exists
let parentGroupAlreadyExists = groupsByPath[parentPath] != nil
let parentGroup = getGroup(
path: parentPath,
mergingChildren: [fileElement],
createIntermediateGroups: false,
hasCustomParent: false,
isBaseGroup: parentGroups.count == 1
)
// As this path is a custom group, remove the path reference
if !parentPathExists {
parentGroup.name = String(parentName)
parentGroup.path = nil
}
if !parentGroupAlreadyExists {
createParentGroups(parentGroups.dropLast(), for: parentGroup)
}
}
// Add groups for all parents recursively
private func createIntermediaGroups(for fileElement: PBXFileElement, at path: Path) {
@ -613,13 +673,45 @@ 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)
}
}
// Make the fileElement path and name relative to its parents aggregated paths
private func makePathRelative(for fileElement: PBXFileElement, at path: Path) throws {
// This makes the fileElement path relative to its parent and not to the project. Xcode then rebuilds the actual
// path for the file based on the hierarchy this fileElement lives in.
var paths: [String] = []
var element: PBXFileElement = fileElement
while true {
guard let parent = element.parent else { break }
if let path = parent.path {
paths.insert(path, at: 0)
}
element = parent
}
let completePath = project.basePath + Path(paths.joined(separator: "/"))
let relativePath = try path.relativePath(from: completePath)
let relativePathString = relativePath.string
if relativePathString != fileElement.path {
fileElement.path = relativePathString
fileElement.name = relativePath.lastComponent
}
}
private func findCurrentCoreDataModelVersionPath(using versionedModels: [Path]) -> Path? {
// Find and parse the current version model stored in the .xccurrentversion file
guard

View File

@ -23,13 +23,13 @@ extension PBXProj {
var string = group.nameOrPath
for child in group.children {
if let group = child as? PBXGroup {
string += "\n 📁 " + printGroup(group: group).replacingOccurrences(of: "\n ", with: "\n ")
string += "\n 📁 " + printGroup(group: group).replacingOccurrences(of: "\n ", with: "\n ")
} else if let fileReference = child as? PBXFileReference {
string += "\n 📄 " + fileReference.nameOrPath
string += "\n 📄 " + fileReference.nameOrPath
} else if let variantGroup = child as? PBXVariantGroup {
string += "\n 🌎 " + variantGroup.nameOrPath
string += "\n 🌎 " + variantGroup.nameOrPath
} else if let versionGroup = child as? XCVersionGroup {
string += "\n 🔢 " + versionGroup.nameOrPath
string += "\n 🔢 " + versionGroup.nameOrPath
}
}
return string

View File

@ -52,6 +52,7 @@
307B5322FC5A220652BA6FE0 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D296BB7355994040E197A1EE /* Result.framework */; };
3133B36F3898A27A2B1C56FC /* FrameworkFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5F527F2590C14956518174 /* FrameworkFile.swift */; };
3318F40C855184C18197ED30 /* StaticLibrary_ObjC.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D0C79A8C750EC0DE748C463 /* StaticLibrary_ObjC.m */; };
339578307B9266AB3D7722D9 /* File2.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC56891DA7446EAC8C2F27EB /* File2.swift */; };
3535891EC86283BB5064E7E1 /* Headers in Headers */ = {isa = PBXBuildFile; fileRef = 2E1E747C7BC434ADB80CC269 /* Headers */; settings = {ATTRIBUTES = (Public, ); }; };
3788E1382B38DF4ACE3D2BB1 /* MyFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A58A16491CDDF968B0D56DE /* MyFramework.h */; settings = {ATTRIBUTES = (Public, ); }; };
3C5134EE524310ACF7B7CD6E /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CAF6C55B555E3E1352645B6 /* ExtensionDelegate.swift */; };
@ -74,6 +75,7 @@
632774E7F21CCB386A76B2A8 /* MessagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B198242976C3395E31FE000A /* MessagesViewController.swift */; };
63D8E7F00276736EDA62D227 /* Framework2.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3EF21DF245F66BEF5446AAEF /* Framework2.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
666AA5F3F63C8FD7C68A6CC5 /* MyFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A58A16491CDDF968B0D56DE /* MyFramework.h */; settings = {ATTRIBUTES = (Public, ); }; };
666DEC173BC78C7641AB22EC /* File1.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE1343F2238429D4DA9D830B /* File1.swift */; };
66C3C5E3C13325F351A3008F /* module.modulemap in CopyFiles */ = {isa = PBXBuildFile; fileRef = F2950763C4C568CC85021D18 /* module.modulemap */; };
6E8F8303759824631C8D9DA3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9E17D598D98065767A04740F /* Localizable.strings */; };
7148A4172BFA1CC22E6ED5DB /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 753001CDCEAA4C4E1AFF8E87 /* MainInterface.storyboard */; };
@ -443,6 +445,7 @@
/* Begin PBXFileReference section */
020E4DA91C9132845CAFDC5D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
039F208D1138598CE060F140 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
03D6D1E34022DA9524E5B38D /* Mintfile */ = {isa = PBXFileReference; path = Mintfile; sourceTree = "<group>"; };
056A43A09CE7E88D578696D8 /* StaticLibrary_ObjC.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = StaticLibrary_ObjC.a; sourceTree = BUILT_PRODUCTS_DIR; };
068EDF47F0B087F6A4052AC0 /* Empty.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Empty.h; sourceTree = "<group>"; };
0704B6CAFBB53E0EBB08F6B3 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
@ -518,8 +521,8 @@
B76E17CE3574081D5BF45B44 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = "<group>"; };
BA040F1F7D6CA08878323A55 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
BB178D03E75929F3F5B10C56 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = "<group>"; };
BC56891DA7446EAC8C2F27EB /* File2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = File2.swift; path = Group2/File2.swift; sourceTree = "<group>"; };
BECEA4A483ADEB8158F640B3 /* Tool */ = {isa = PBXFileReference; includeInIndex = 0; path = Tool; sourceTree = BUILT_PRODUCTS_DIR; };
C2F3574CCEF023755DDB1A06 /* Mintfile */ = {isa = PBXFileReference; path = Mintfile; sourceTree = "<group>"; };
C53ACB2962FED621389C36A2 /* iMessageStickersExtension.appex */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "wrapper.app-extension"; path = iMessageStickersExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
C7809CE9FE9852C2AA87ACE5 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
C934C1F7A68CCD0AB6B38478 /* NotificationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationController.swift; sourceTree = "<group>"; };
@ -534,17 +537,18 @@
D70BE0C05E5779A077793BE6 /* Model 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Model 2.xcdatamodel"; sourceTree = "<group>"; };
D8A016580A3B8F72B820BFBF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
D8D8907E2CDA1295D0D94F53 /* StaticLibrary_Swift.a */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = archive.ar; path = StaticLibrary_Swift.a; sourceTree = BUILT_PRODUCTS_DIR; };
DAA7880242A9DE61E68026CC /* Folder */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Folder; sourceTree = SOURCE_ROOT; };
E2AF3B572B3B2E815DA1844D /* BundleA.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = BundleA.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
E42335D1200CB7B8B91E962F /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = Base; path = Base.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
E43116070AFEF5D8C3A5A957 /* TestFramework.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TestFramework.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E55F45EACB0F382722D61C8D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
E9672EF8FE1DDC8DE0705129 /* PushNotificationPayload.apns */ = {isa = PBXFileReference; lastKnownFileType = text; path = PushNotificationPayload.apns; sourceTree = "<group>"; };
EE1343F2238429D4DA9D830B /* File1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = File1.swift; path = Group/File1.swift; sourceTree = "<group>"; };
EF92E90B6F1D583382BD85BE /* StaticLibrary_ObjC.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = StaticLibrary_ObjC.a; sourceTree = BUILT_PRODUCTS_DIR; };
F0D48A913C087D049C8EDDD7 /* App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = "<group>"; };
F2950763C4C568CC85021D18 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
F2FA55A558627ED576A4AFD6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
FA86D418796C1A6864414460 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
FC60FF5527FEDF545816FFCF /* Folder */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Folder; sourceTree = SOURCE_ROOT; };
FD05F36F95D6F098A76F220B /* XPC_Service.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XPC_Service.h; sourceTree = "<group>"; };
FD4A16C7B8FEB7F97F3CBE3F /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
FDB2B6A77D39CD5602F2125F /* Contacts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Contacts.framework; path = System/Library/Frameworks/Contacts.framework; sourceTree = SDKROOT; };
@ -702,6 +706,7 @@
795B8D70B674C850B57DD39D /* App_watchOS Extension */,
6DBE0EE90642BB3F6E58AD43 /* Configs */,
3F2E22B7AB20FA42CD205C2A /* CopyFiles */,
FCC084D4F8992BBC49983A38 /* CustomGroup */,
5CBCE0E2A145046265FE99E2 /* FileGroup */,
1A57D1EE1FBC13598F6B5CB0 /* Framework */,
A3DCF90D9B1EF4E27CF54B19 /* iMessageApp */,
@ -714,9 +719,8 @@
8CFD8AD4820FAB9265663F92 /* Tool */,
3FEA12CF227D41EF50E5C2DB /* Vendor */,
80C3A0E524EC1ABCB9149EA2 /* XPC Service */,
FC60FF5527FEDF545816FFCF /* Folder */,
DAA7880242A9DE61E68026CC /* Folder */,
2E1E747C7BC434ADB80CC269 /* Headers */,
C2F3574CCEF023755DDB1A06 /* Mintfile */,
6B1603BA83AA0C7B94E45168 /* ResourceFolder */,
6BBE762F36D94AB6FFBFE834 /* SomeFile */,
79DC4A1E4D2E0D3A215179BC /* Bundles */,
@ -982,6 +986,17 @@
path = App_iOS_Tests;
sourceTree = "<group>";
};
FCC084D4F8992BBC49983A38 /* CustomGroup */ = {
isa = PBXGroup;
children = (
EE1343F2238429D4DA9D830B /* File1.swift */,
BC56891DA7446EAC8C2F27EB /* File2.swift */,
DAA7880242A9DE61E68026CC /* Folder */,
03D6D1E34022DA9524E5B38D /* Mintfile */,
);
name = CustomGroup;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@ -1951,6 +1966,8 @@
buildActionMask = 2147483647;
files = (
09617AB755651FFEB2564CBC /* AppDelegate.swift in Sources */,
666DEC173BC78C7641AB22EC /* File1.swift in Sources */,
339578307B9266AB3D7722D9 /* File2.swift in Sources */,
F788A3FA1CE6489BC257C1C3 /* Model.xcdatamodeld in Sources */,
58C18019E71E372F635A3FB4 /* MoreUnder.swift in Sources */,
5D10822B0E7C33DD6979F656 /* Standalone.swift in Sources */,

View File

@ -72,9 +72,15 @@ targets:
- path: Folder
type: folder
buildPhase: none
group: CustomGroup
- path: Mintfile
type: file
buildPhase: none
group: CustomGroup
- path: Group/File1.swift
group: CustomGroup
- path: Group2/File2.swift
group: CustomGroup
- path: CopyFiles
buildPhase:
copyFiles:

View File

@ -912,7 +912,7 @@ class ProjectGeneratorTests: XCTestCase {
], localPackages: ["../XcodeGen"], options: .init(localPackagesGroup: "MyPackages"))
let pbxProject = try project.generatePbxProj(specValidate: false)
let nativeTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == "dfg" }))
let nativeTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == app.name }))
let projectSpecDependency = try unwrap(nativeTarget.packageProductDependencies.first(where: { $0.productName == "ProjectSpec" }))

View File

@ -470,6 +470,44 @@ class SourceGeneratorTests: XCTestCase {
try pbxProj.expectFile(paths: ["Sources/B", "b.swift"], names: ["B", "b.swift"], buildPhase: .sources)
}
$0.it("generates custom groups") {
let directories = """
- Sources:
- a.swift
- A:
- b.swift
- F:
- G:
- h.swift
- i.swift
- B:
- b.swift
- C:
- c.swift
"""
try createDirectories(directories)
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
TargetSource(path: "Sources/a.swift", group: "CustomGroup1"),
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),
TargetSource(path: "Sources/F/G/i.swift", group: "Sources/F/G/CustomGroup3"),
])
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.swift"], names: ["CustomGroup1", "a.swift"], buildPhase: .sources)
try pbxProj.expectFile(paths: ["CustomGroup1", "Sources/A/b.swift"], names: ["CustomGroup1", "b.swift"], buildPhase: .sources)
try pbxProj.expectFile(paths: ["CustomGroup1", "Sources/F/G/h.swift"], names: ["CustomGroup1", "h.swift"], buildPhase: .sources)
try pbxProj.expectFile(paths: ["Sources", "F", "G", "CustomGroup3", "i.swift"], names: ["Sources", "F", "G", "CustomGroup3", "i.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 = """
Sources:
@ -833,6 +871,64 @@ class SourceGeneratorTests: XCTestCase {
try pbxProj.expectFileMissing(paths: ["Sources", "group2", "file.swift"])
try pbxProj.expectFileMissing(paths: ["Sources", "group", "file.swift"])
}
$0.describe("Localized sources") {
$0.context("With localized sources") {
$0.it("*.intentdefinition should be added to source phase") {
let directories = """
Sources:
Base.lproj:
- Intents.intentdefinition
en.lproj:
- Intents.strings
ja.lproj:
- Intents.strings
"""
try createDirectories(directories)
let directoryPath = Path("TestDirectory")
let target = Target(name: "IntentDefinitions",
type: .application,
platform: .iOS,
sources: [TargetSource(path: "Sources")])
let project = Project(basePath: directoryPath,
name: "IntendDefinitions",
targets: [target])
let pbxProj = try project.generatePbxProj()
let sourceBuildPhase = try unwrap(pbxProj.buildPhases.first { $0.buildPhase == .sources })
try expect(sourceBuildPhase.files?.compactMap { $0.file?.nameOrPath }) == ["Intents.intentdefinition"]
}
}
$0.context("With localized sources with buildPhase") {
$0.it("*.intentdefinition with buildPhase should be added to resource phase") {
let directories = """
Sources:
Base.lproj:
- Intents.intentdefinition
en.lproj:
- Intents.strings
ja.lproj:
- Intents.strings
"""
try createDirectories(directories)
let directoryPath = Path("TestDirectory")
let target = Target(name: "IntentDefinitions",
type: .application,
platform: .iOS,
sources: [TargetSource(path: "Sources", buildPhase: .resources)])
let project = Project(basePath: directoryPath,
name: "IntendDefinitions",
targets: [target])
let pbxProj = try project.generatePbxProj()
let sourceBuildPhase = try unwrap(pbxProj.buildPhases.first { $0.buildPhase == .sources })
let resourcesBuildPhase = try unwrap(pbxProj.buildPhases.first { $0.buildPhase == .resources })
try expect(sourceBuildPhase.files) == []
try expect(resourcesBuildPhase.files?.compactMap { $0.file?.nameOrPath }) == ["Intents.intentdefinition"]
}
}
}
}
}
}
@ -846,6 +942,7 @@ extension PBXProj {
if let names = names, names != paths {
error += " and name \(names.joined(separator: "/").quoted)"
}
error += "\n\(self.printGroups())"
throw failure(error, file: file, line: line)
}
@ -895,18 +992,22 @@ 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 }
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 }
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)
}
}