forked from yonaskolb/XcodeGen
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add PBXVariantGroupGenerator, TargetSourceFilterable
- Loading branch information
1 parent
e7f7537
commit 8a33b01
Showing
2 changed files
with
227 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import XcodeProj | ||
import ProjectSpec | ||
import PathKit | ||
import XcodeGenCore | ||
|
||
class PBXVariantGroupInfo { | ||
let targetName: String | ||
let variantGroup: PBXVariantGroup | ||
var path: Path | ||
|
||
init(targetName: String, variantGroup: PBXVariantGroup, path: Path) { | ||
self.targetName = targetName | ||
self.variantGroup = variantGroup | ||
self.path = path | ||
} | ||
} | ||
|
||
class PBXVariantGroupGenerator: TargetSourceFilterable { | ||
let pbxProj: PBXProj | ||
let project: Project | ||
|
||
init(pbxProj: PBXProj, project: Project) { | ||
self.pbxProj = pbxProj | ||
self.project = project | ||
} | ||
|
||
var alwaysStoredBaseExtensions: [String] { | ||
[".xib", ".storyboard", ".intentdefinition"] | ||
} | ||
|
||
func generate() throws -> [PBXVariantGroupInfo] { | ||
var variantGroupInfoList: [PBXVariantGroupInfo] = [] | ||
|
||
try project.targets.forEach { target in | ||
try target.sources.forEach { targetSource in | ||
let excludePaths = getSourceMatches(targetSource: targetSource, | ||
patterns: targetSource.excludes) | ||
let includePaths = getSourceMatches(targetSource: targetSource, | ||
patterns: targetSource.includes) | ||
|
||
let path = project.basePath + targetSource.path | ||
|
||
try generateVarientGroup(targetName: target.name, | ||
targetSource: targetSource, | ||
path: path, | ||
excludePaths: excludePaths, | ||
includePaths: SortedArray(includePaths)) | ||
} | ||
} | ||
|
||
func generateVarientGroup(targetName: String, | ||
targetSource: TargetSource, | ||
path: Path, | ||
excludePaths: Set<Path>, | ||
includePaths: SortedArray<Path>) throws { | ||
guard path.exists && path.isDirectory && !Xcode.isDirectoryFileWrapper(path: path) else { | ||
return | ||
} | ||
|
||
let children = try getSourceChildren(targetSource: targetSource, | ||
dirPath: path, | ||
excludePaths: excludePaths, | ||
includePaths: includePaths) | ||
|
||
try children.forEach { | ||
let excludePaths = getSourceMatches(targetSource: targetSource, | ||
patterns: targetSource.excludes) | ||
let includePaths = getSourceMatches(targetSource: targetSource, | ||
patterns: targetSource.includes) | ||
|
||
try generateVarientGroup(targetName: targetName, | ||
targetSource: targetSource, | ||
path: $0, | ||
excludePaths: excludePaths, | ||
includePaths: SortedArray(includePaths)) | ||
} | ||
|
||
let localizeDirs: [Path] = children | ||
.filter ({ $0.extension == "lproj" }) | ||
|
||
guard localizeDirs.count > 0 else { | ||
return | ||
} | ||
|
||
try localizeDirs.forEach { localizedDir in | ||
try localizedDir.children() | ||
.filter { self.isIncludedPath($0, excludePaths: excludePaths, includePaths: includePaths) } | ||
.sorted() | ||
.forEach { localizedDirChildPath in | ||
let fileReferencePath = try localizedDirChildPath.relativePath(from: path) | ||
let fileRef = PBXFileReference( | ||
sourceTree: .group, | ||
name: localizedDir.lastComponentWithoutExtension, | ||
lastKnownFileType: Xcode.fileType(path: localizedDirChildPath), | ||
path: fileReferencePath.string | ||
) | ||
pbxProj.add(object: fileRef) | ||
|
||
let variantGroupInfo = getVariantGroupInfo(targetName: targetName, localizedChildPath: localizedDirChildPath) | ||
|
||
if localizedDir.lastComponentWithoutExtension == "Base" || project.options.developmentLanguage == localizedDir.lastComponentWithoutExtension { | ||
|
||
variantGroupInfo.path = localizedDirChildPath | ||
variantGroupInfo.variantGroup.name = localizedDirChildPath.lastComponent | ||
} | ||
|
||
variantGroupInfo.variantGroup.children.append(fileRef) | ||
} | ||
} | ||
} | ||
|
||
func getVariantGroupInfo(targetName: String, localizedChildPath: Path) -> PBXVariantGroupInfo { | ||
let pbxVariantGroupInfo = variantGroupInfoList | ||
.filter { $0.targetName == targetName } | ||
.first { | ||
let existsAlwaysStoredBaseFile = alwaysStoredBaseExtensions | ||
.reduce(into: [Bool]()) { $0.append(localizedChildPath.lastComponent.contains($1)) } | ||
.filter { $0 } | ||
.count > 0 | ||
|
||
if existsAlwaysStoredBaseFile { | ||
return $0.path.lastComponentWithoutExtension == localizedChildPath.lastComponentWithoutExtension | ||
} else { | ||
return $0.path.lastComponent == localizedChildPath.lastComponent | ||
} | ||
} | ||
|
||
if let pbxVariantGroupInfo = pbxVariantGroupInfo { | ||
return pbxVariantGroupInfo | ||
} else { | ||
let variantGroup = PBXVariantGroup( | ||
sourceTree: .group, | ||
name: localizedChildPath.lastComponent | ||
) | ||
pbxProj.add(object: variantGroup) | ||
|
||
let pbxVariantGroupInfo = PBXVariantGroupInfo(targetName: targetName, | ||
variantGroup: variantGroup, | ||
path: localizedChildPath) | ||
variantGroupInfoList.append(pbxVariantGroupInfo) | ||
|
||
return pbxVariantGroupInfo | ||
} | ||
} | ||
|
||
return variantGroupInfoList | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import XcodeProj | ||
import ProjectSpec | ||
import PathKit | ||
import XcodeGenCore | ||
|
||
protocol TargetSourceFilterable { | ||
var project: Project { get } | ||
var defaultExcludedFiles: [String] { get } | ||
var defaultExcludedExtensions: [String] { get } | ||
} | ||
|
||
extension TargetSourceFilterable { | ||
|
||
var defaultExcludedFiles: [String] { | ||
[".DS_Store"] | ||
} | ||
|
||
var defaultExcludedExtensions: [String] { | ||
["orig"] | ||
} | ||
|
||
/// Gets all the children paths that aren't excluded | ||
func getSourceChildren(targetSource: TargetSource, dirPath: Path, excludePaths: Set<Path>, includePaths: SortedArray<Path>) throws -> [Path] { | ||
try dirPath.children() | ||
.filter { | ||
if $0.isDirectory { | ||
let children = try $0.children() | ||
|
||
if children.isEmpty { | ||
return project.options.generateEmptyDirectories | ||
} | ||
|
||
return !children | ||
.filter { self.isIncludedPath($0, excludePaths: excludePaths, includePaths: includePaths) } | ||
.isEmpty | ||
} else if $0.isFile { | ||
return self.isIncludedPath($0, excludePaths: excludePaths, includePaths: includePaths) | ||
} else { | ||
return false | ||
} | ||
} | ||
} | ||
|
||
/// Checks whether the path is not in any default or TargetSource excludes | ||
func isIncludedPath(_ path: Path, excludePaths: Set<Path>, includePaths: SortedArray<Path>) -> Bool { | ||
return !defaultExcludedFiles.contains(where: { path.lastComponent == $0 }) | ||
&& !(path.extension.map(defaultExcludedExtensions.contains) ?? false) | ||
&& !excludePaths.contains(path) | ||
// If includes is empty, it's included. If it's not empty, the path either needs to match exactly, or it needs to be a direct parent of an included path. | ||
&& (includePaths.value.isEmpty || _isIncludedPathSorted(path, sortedPaths: includePaths)) | ||
|
||
func _isIncludedPathSorted(_ path: Path, sortedPaths: SortedArray<Path>) -> Bool { | ||
guard let idx = sortedPaths.firstIndex(where: { $0 >= path }) else { return false } | ||
let foundPath = sortedPaths.value[idx] | ||
return foundPath.description.hasPrefix(path.description) | ||
} | ||
} | ||
|
||
func getSourceMatches(targetSource: TargetSource, patterns: [String]) -> Set<Path> { | ||
let rootSourcePath = project.basePath + targetSource.path | ||
|
||
return Set( | ||
patterns.parallelMap { pattern in | ||
guard !pattern.isEmpty else { return [] } | ||
return Glob(pattern: "\(rootSourcePath)/\(pattern)") | ||
.map { Path($0) } | ||
.map { | ||
guard $0.isDirectory else { | ||
return [$0] | ||
} | ||
|
||
return (try? $0.recursiveChildren()) ?? [] | ||
} | ||
.reduce([], +) | ||
} | ||
.reduce([], +) | ||
) | ||
} | ||
} |