Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions Sources/XcodeProj/Objects/BuildPhase/PBXCopyFilesBuildPhase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,67 @@ public final class PBXCopyFilesBuildPhase: PBXBuildPhase {
case other
}

public enum DstSubfolder: Equatable, Decodable {
case absolutePath
case productsDirectory
case wrapper
case executables
case resources
case javaResources
case frameworks
case sharedFrameworks
case sharedSupport
case plugins
case other
case product
case none
case unknown(String)

public init(rawValue: String) {
switch rawValue {
case "AbsolutePath": self = .absolutePath
case "ProductsDirectory": self = .productsDirectory
case "Wrapper": self = .wrapper
case "Executables": self = .executables
case "Resources": self = .resources
case "JavaResources": self = .javaResources
case "Frameworks": self = .frameworks
case "SharedFrameworks": self = .sharedFrameworks
case "SharedSupport": self = .sharedSupport
case "PlugIns": self = .plugins
case "Other": self = .other
case "Product": self = .product
case "None": self = .none
default: self = .unknown(rawValue)
}
}

public var rawValue: String {
switch self {
case .absolutePath: "AbsolutePath"
case .productsDirectory: "ProductsDirectory"
case .wrapper: "Wrapper"
case .executables: "Executables"
case .resources: "Resources"
case .javaResources: "JavaResources"
case .frameworks: "Frameworks"
case .sharedFrameworks: "SharedFrameworks"
case .sharedSupport: "SharedSupport"
case .plugins: "PlugIns"
case .other: "Other"
case .product: "Product"
case .none: "None"
case let .unknown(rawValue): rawValue
}
}

public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let rawValue = try container.decode(String.self)
self = .init(rawValue: rawValue)
}
}

// MARK: - Attributes

/// Element destination path
Expand All @@ -24,6 +85,8 @@ public final class PBXCopyFilesBuildPhase: PBXBuildPhase {
/// Element destination subfolder spec
public var dstSubfolderSpec: SubFolder?

public var dstSubfolder: DstSubfolder?

/// Copy files build phase name
public var name: String?

Expand All @@ -38,17 +101,20 @@ public final class PBXCopyFilesBuildPhase: PBXBuildPhase {
/// - Parameters:
/// - dstPath: Destination path.
/// - dstSubfolderSpec: Destination subfolder spec.
/// - dstSubfolder: Destination subfolder.
/// - buildActionMask: Build action mask.
/// - files: Build files to copy.
/// - runOnlyForDeploymentPostprocessing: Run only for deployment post processing.
public init(dstPath: String? = nil,
dstSubfolderSpec: SubFolder? = nil,
dstSubfolder: DstSubfolder? = nil,
name: String? = nil,
buildActionMask: UInt = defaultBuildActionMask,
files: [PBXBuildFile] = [],
runOnlyForDeploymentPostprocessing: Bool = false) {
self.dstPath = dstPath
self.dstSubfolderSpec = dstSubfolderSpec
self.dstSubfolder = dstSubfolder
self.name = name
super.init(files: files,
buildActionMask: buildActionMask,
Expand All @@ -61,13 +127,15 @@ public final class PBXCopyFilesBuildPhase: PBXBuildPhase {
fileprivate enum CodingKeys: String, CodingKey {
case dstPath
case dstSubfolderSpec
case dstSubfolder
case name
}

public required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
dstPath = try container.decodeIfPresent(.dstPath)
dstSubfolderSpec = try container.decodeIntIfPresent(.dstSubfolderSpec).flatMap(SubFolder.init)
dstSubfolder = try container.decodeIfPresent(.dstSubfolder)
name = try container.decodeIfPresent(.name)
try super.init(from: decoder)
}
Expand All @@ -93,6 +161,9 @@ extension PBXCopyFilesBuildPhase: PlistSerializable {
if let dstSubfolderSpec {
dictionary["dstSubfolderSpec"] = .string(CommentedString("\(dstSubfolderSpec.rawValue)"))
}
if let dstSubfolder {
dictionary["dstSubfolder"] = .string(CommentedString("\(dstSubfolder.rawValue)"))
}
return (key: CommentedString(reference, comment: name ?? "CopyFiles"), value: .dictionary(dictionary))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ extension PBXCopyFilesBuildPhase {
func isEqual(to rhs: PBXCopyFilesBuildPhase) -> Bool {
if dstPath != rhs.dstPath { return false }
if dstSubfolderSpec != rhs.dstSubfolderSpec { return false }
if dstSubfolder != rhs.dstSubfolder { return false }
if name != rhs.name { return false }
return super.isEqual(to: rhs)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,29 @@ final class PBXCopyFilesBuildPhaseTests: XCTestCase {
} catch {}
}

func test_init_decodesDstSubfolder() {
var dictionary = testDictionary()
dictionary["dstSubfolder"] = "Frameworks"
let data = try! JSONSerialization.data(withJSONObject: dictionary, options: [])
let decoder = XcodeprojJSONDecoder()
do {
let phase = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data)
XCTAssertEqual(phase.dstSubfolder, .frameworks)
} catch {}
}

func test_init_decodesUnknownDstSubfolder() {
var dictionary = testDictionary()
dictionary["dstSubfolder"] = "InvalidSubfolder"
let data = try! JSONSerialization.data(withJSONObject: dictionary, options: [])
let decoder = XcodeprojJSONDecoder()
do {
let phase = try decoder.decode(PBXCopyFilesBuildPhase.self, from: data)
XCTAssertEqual(phase.dstSubfolder, .unknown("InvalidSubfolder"))
XCTAssertEqual(phase.dstSubfolder?.rawValue, "InvalidSubfolder")
} catch {}
}

func test_init_fails_whenFilesIsMissing() {
var dictionary = testDictionary()
dictionary.removeValue(forKey: "files")
Expand All @@ -102,6 +125,26 @@ final class PBXCopyFilesBuildPhaseTests: XCTestCase {
XCTAssertEqual(PBXCopyFilesBuildPhase.isa, "PBXCopyFilesBuildPhase")
}

func test_equal_whenDstSubfolderIsDifferent_returnsFalse() {
let lhs = PBXCopyFilesBuildPhase(dstPath: "dstPath",
dstSubfolderSpec: .frameworks,
dstSubfolder: .frameworks,
name: "Copy")
let rhs = PBXCopyFilesBuildPhase(dstPath: "dstPath",
dstSubfolderSpec: .frameworks,
dstSubfolder: .resources,
name: "Copy")
XCTAssertNotEqual(lhs, rhs)
}

func test_write_preservesUnknownDstSubfolderRawValue() throws {
let subject = PBXCopyFilesBuildPhase(dstSubfolder: .unknown("InvalidSubfolder"))
let proj = PBXProj.fixture()
let (_, plistValue) = try subject.plistKeyAndValue(proj: proj, reference: "ref")

XCTAssertEqual(plistValue.dictionary?["dstSubfolder"]?.string, "InvalidSubfolder")
}

func testDictionary() -> [String: Any] {
[
"dstPath": "dstPath",
Expand Down
Loading