-
-
Notifications
You must be signed in to change notification settings - Fork 304
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Some Xcode projects have custom build rules to automatically invoke a tool when an input file changes. Add a PBXObject subclass, PBXBuildRule, which describes when and how a custom build tool should be invoked. Update PBXProj to store custom build rules and add decoding/encoding support. In addition to updating the tests, verified xcproj correctly round trips a project with custom build rules (custom pattern/custom tool, custom pattern/built-in tool, built-in pattern/custom tool, built-in pattern/built-in tool).
- Loading branch information
1 parent
fb7c56b
commit dc33c09
Showing
8 changed files
with
214 additions
and
1 deletion.
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
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
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,134 @@ | ||
import Foundation | ||
|
||
/// A PBXBuildRule is used to specify a method for transforming an input file in to an output file(s). | ||
final public class PBXBuildRule: PBXObject, Equatable { | ||
|
||
// MARK: - Attributes | ||
|
||
/// Element compiler spec. | ||
public var compilerSpec: String | ||
|
||
/// Element file patterns. | ||
public var filePatterns: String? | ||
|
||
/// Element file type. | ||
public var fileType: String | ||
|
||
/// Element is editable. | ||
public var isEditable: UInt | ||
|
||
/// Element name. | ||
public var name: String? | ||
|
||
/// Element output files. | ||
public var outputFiles: [String] | ||
|
||
/// Element output files compiler flags. | ||
public var outputFilesCompilerFlags: [String]? | ||
|
||
/// Element script. | ||
public var script: String | ||
|
||
// MARK: - Init | ||
|
||
public init(reference: String, | ||
compilerSpec: String, | ||
filePatterns: String, | ||
fileType: String, | ||
isEditable: UInt = 1, | ||
name: String?, | ||
outputFiles: [String], | ||
outputFilesCompilerFlags: [String]?, | ||
script: String) { | ||
self.compilerSpec = compilerSpec | ||
self.filePatterns = filePatterns | ||
self.fileType = fileType | ||
self.isEditable = isEditable | ||
self.name = name | ||
self.outputFiles = outputFiles | ||
self.outputFilesCompilerFlags = outputFilesCompilerFlags | ||
self.script = script | ||
super.init(reference: reference) | ||
} | ||
|
||
// MARK: - Decodable | ||
|
||
enum CodingKeys: String, CodingKey { | ||
case compilerSpec | ||
case filePatterns | ||
case fileType | ||
case isEditable | ||
case name | ||
case outputFiles | ||
case outputFilesCompilerFlags | ||
case script | ||
} | ||
|
||
public required init(from decoder: Decoder) throws { | ||
let container = try decoder.container(keyedBy: CodingKeys.self) | ||
self.compilerSpec = try container.decodeIfPresent(.compilerSpec) ?? "" | ||
self.filePatterns = try container.decodeIfPresent(.filePatterns) | ||
self.fileType = try container.decodeIfPresent(.fileType) ?? "" | ||
let isEditable: String? = try container.decodeIfPresent(.isEditable) | ||
self.isEditable = isEditable.flatMap(UInt.init) ?? 0 | ||
self.name = try container.decodeIfPresent(.name) | ||
self.outputFiles = try container.decodeIfPresent(.outputFiles) ?? [] | ||
self.outputFilesCompilerFlags = try container.decodeIfPresent(.outputFilesCompilerFlags) | ||
self.script = try container.decodeIfPresent(.script) ?? "" | ||
try super.init(from: decoder) | ||
} | ||
|
||
// MARK: - Equatable | ||
|
||
public static func == (lhs: PBXBuildRule, | ||
rhs: PBXBuildRule) -> Bool { | ||
let outputFilesCompilerFlagsAreEqual: Bool = { | ||
switch (lhs.outputFilesCompilerFlags, rhs.outputFilesCompilerFlags) { | ||
case (.none, .none): | ||
return true | ||
case (.none, .some), (.some, .none): | ||
return false | ||
case (.some(let lhsOutputFilesCompilerFlags), .some(let rhsOutputFilesCompilerFlags)): | ||
return lhsOutputFilesCompilerFlags == rhsOutputFilesCompilerFlags | ||
} | ||
}() | ||
return lhs.reference == rhs.reference && | ||
lhs.compilerSpec == rhs.compilerSpec && | ||
lhs.filePatterns == rhs.filePatterns && | ||
lhs.fileType == rhs.fileType && | ||
lhs.isEditable == rhs.isEditable && | ||
lhs.name == rhs.name && | ||
lhs.outputFiles == rhs.outputFiles && | ||
outputFilesCompilerFlagsAreEqual && | ||
lhs.script == rhs.script | ||
} | ||
} | ||
|
||
// MARK: - PBXBuildRule Extension (PlistSerializable) | ||
|
||
extension PBXBuildRule: PlistSerializable { | ||
|
||
var multiline: Bool { return true } | ||
|
||
func plistKeyAndValue(proj: PBXProj) -> (key: CommentedString, value: PlistValue) { | ||
var dictionary: [CommentedString: PlistValue] = [:] | ||
dictionary["isa"] = .string(CommentedString(PBXBuildRule.isa)) | ||
dictionary["compilerSpec"] = .string(CommentedString(compilerSpec)) | ||
if let filePatterns = filePatterns { | ||
dictionary["filePatterns"] = .string(CommentedString(filePatterns)) | ||
} | ||
dictionary["fileType"] = .string(CommentedString(fileType)) | ||
dictionary["isEditable"] = .string(CommentedString("\(isEditable)")) | ||
if let name = name { | ||
dictionary["name"] = .string(CommentedString(name)) | ||
} | ||
dictionary["outputFiles"] = .array(outputFiles.map { PlistValue.string(CommentedString($0)) }) | ||
if let outputFilesCompilerFlags = outputFilesCompilerFlags { | ||
dictionary["outputFilesCompilerFlags"] = .array(outputFilesCompilerFlags.map { PlistValue.string(CommentedString($0)) }) | ||
} | ||
dictionary["script"] = .string(CommentedString(script)) | ||
return (key: CommentedString(self.reference, comment: PBXBuildRule.isa), | ||
value: .dictionary(dictionary)) | ||
} | ||
|
||
} |
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
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
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
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,54 @@ | ||
import Foundation | ||
import XCTest | ||
import xcproj | ||
|
||
final class PBXBuildRuleSpec: XCTestCase { | ||
|
||
var subject: PBXBuildRule! | ||
|
||
override func setUp() { | ||
super.setUp() | ||
subject = PBXBuildRule(reference: "ref", | ||
compilerSpec: "spec", | ||
filePatterns: "pattern", | ||
fileType: "type", | ||
isEditable: 1, | ||
name: "rule", | ||
outputFiles:["a", "b"], | ||
outputFilesCompilerFlags: ["-1", "-2"], | ||
script: "script") | ||
} | ||
|
||
func test_init_initializesTheBuildRuleWithTheRightAttributes() { | ||
XCTAssertEqual(subject.reference, "ref") | ||
XCTAssertEqual(subject.compilerSpec, "spec") | ||
XCTAssertEqual(subject.filePatterns, "pattern") | ||
XCTAssertEqual(subject.fileType, "type") | ||
XCTAssertEqual(subject.isEditable, 1) | ||
XCTAssertEqual(subject.name, "rule") | ||
XCTAssertEqual(subject.outputFiles, ["a", "b"]) | ||
XCTAssertEqual(subject.outputFilesCompilerFlags ?? [], ["-1", "-2"]) | ||
XCTAssertEqual(subject.script, "script") | ||
} | ||
|
||
func test_isa_returnsTheCorrectValue() { | ||
XCTAssertEqual(PBXBuildRule.isa, "PBXBuildRule") | ||
} | ||
|
||
func test_hashValue_returnsTheReferenceHashValue() { | ||
XCTAssertEqual(subject.hashValue, subject.reference.hashValue) | ||
} | ||
|
||
func test_equal_shouldReturnTheCorrectValue() { | ||
let another = PBXBuildRule(reference: "ref", | ||
compilerSpec: "spec", | ||
filePatterns: "pattern", | ||
fileType: "type", | ||
isEditable: 1, | ||
name: "rule", | ||
outputFiles:["a", "b"], | ||
outputFilesCompilerFlags: ["-1", "-2"], | ||
script: "script") | ||
XCTAssertEqual(subject, another) | ||
} | ||
} |
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