Skip to content

Commit

Permalink
Add support for simple files excludes from sources #5. (#135)
Browse files Browse the repository at this point in the history
* Add support for simple files excludes from sources #5.

* Add pattern matching for Source excludes #5.

* Simplify getting Source.excludes from jsonDictionary.

* Remove recursive pattern matching and formatting.
  • Loading branch information
PeymanKh authored and yonaskolb committed Nov 10, 2017
1 parent 94a449c commit 91729a9
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 9 deletions.
15 changes: 12 additions & 3 deletions Sources/ProjectSpec/Source.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ public struct Source {

public var path: String
public var compilerFlags: [String]
public var excludes: [String]

public init(path: String, compilerFlags: [String] = []) {
public init(path: String, compilerFlags: [String] = [], excludes: [String] = []) {
self.path = path
self.compilerFlags = compilerFlags
self.excludes = excludes
}
}

Expand All @@ -39,22 +41,29 @@ extension Source: JSONObjectConvertible {

public init(jsonDictionary: JSONDictionary) throws {
path = try jsonDictionary.json(atKeyPath: "path")

let maybeCompilerFlagsString: String? = jsonDictionary.json(atKeyPath: "compilerFlags")
let maybeCompilerFlagsArray: [String]? = jsonDictionary.json(atKeyPath: "compilerFlags")
compilerFlags = maybeCompilerFlagsArray ??
maybeCompilerFlagsString.map{ $0.split(separator: " ").map{ String($0) } } ?? []

excludes = jsonDictionary.json(atKeyPath: "excludes") ?? []
}
}

extension Source: Equatable {

public static func == (lhs: Source, rhs: Source) -> Bool {
return lhs.path == rhs.path && lhs.compilerFlags == rhs.compilerFlags
return lhs.path == rhs.path
&& lhs.compilerFlags == rhs.compilerFlags
&& lhs.excludes == rhs.excludes
}
}

extension Source: Hashable {
public var hashValue: Int {
return path.hashValue ^ compilerFlags.joined(separator: ":").hashValue
return path.hashValue
^ compilerFlags.joined(separator: ":").hashValue
^ excludes.joined(separator: ":").hashValue
}
}
67 changes: 63 additions & 4 deletions Sources/XcodeGenKit/PBXProjGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -563,21 +563,23 @@ public class PBXProjGenerator {
}

func getSources(sourceMetadata source: Source, path: Path, depth: Int = 0) throws -> (sourceFiles: [SourceFile], groups: [PBXGroup]) {

// if we have a file, move it to children and use the parent as the path
// pretend xcassets are files
let (children, path) = path.isFile || path.extension == "xcassets" ?
([path], path.parent()) :
(try path.children().sorted(), path)
(try getSourceChildren(sourceMetadata: source, dirPath: path).sorted(), path)

let excludedFiles: [String] = [".DS_Store"]
guard children.count > 0 else {
return ([], [])
}

let directories = children
.filter { $0.isDirectory && $0.extension == nil && $0.extension != "lproj" }
.sorted { $0.lastComponent < $1.lastComponent }

let filePaths = children
.filter { $0.isFile || $0.extension != nil && $0.extension != "lproj" }
.filter { !excludedFiles.contains($0.lastComponent) }
.sorted { $0.lastComponent < $1.lastComponent }

let localisedDirectories = children
Expand All @@ -592,8 +594,18 @@ public class PBXProjGenerator {

for path in directories {
let subGroups = try getSources(sourceMetadata: source, path: path, depth: depth + 1)

guard !subGroups.sourceFiles.isEmpty else {
continue
}

allSourceFiles += subGroups.sourceFiles
groupChildren.append(subGroups.groups.first!.reference)

guard let first = subGroups.groups.first else {
continue
}

groupChildren.append(first.reference)
groups += subGroups.groups
}

Expand Down Expand Up @@ -649,4 +661,51 @@ public class PBXProjGenerator {
groups.insert(group, at: 0)
return (allSourceFiles, groups)
}

func getSourceChildren(sourceMetadata source: Source, dirPath: Path) throws -> [Path] {

func getSourceExcludes(sourceMetadata source: Source, dirPath: Path) -> [Path] {
return source.excludes.map {
return Path.glob("\(dirPath)/\($0)")
.map {
guard $0.isDirectory else {
return [$0]
}

return (try? $0.recursiveChildren().filter { $0.isFile }) ?? []
}
.reduce([], +)
}
.reduce([], +)
}

let defaultExcludedFiles = [".DS_Store"].map { dirPath + Path($0) }

let sourcePath = Path(source.path)

/*
Exclude following if mentioned in Source.excludes.
Any path related to source dirPath
+ Pre-defined Excluded files
*/

let sourceExcludeFilePaths: Set<Path> = Set(getSourceExcludes(sourceMetadata: source, dirPath: sourcePath)
+ defaultExcludedFiles)

return try dirPath.children()
.filter {
if $0.isDirectory {
let pathChildren = try $0.children()
.filter {
return !sourceExcludeFilePaths.contains($0)
}

return !pathChildren.isEmpty
} else if $0.isFile {
return !sourceExcludeFilePaths.contains($0)
} else {
return false
}
}
}
}
62 changes: 62 additions & 0 deletions Tests/XcodeGenKitTests/ProjectGeneratorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,60 @@ func projectGeneratorTests() {
try project.expectFile(paths: ["Sources", "A", "B", "b.swift"], buildPhase: .sources)
}

$0.it("generates source groups with excludes") {
let directories = """
Sources:
A:
- a.swift
- B:
- b.swift
B:
- b.swift
C:
- c.swift
- c.m
- c.h
D:
- d.h
- d.m
E:
- e.jpg
- e.h
- e.m
- F:
- f.swift
G:
H:
- h.swift
"""
try createDirectories(directories)

let excludes = [
"B",
"C/*.h",
"d.m",
"E/F/*.swift",
"G/H/"
]

target.sources = [Source(path: "Sources", excludes: excludes)]
spec.targets = [target]

let project = try getPbxProj(spec)
try project.expectFile(paths: ["Sources", "A", "a.swift"], buildPhase: .sources)
try project.expectFile(paths: ["Sources", "C", "c.swift"], buildPhase: .sources)
try project.expectFile(paths: ["Sources", "C", "c.m"], buildPhase: .sources)
try project.expectFile(paths: ["Sources", "D", "d.h"])
try project.expectFile(paths: ["Sources", "D", "d.m"], buildPhase: .sources)
try project.expectFile(paths: ["Sources", "E", "e.jpg"], buildPhase: .resources)
try project.expectFile(paths: ["Sources", "E", "e.m"], buildPhase: .sources)
try project.expectFile(paths: ["Sources", "E", "e.h"])
try project.expectFileMissing(paths: ["Sources/B", "b.swift"])
try project.expectFileMissing(paths: ["Sources/C", "c.h"])
try project.expectFileMissing(paths: ["Sources/E/F", "f.swift"])
try project.expectFileMissing(paths: ["Sources/G/H", "h.swift"])
}

$0.it("generates file sources") {
let directories = """
Sources:
Expand Down Expand Up @@ -381,6 +435,14 @@ extension PBXProj {
}
}

/// expect a missing file within groups of the paths, using optional different names
func expectFileMissing(paths: [String], names: [String]? = nil) throws {
let names = names ?? paths
if getFileReference(paths: paths, names: names) != nil {
throw failure("Found unexpected file at path \(paths.joined(separator: "/").quoted) and name \(paths.joined(separator: "/").quoted)")
}
}

func getFileReference(paths: [String], names: [String]) -> PBXFileReference? {
guard let project = projects.first else { return nil }
guard let mainGroup = groups.getReference(project.mainGroup) else { return nil }
Expand Down
13 changes: 11 additions & 2 deletions Tests/XcodeGenKitTests/SpecLoadingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,24 @@ func specLoadingTests() {
"source1",
["path": "source2"],
["path": "sourceWithFlags", "compilerFlags": ["-Werror"]],
["path": "sourceWithFlagsStr", "compilerFlags": "-Werror -Wextra"]
["path": "sourceWithFlagsStr", "compilerFlags": "-Werror -Wextra"],
["path": "sourceWithExcludes", "excludes": ["Foo.swift"]],
["path": "sourceWithCompilerFlagsExcludes", "compilerFlags": ["-Werror"], "excludes": ["Foo.swift"]],
]
var targetDictionary2 = validTarget
targetDictionary2["sources"] = "source3"

let target1 = try Target(name: "test", jsonDictionary: targetDictionary1)
let target2 = try Target(name: "test", jsonDictionary: targetDictionary2)

try expect(target1.sources) == [Source(path: "source1"), Source(path: "source2"), Source(path: "sourceWithFlags", compilerFlags: ["-Werror"]), Source(path: "sourceWithFlagsStr", compilerFlags: ["-Werror", "-Wextra"])]
let target1SourcesExpect = [Source(path: "source1"),
Source(path: "source2"),
Source(path: "sourceWithFlags", compilerFlags: ["-Werror"]),
Source(path: "sourceWithFlagsStr", compilerFlags: ["-Werror", "-Wextra"]),
Source(path: "sourceWithExcludes", excludes: ["Foo.swift"]),
Source(path: "sourceWithCompilerFlagsExcludes", compilerFlags: ["-Werror"], excludes: ["Foo.swift"])]

try expect(target1.sources) == target1SourcesExpect
try expect(target2.sources) == [Source(path: "source3")]
}

Expand Down

0 comments on commit 91729a9

Please sign in to comment.