From ac46689961c93347a8ed72f0b094e9f630ad84ce Mon Sep 17 00:00:00 2001 From: Pedro Date: Sat, 20 Jul 2024 11:07:28 +0200 Subject: [PATCH 01/12] Add PBXFileSystemSynchronizedRootGroup and property to PBXTarget --- .../PBXFileSystemSynchronizedRootGroup.swift | 8 +++++ .../XcodeProj/Objects/Targets/PBXTarget.swift | 29 +++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift new file mode 100644 index 000000000..a07dd934c --- /dev/null +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift @@ -0,0 +1,8 @@ +import Foundation +import PathKit + +public class PBXFileSystemSynchronizedRootGroup: PBXFileElement {} + +/** 6CEC6EEA2C4BAE2D005DD0F8 /* Test */ = {isa = + PBXFileSystemSynchronizedRootGroup; + explicitFileTypes = {}; explicitFolders = (); path = Test; sourceTree = ""; }; */ diff --git a/Sources/XcodeProj/Objects/Targets/PBXTarget.swift b/Sources/XcodeProj/Objects/Targets/PBXTarget.swift index 86646cfd8..16c9c9f94 100644 --- a/Sources/XcodeProj/Objects/Targets/PBXTarget.swift +++ b/Sources/XcodeProj/Objects/Targets/PBXTarget.swift @@ -88,6 +88,19 @@ public class PBXTarget: PBXContainerItem { } } + // File system synchronized groups references. + var fileSystemSynchronizedGroupsReferences: [PBXObjectReference]? + + // File system synchronized groups. + public var fileSystemSynchronizedGroups: [PBXFileSystemSynchronizedRootGroup]? { + set { + fileSystemSynchronizedGroupsReferences = newValue?.references() + } + get { + fileSystemSynchronizedGroupsReferences?.objects() + } + } + /// Target product type. public var productType: PBXProductType? @@ -110,12 +123,15 @@ public class PBXTarget: PBXContainerItem { packageProductDependencies: [XCSwiftPackageProductDependency] = [], productName: String? = nil, product: PBXFileReference? = nil, - productType: PBXProductType? = nil) { + productType: PBXProductType? = nil, + fileSystemSynchronizedGroups: [PBXFileSystemSynchronizedRootGroup]? = nil) + { buildConfigurationListReference = buildConfigurationList?.reference buildPhaseReferences = buildPhases.references() buildRuleReferences = buildRules.references() dependencyReferences = dependencies.references() packageProductDependencyReferences = packageProductDependencies.references() + fileSystemSynchronizedGroupsReferences = fileSystemSynchronizedGroups?.references() self.name = name self.productName = productName productReference = product?.reference @@ -135,6 +151,7 @@ public class PBXTarget: PBXContainerItem { case productReference case productType case packageProductDependencies + case fileSystemSynchronizedGroups } public required init(from decoder: Decoder) throws { @@ -160,11 +177,12 @@ public class PBXTarget: PBXContainerItem { } else { productReference = nil } - let packageProductDependencyReferenceStrings: [String] = try container.decodeIfPresent(.packageProductDependencies) ?? [] packageProductDependencyReferences = packageProductDependencyReferenceStrings.map { objectReferenceRepository.getOrCreate(reference: $0, objects: objects) } + let fileSystemSynchronizedGroupsReferences: [String] = try container.decodeIfPresent(.fileSystemSynchronizedGroups) ?? [] + self.fileSystemSynchronizedGroupsReferences = fileSystemSynchronizedGroupsReferences.map { objectReferenceRepository.getOrCreate(reference: $0, objects: objects) } productType = try container.decodeIfPresent(.productType) try super.init(from: decoder) @@ -190,6 +208,13 @@ public class PBXTarget: PBXContainerItem { } dictionary["dependencies"] = .array(dependencyReferences.map { .string(CommentedString($0.value, comment: PBXTargetDependency.isa)) }) + if let fileSystemSynchronizedGroupsReferences { + dictionary["fileSystemSynchronizedGroups"] = .array(fileSystemSynchronizedGroupsReferences.map { fileSystemSynchronizedGroupReference in + let fileSystemSynchronizedGroup: PBXFileSystemSynchronizedRootGroup? = fileSystemSynchronizedGroupReference.getObject() + return .string(CommentedString(fileSystemSynchronizedGroupReference.value, comment: fileSystemSynchronizedGroup?.name)) + }) + } + dictionary["name"] = .string(CommentedString(name)) if let productName { dictionary["productName"] = .string(CommentedString(productName)) From 8fe5e51f995a15d7679c796f0686e04735a1b77b Mon Sep 17 00:00:00 2001 From: Pedro Date: Sat, 20 Jul 2024 11:57:32 +0200 Subject: [PATCH 02/12] Implement the new models --- .mise.toml | 2 + ...temSynchronizedBuildFileExceptionSet.swift | 55 +++++++++ .../PBXFileSystemSynchronizedRootGroup.swift | 106 +++++++++++++++++- .../Objects/Sourcery/Equality.generated.swift | 20 ++++ 4 files changed, 179 insertions(+), 4 deletions(-) create mode 100644 Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift diff --git a/.mise.toml b/.mise.toml index 317af7dd7..cf6bcf300 100644 --- a/.mise.toml +++ b/.mise.toml @@ -1,3 +1,5 @@ [tools] swiftlint = "0.54.0" swiftformat = "0.54.3" +tuist = "4.21.2" + diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift new file mode 100644 index 000000000..7b62cc585 --- /dev/null +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift @@ -0,0 +1,55 @@ +import Foundation + + +/// Class representing an element that may contain other elements. +public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXObject { + + // MARK: - Attributes + + /// A list of relative paths to children subfolders for which exceptions are applied. + public var membershipExceptions: [String]? + + var targetReference: PBXObjectReference + + public var target: PBXTarget! { + get { + targetReference.getObject() as? PBXTarget + } + set { + targetReference = newValue.reference + } + } + + // MARK: - Init + + public init(target: PBXTarget, membershipExceptions: [String]) { + self.targetReference = target.reference + self.membershipExceptions = membershipExceptions + super.init() + } + + // MARK: - Decodable + + fileprivate enum CodingKeys: String, CodingKey { + case target + case membershipExceptions + } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let referenceRepository = decoder.context.objectReferenceRepository + let objects = decoder.context.objects + let targetReference: String = try container.decode(.target) + self.targetReference = referenceRepository.getOrCreate(reference: targetReference, objects: objects) + self.membershipExceptions = try container.decodeIfPresent(.membershipExceptions) + try super.init(from: decoder) + } + + // MARK: - Equatable + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXFrameworksBuildPhase else { return false } + return isEqual(to: rhs) + } + +} diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift index a07dd934c..0d8196837 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift @@ -1,8 +1,106 @@ import Foundation import PathKit -public class PBXFileSystemSynchronizedRootGroup: PBXFileElement {} +public class PBXFileSystemSynchronizedRootGroup: PBXFileElement { + + /// It maps relative paths inside the synchronized root group to a particular file type. + /// If a path doesn't have a particular file type specified, Xcode defaults to the default file type + /// based on the extension of the file. + public var explicitFileTypes: [String: String] + + /// Returns the references of the exceptions. + var exceptionsReferences: [PBXObjectReference] -/** 6CEC6EEA2C4BAE2D005DD0F8 /* Test */ = {isa = - PBXFileSystemSynchronizedRootGroup; - explicitFileTypes = {}; explicitFolders = (); path = Test; sourceTree = ""; }; */ + /// It returns a list of exception objects that override the configuration for some children + /// in the synchronized root group. + public var exceptions: [PBXFileSystemSynchronizedBuildFileExceptionSet] { + set { + exceptionsReferences = newValue.references() + } + get { + exceptionsReferences.objects() + } + } + + /// A list of relative paths to children folder whose configuration is overriden. + public var explicitFolders: [String] + + /// Initializes the file element with its properties. + /// + /// - Parameters: + /// - sourceTree: file source tree. + /// - path: object relative path from `sourceTree`, if different than `name`. + /// - name: object name. + /// - includeInIndex: should the IDE index the object? + /// - usesTabs: object uses tabs. + /// - indentWidth: the number of positions to indent blocks of code + /// - tabWidth: the visual width of tab characters + /// - wrapsLines: should the IDE wrap lines when editing the object? + /// - explicitFileTypes: It maps relative paths inside the synchronized root group to a particular file type. + /// - exceptions: It returns a list of exception objects that override the configuration for some children in the synchronized root group. + /// - explicitFolders: A list of relative paths to children folder whose configuration is overriden. + public init(sourceTree: PBXSourceTree? = nil, + path: String? = nil, + name: String? = nil, + includeInIndex: Bool? = nil, + usesTabs: Bool? = nil, + indentWidth: UInt? = nil, + tabWidth: UInt? = nil, + wrapsLines: Bool? = nil, + explicitFileTypes: [String: String] = [:], + exceptions: [PBXFileSystemSynchronizedBuildFileExceptionSet] = [], + explicitFolders: [String] = []) { + self.explicitFileTypes = explicitFileTypes + self.exceptionsReferences = exceptions.references() + self.explicitFolders = explicitFolders + super.init(sourceTree: sourceTree, + path: path, + name: name, + includeInIndex: includeInIndex, + usesTabs: usesTabs, + indentWidth: indentWidth, + tabWidth: tabWidth, + wrapsLines: wrapsLines) + } + + // MARK: - Decodable + + fileprivate enum CodingKeys: String, CodingKey { + case explicitFileTypes + case exceptions + case explicitFolders + } + + public required init(from decoder: Decoder) throws { + let objects = decoder.context.objects + let objectReferenceRepository = decoder.context.objectReferenceRepository + let container = try decoder.container(keyedBy: CodingKeys.self) + self.explicitFileTypes = (try container.decodeIfPresent(.explicitFileTypes)) ?? [:] + let exceptionsReferences: [String] = (try container.decodeIfPresent(.exceptions)) ?? [] + self.exceptionsReferences = exceptionsReferences.map { objectReferenceRepository.getOrCreate(reference: $0, objects: objects) } + self.explicitFolders = (try container.decodeIfPresent(.explicitFolders)) ?? [] + try super.init(from: decoder) + } + + // MARK: - PlistSerializable + + override func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { + var dictionary: [CommentedString: PlistValue] = try super.plistKeyAndValue(proj: proj, reference: reference).value.dictionary ?? [:] + dictionary["isa"] = .string(CommentedString(type(of: self).isa)) + dictionary["exceptions"] = .array(self.exceptionsReferences.map({ exceptionReference in + return .string(CommentedString(exceptionReference.value, comment: "PBXFileSystemSynchronizedBuildFileExceptionSet")) + })) + dictionary["explicitFileTypes"] = .dictionary(Dictionary(uniqueKeysWithValues: self.explicitFileTypes.map({ (relativePath, fileType) in + return (CommentedString(relativePath), .string(CommentedString(fileType))) + }))) + dictionary["explicitFolders"] = .array(explicitFolders.map({ .string(CommentedString($0)) })) + return (key: CommentedString(reference, + comment: name ?? path), + value: .dictionary(dictionary)) + } + + override func isEqual(to object: Any?) -> Bool { + guard let rhs = object as? PBXFrameworksBuildPhase else { return false } + return isEqual(to: rhs) + } +} diff --git a/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift b/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift index c20d93c20..7ea7358fd 100644 --- a/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift +++ b/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift @@ -301,3 +301,23 @@ extension XCVersionGroup { return super.isEqual(to: rhs) } } + + +extension PBXFileSystemSynchronizedRootGroup { + /// :nodoc: + func isEqual(to rhs: PBXFileSystemSynchronizedRootGroup) -> Bool { + if explicitFileTypes != rhs.explicitFileTypes { return false } + if exceptionsReferences != rhs.exceptionsReferences { return false } + if explicitFolders != rhs.explicitFolders { return false } + return super.isEqual(to: rhs) + } +} + +extension PBXFileSystemSynchronizedBuildFileExceptionSet { + /// :nodoc: + func isEqual(to rhs: PBXFileSystemSynchronizedBuildFileExceptionSet) -> Bool { + if membershipExceptions != rhs.membershipExceptions { return false } + if targetReference != rhs.targetReference { return false } + return super.isEqual(to: rhs) + } +} From f00f493f25523fdb9cbf718aabdb72c5bfc91351 Mon Sep 17 00:00:00 2001 From: Pedro Date: Sat, 20 Jul 2024 12:10:56 +0200 Subject: [PATCH 03/12] Add publicHeaders and privateHeaders --- ...SystemSynchronizedBuildFileExceptionSet.swift | 16 +++++++++++++++- Sources/XcodeProj/Objects/Files/PBXGroup.swift | 10 ++++++++++ .../Objects/Sourcery/Equality.generated.swift | 2 ++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift index 7b62cc585..04923f3e5 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift @@ -9,6 +9,14 @@ public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXObject { /// A list of relative paths to children subfolders for which exceptions are applied. public var membershipExceptions: [String]? + /// Changes the default header visibility (project) to public for the following headers. + /// Every item in the list is the relative path inside the root synchronized group. + public var publicHeaders: [String]? + + /// Changes the default header visibility (project) to private for the following headers. + /// Every item in the list is the relative path inside the root synchronized group. + public var privateHeaders: [String]? + var targetReference: PBXObjectReference public var target: PBXTarget! { @@ -22,9 +30,11 @@ public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXObject { // MARK: - Init - public init(target: PBXTarget, membershipExceptions: [String]) { + public init(target: PBXTarget, membershipExceptions: [String]?, publicHeaders: [String]?, privateHeaders: [String]?) { self.targetReference = target.reference self.membershipExceptions = membershipExceptions + self.publicHeaders = publicHeaders + self.privateHeaders = privateHeaders super.init() } @@ -33,6 +43,8 @@ public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXObject { fileprivate enum CodingKeys: String, CodingKey { case target case membershipExceptions + case publicHeaders + case privateHeaders } public required init(from decoder: Decoder) throws { @@ -42,6 +54,8 @@ public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXObject { let targetReference: String = try container.decode(.target) self.targetReference = referenceRepository.getOrCreate(reference: targetReference, objects: objects) self.membershipExceptions = try container.decodeIfPresent(.membershipExceptions) + self.publicHeaders = try container.decodeIfPresent(.publicHeaders) + self.privateHeaders = try container.decodeIfPresent(.privateHeaders) try super.init(from: decoder) } diff --git a/Sources/XcodeProj/Objects/Files/PBXGroup.swift b/Sources/XcodeProj/Objects/Files/PBXGroup.swift index 05cece653..c3c913d5b 100644 --- a/Sources/XcodeProj/Objects/Files/PBXGroup.swift +++ b/Sources/XcodeProj/Objects/Files/PBXGroup.swift @@ -115,6 +115,16 @@ public extension PBXGroup { .objects() .first(where: { $0.name == name }) } + + /// Returns the synchronized root group with the given name contained in the given parent group. + /// + /// - Parameter groupName: group name. + /// - Returns: the synchronized root group with the given name contained in the given parent group. + func synchronizedRootGroup(named name: String) -> PBXFileSystemSynchronizedRootGroup? { + childrenReferences + .objects() + .first(where: { $0.name == name }) + } /// Returns the file in the group with the given name. /// diff --git a/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift b/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift index 7ea7358fd..1003db40b 100644 --- a/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift +++ b/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift @@ -318,6 +318,8 @@ extension PBXFileSystemSynchronizedBuildFileExceptionSet { func isEqual(to rhs: PBXFileSystemSynchronizedBuildFileExceptionSet) -> Bool { if membershipExceptions != rhs.membershipExceptions { return false } if targetReference != rhs.targetReference { return false } + if publicHeaders != rhs.publicHeaders { return false } + if privateHeaders != rhs.privateHeaders { return false } return super.isEqual(to: rhs) } } From 637fa44c5f3677134fb82fdaedfb0d2d4ffe6461 Mon Sep 17 00:00:00 2001 From: Pedro Date: Sat, 20 Jul 2024 18:58:09 +0200 Subject: [PATCH 04/12] Fix some bugs and add some tests --- ...temSynchronizedBuildFileExceptionSet.swift | 2 +- .../PBXFileSystemSynchronizedRootGroup.swift | 2 +- .../XcodeProj/Objects/Targets/PBXTarget.swift | 7 +-- ...onizedBuildFileExceptionSet+Fixtures.swift | 13 +++++ ...nchronizedBuildFileExceptionSetTests.swift | 29 +++++++++++ ...SystemSynchronizedRootGroup+Fixtures.swift | 27 ++++++++++ ...FileSystemSynchronizedRootGroupTests.swift | 51 +++++++++++++++++++ 7 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet+Fixtures.swift create mode 100644 Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSetTests.swift create mode 100644 Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroup+Fixtures.swift create mode 100644 Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroupTests.swift diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift index 04923f3e5..d2f6336a2 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift @@ -62,7 +62,7 @@ public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXObject { // MARK: - Equatable override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXFrameworksBuildPhase else { return false } + guard let rhs = object as? PBXFileSystemSynchronizedBuildFileExceptionSet else { return false } return isEqual(to: rhs) } diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift index 0d8196837..23da01498 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift @@ -100,7 +100,7 @@ public class PBXFileSystemSynchronizedRootGroup: PBXFileElement { } override func isEqual(to object: Any?) -> Bool { - guard let rhs = object as? PBXFrameworksBuildPhase else { return false } + guard let rhs = object as? PBXFileSystemSynchronizedRootGroup else { return false } return isEqual(to: rhs) } } diff --git a/Sources/XcodeProj/Objects/Targets/PBXTarget.swift b/Sources/XcodeProj/Objects/Targets/PBXTarget.swift index 16c9c9f94..93f102283 100644 --- a/Sources/XcodeProj/Objects/Targets/PBXTarget.swift +++ b/Sources/XcodeProj/Objects/Targets/PBXTarget.swift @@ -181,9 +181,10 @@ public class PBXTarget: PBXContainerItem { packageProductDependencyReferences = packageProductDependencyReferenceStrings.map { objectReferenceRepository.getOrCreate(reference: $0, objects: objects) } - let fileSystemSynchronizedGroupsReferences: [String] = try container.decodeIfPresent(.fileSystemSynchronizedGroups) ?? [] - self.fileSystemSynchronizedGroupsReferences = fileSystemSynchronizedGroupsReferences.map { objectReferenceRepository.getOrCreate(reference: $0, objects: objects) } - + let fileSystemSynchronizedGroupsReferences: [String]? = try container.decodeIfPresent(.fileSystemSynchronizedGroups) + if let fileSystemSynchronizedGroupsReferences = fileSystemSynchronizedGroupsReferences { + self.fileSystemSynchronizedGroupsReferences = fileSystemSynchronizedGroupsReferences.map { objectReferenceRepository.getOrCreate(reference: $0, objects: objects) } + } productType = try container.decodeIfPresent(.productType) try super.init(from: decoder) } diff --git a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet+Fixtures.swift b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet+Fixtures.swift new file mode 100644 index 000000000..d49b85791 --- /dev/null +++ b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet+Fixtures.swift @@ -0,0 +1,13 @@ +import XcodeProj + +extension PBXFileSystemSynchronizedBuildFileExceptionSet { + static func fixture(target: PBXTarget = .fixture(), + membershipExceptions: [String]? = [], + publicHeaders: [String]? = [], + privateHeaders: [String]? = []) -> PBXFileSystemSynchronizedBuildFileExceptionSet { + return PBXFileSystemSynchronizedBuildFileExceptionSet(target: target, + membershipExceptions: membershipExceptions, + publicHeaders: publicHeaders, + privateHeaders: privateHeaders) + } +} diff --git a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSetTests.swift b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSetTests.swift new file mode 100644 index 000000000..799edaee3 --- /dev/null +++ b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSetTests.swift @@ -0,0 +1,29 @@ +import Foundation +import XCTest +@testable import XcodeProj + +final class PBXFileSystemSynchronizedBuildFileExceptionSetTests: XCTestCase { + var target: PBXTarget! + var subject: PBXFileSystemSynchronizedBuildFileExceptionSet! + + override func setUp() { + super.setUp() + target = PBXTarget.fixture() + subject = PBXFileSystemSynchronizedBuildFileExceptionSet.fixture(target: target) + } + + override func tearDown() { + target = nil + subject = nil + super.tearDown() + } + + func test_itHasTheCorrectIsa() { + XCTAssertEqual(PBXFileSystemSynchronizedBuildFileExceptionSet.isa, "PBXFileSystemSynchronizedBuildFileExceptionSet") + } + + func test_equal_returnsTheCorrectValue() { + let another = PBXFileSystemSynchronizedBuildFileExceptionSet.fixture(target: target) + XCTAssertEqual(subject, another) + } +} diff --git a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroup+Fixtures.swift b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroup+Fixtures.swift new file mode 100644 index 000000000..7a14963af --- /dev/null +++ b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroup+Fixtures.swift @@ -0,0 +1,27 @@ +import XcodeProj + +extension PBXFileSystemSynchronizedRootGroup { + static func fixture(sourceTree: PBXSourceTree? = nil, + path: String? = nil, + name: String? = nil, + includeInIndex: Bool? = nil, + usesTabs: Bool? = nil, + indentWidth: UInt? = nil, + tabWidth: UInt? = nil, + wrapsLines: Bool? = nil, + explicitFileTypes: [String: String] = [:], + exceptions: [PBXFileSystemSynchronizedBuildFileExceptionSet] = [], + explicitFolders: [String] = []) -> PBXFileSystemSynchronizedRootGroup { + return PBXFileSystemSynchronizedRootGroup(sourceTree: sourceTree, + path: path, + name: name, + includeInIndex: includeInIndex, + usesTabs: usesTabs, + indentWidth: indentWidth, + tabWidth: tabWidth, + wrapsLines: wrapsLines, + explicitFileTypes: explicitFileTypes, + exceptions: exceptions, + explicitFolders: explicitFolders) + } +} diff --git a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroupTests.swift b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroupTests.swift new file mode 100644 index 000000000..21894e0e0 --- /dev/null +++ b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroupTests.swift @@ -0,0 +1,51 @@ +import Foundation +import XCTest +@testable import XcodeProj + +final class PBXFileSystemSynchronizedRootGroupTests: XCTestCase { + var subject: PBXFileSystemSynchronizedRootGroup! + var target: PBXTarget! + var exception: PBXFileSystemSynchronizedBuildFileExceptionSet! + + override func setUp() { + super.setUp() + target = PBXTarget.fixture() + exception = PBXFileSystemSynchronizedBuildFileExceptionSet.fixture(target: target) + subject = PBXFileSystemSynchronizedRootGroup(sourceTree: .group, + path: "synchronized", + name: "synchronized", + includeInIndex: true, + usesTabs: true, + indentWidth: 2, + tabWidth: 2, + wrapsLines: true, + explicitFileTypes: ["Source.m": "sourcecode.c.objc"], + exceptions: [exception], + explicitFolders: []) + } + + override func tearDown() { + exception = nil + subject = nil + super.tearDown() + } + + func test_itHasTheCorrectIsa() { + XCTAssertEqual(PBXFileSystemSynchronizedRootGroup.isa, "PBXFileSystemSynchronizedRootGroup") + } + + func test_equal_returnsTheCorrectValue() { + let another = PBXFileSystemSynchronizedRootGroup(sourceTree: .group, + path: "synchronized", + name: "synchronized", + includeInIndex: true, + usesTabs: true, + indentWidth: 2, + tabWidth: 2, + wrapsLines: true, + explicitFileTypes: ["Source.m": "sourcecode.c.objc"], + exceptions: [exception], + explicitFolders: []) + XCTAssertEqual(subject, another) + } +} From e4f9029b37aaf2cc76361452d0d654d760ab8d13 Mon Sep 17 00:00:00 2001 From: Pedro Date: Sat, 20 Jul 2024 19:05:23 +0200 Subject: [PATCH 05/12] Fix some linting issues --- .github/workflows/xcodeproj.yml | 10 ++++++++++ .mise.toml | 3 +-- .mise/tasks/lint | 7 +++++++ .mise/tasks/lint-fix | 7 +++++++ ...BXFileSystemSynchronizedBuildFileExceptionSet.swift | 1 - .../Objects/Sourcery/Equality.generated.swift | 1 - Sources/XcodeProj/Objects/Targets/PBXTarget.swift | 7 ++++--- Sources/XcodeProj/Scheme/XCScheme+ProfileAction.swift | 4 ++++ Sources/XcodeProj/Utils/XCConfig.swift | 4 ++++ 9 files changed, 37 insertions(+), 7 deletions(-) diff --git a/.github/workflows/xcodeproj.yml b/.github/workflows/xcodeproj.yml index 7d921922f..b297a1641 100644 --- a/.github/workflows/xcodeproj.yml +++ b/.github/workflows/xcodeproj.yml @@ -8,7 +8,11 @@ concurrency: cancel-in-progress: true env: +<<<<<<< HEAD MISE_EXPERIMENTAL: 1 +======= + MISE_EXPERIMENTAL: "1" +>>>>>>> be7969e7 (Fix some linting issues) jobs: build: @@ -52,9 +56,15 @@ jobs: git config --global init.defaultBranch main - name: Build and run tests run: swift test --enable-test-discovery +<<<<<<< HEAD lint: name: Lint runs-on: macos-latest +======= + swiftlint: + name: Lint + runs-on: ubuntu-latest +>>>>>>> be7969e7 (Fix some linting issues) steps: - uses: actions/checkout@v3 - uses: jdx/mise-action@v2 diff --git a/.mise.toml b/.mise.toml index cf6bcf300..ec954e154 100644 --- a/.mise.toml +++ b/.mise.toml @@ -1,5 +1,4 @@ [tools] -swiftlint = "0.54.0" swiftformat = "0.54.3" tuist = "4.21.2" - +swiftlint = "0.55.1" diff --git a/.mise/tasks/lint b/.mise/tasks/lint index d1a523f1d..66ff8c388 100755 --- a/.mise/tasks/lint +++ b/.mise/tasks/lint @@ -1,6 +1,13 @@ +<<<<<<< HEAD #!/bin/bash # mise description="Lint the workspace" set -euo pipefail swiftformat $MISE_PROJECT_ROOT --lint swiftlint lint --quiet --config $MISE_PROJECT_ROOT/.swiftlint.yml $MISE_PROJECT_ROOT/Sources +======= +#!/usr/bin/env bash +# mise description="Lints the project" + +swiftlint $MISE_PROJECT_ROOT +>>>>>>> be7969e7 (Fix some linting issues) diff --git a/.mise/tasks/lint-fix b/.mise/tasks/lint-fix index cfc035959..df75b136f 100755 --- a/.mise/tasks/lint-fix +++ b/.mise/tasks/lint-fix @@ -1,6 +1,13 @@ +<<<<<<< HEAD #!/bin/bash # mise description="Lint the workspace fixing issues" set -euo pipefail swiftformat $MISE_PROJECT_ROOT swiftlint lint --fix --quiet --config $MISE_PROJECT_ROOT/.swiftlint.yml $MISE_PROJECT_ROOT/Sources +======= +#!/usr/bin/env bash +# mise description="Lints the project" + +swiftlint --fix $MISE_PROJECT_ROOT +>>>>>>> be7969e7 (Fix some linting issues) diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift index d2f6336a2..6d7a12156 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift @@ -1,6 +1,5 @@ import Foundation - /// Class representing an element that may contain other elements. public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXObject { diff --git a/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift b/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift index 1003db40b..4c9a1d98c 100644 --- a/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift +++ b/Sources/XcodeProj/Objects/Sourcery/Equality.generated.swift @@ -302,7 +302,6 @@ extension XCVersionGroup { } } - extension PBXFileSystemSynchronizedRootGroup { /// :nodoc: func isEqual(to rhs: PBXFileSystemSynchronizedRootGroup) -> Bool { diff --git a/Sources/XcodeProj/Objects/Targets/PBXTarget.swift b/Sources/XcodeProj/Objects/Targets/PBXTarget.swift index 93f102283..518b26736 100644 --- a/Sources/XcodeProj/Objects/Targets/PBXTarget.swift +++ b/Sources/XcodeProj/Objects/Targets/PBXTarget.swift @@ -124,8 +124,7 @@ public class PBXTarget: PBXContainerItem { productName: String? = nil, product: PBXFileReference? = nil, productType: PBXProductType? = nil, - fileSystemSynchronizedGroups: [PBXFileSystemSynchronizedRootGroup]? = nil) - { + fileSystemSynchronizedGroups: [PBXFileSystemSynchronizedRootGroup]? = nil) { buildConfigurationListReference = buildConfigurationList?.reference buildPhaseReferences = buildPhases.references() buildRuleReferences = buildRules.references() @@ -183,7 +182,9 @@ public class PBXTarget: PBXContainerItem { } let fileSystemSynchronizedGroupsReferences: [String]? = try container.decodeIfPresent(.fileSystemSynchronizedGroups) if let fileSystemSynchronizedGroupsReferences = fileSystemSynchronizedGroupsReferences { - self.fileSystemSynchronizedGroupsReferences = fileSystemSynchronizedGroupsReferences.map { objectReferenceRepository.getOrCreate(reference: $0, objects: objects) } + self.fileSystemSynchronizedGroupsReferences = fileSystemSynchronizedGroupsReferences.map { + objectReferenceRepository.getOrCreate(reference: $0, objects: objects) + } } productType = try container.decodeIfPresent(.productType) try super.init(from: decoder) diff --git a/Sources/XcodeProj/Scheme/XCScheme+ProfileAction.swift b/Sources/XcodeProj/Scheme/XCScheme+ProfileAction.swift index 6c4864996..b3ccc4cf0 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+ProfileAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+ProfileAction.swift @@ -81,8 +81,12 @@ public extension XCScheme { commandlineArguments: CommandLineArguments? = nil, environmentVariables: [EnvironmentVariable]? = nil, enableTestabilityWhenProfilingTests: Bool = true, +<<<<<<< HEAD launchAutomaticallySubstyle: String? = nil ) { +======= + launchAutomaticallySubstyle: String? = nil) { +>>>>>>> be7969e7 (Fix some linting issues) self.init( runnable: buildableProductRunnable, buildConfiguration: buildConfiguration, diff --git a/Sources/XcodeProj/Utils/XCConfig.swift b/Sources/XcodeProj/Utils/XCConfig.swift index e1e585ea6..cd1846ce5 100644 --- a/Sources/XcodeProj/Utils/XCConfig.swift +++ b/Sources/XcodeProj/Utils/XCConfig.swift @@ -68,7 +68,11 @@ enum XCConfigParser { do { // first try to load the included xcconfig relative to the current xcconfig config = try XCConfig(path: path.parent() + includePath, projectPath: projectPath) +<<<<<<< HEAD } catch XCConfigError.notFound(_) where projectPath != nil { +======= + } catch XCConfigError.notFound(_)where projectPath != nil { +>>>>>>> be7969e7 (Fix some linting issues) // if that fails, try to load the included xcconfig relative to the project config = try? XCConfig(path: projectPath!.parent() + includePath, projectPath: projectPath) } catch { From b297994a601577371f7d63e3adc5ef8759df8c25 Mon Sep 17 00:00:00 2001 From: Pedro Date: Sat, 20 Jul 2024 19:09:47 +0200 Subject: [PATCH 06/12] Fix the pipeline to not run twice on PRs --- .github/workflows/xcodeproj.yml | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/.github/workflows/xcodeproj.yml b/.github/workflows/xcodeproj.yml index b297a1641..5d1e3b5f7 100644 --- a/.github/workflows/xcodeproj.yml +++ b/.github/workflows/xcodeproj.yml @@ -1,18 +1,18 @@ # https://help.github.com/en/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idname name: XcodeProj -on: [push, pull_request] +on: + push: + branches: + - main + pull_request: {} concurrency: group: xcodeproj-${{ github.head_ref }} cancel-in-progress: true env: -<<<<<<< HEAD MISE_EXPERIMENTAL: 1 -======= - MISE_EXPERIMENTAL: "1" ->>>>>>> be7969e7 (Fix some linting issues) jobs: build: @@ -56,15 +56,10 @@ jobs: git config --global init.defaultBranch main - name: Build and run tests run: swift test --enable-test-discovery -<<<<<<< HEAD + lint: name: Lint runs-on: macos-latest -======= - swiftlint: - name: Lint - runs-on: ubuntu-latest ->>>>>>> be7969e7 (Fix some linting issues) steps: - uses: actions/checkout@v3 - uses: jdx/mise-action@v2 From 697d42d445d9ff5b9a42d9473c8625ec2cc0aaa4 Mon Sep 17 00:00:00 2001 From: Pedro Date: Fri, 26 Jul 2024 17:47:50 +0200 Subject: [PATCH 07/12] Add tests --- .gitignore | 3 + .mise/tasks/lint | 7 - .mise/tasks/lint-fix | 7 - .../project.pbxproj | 359 ++++++++++++++++++ .../xcschemes/xcschememanagement.plist | 14 + .../Exception/Exception.swift | 6 + .../SynchronizedRootGroups.swift | 6 + ...temSynchronizedBuildFileExceptionSet.swift | 48 ++- .../PBXFileSystemSynchronizedRootGroup.swift | 37 +- .../XcodeProj/Objects/Files/PBXGroup.swift | 2 +- .../Objects/Project/PBXObjectParser.swift | 4 + .../Objects/Project/PBXObjects.swift | 12 + .../Objects/Project/PBXProjEncoder.swift | 12 + .../XcodeProj/Objects/Targets/PBXTarget.swift | 4 +- .../Scheme/XCScheme+ProfileAction.swift | 4 - Sources/XcodeProj/Utils/XCConfig.swift | 4 - ...onizedBuildFileExceptionSet+Fixtures.swift | 8 +- ...nchronizedBuildFileExceptionSetTests.swift | 8 +- ...SystemSynchronizedRootGroup+Fixtures.swift | 40 +- ...FileSystemSynchronizedRootGroupTests.swift | 8 +- .../Objects/Project/PBXProjEncoderTests.swift | 36 ++ Tests/XcodeProjTests/Tests/Fixtures.swift | 5 + 22 files changed, 543 insertions(+), 91 deletions(-) create mode 100644 Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/project.pbxproj create mode 100644 Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/xcuserdata/pepicrft.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/Exception/Exception.swift create mode 100644 Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/SynchronizedRootGroups.swift diff --git a/.gitignore b/.gitignore index 97f05bc98..a53f04dd9 100644 --- a/.gitignore +++ b/.gitignore @@ -190,3 +190,6 @@ XcodeProj.framework.zip Derived/ *.xcworkspace/ *.xcodeproj/ + +!Fixtures/**/*.xcodeproj/ + diff --git a/.mise/tasks/lint b/.mise/tasks/lint index 66ff8c388..d1a523f1d 100755 --- a/.mise/tasks/lint +++ b/.mise/tasks/lint @@ -1,13 +1,6 @@ -<<<<<<< HEAD #!/bin/bash # mise description="Lint the workspace" set -euo pipefail swiftformat $MISE_PROJECT_ROOT --lint swiftlint lint --quiet --config $MISE_PROJECT_ROOT/.swiftlint.yml $MISE_PROJECT_ROOT/Sources -======= -#!/usr/bin/env bash -# mise description="Lints the project" - -swiftlint $MISE_PROJECT_ROOT ->>>>>>> be7969e7 (Fix some linting issues) diff --git a/.mise/tasks/lint-fix b/.mise/tasks/lint-fix index df75b136f..cfc035959 100755 --- a/.mise/tasks/lint-fix +++ b/.mise/tasks/lint-fix @@ -1,13 +1,6 @@ -<<<<<<< HEAD #!/bin/bash # mise description="Lint the workspace fixing issues" set -euo pipefail swiftformat $MISE_PROJECT_ROOT swiftlint lint --fix --quiet --config $MISE_PROJECT_ROOT/.swiftlint.yml $MISE_PROJECT_ROOT/Sources -======= -#!/usr/bin/env bash -# mise description="Lints the project" - -swiftlint --fix $MISE_PROJECT_ROOT ->>>>>>> be7969e7 (Fix some linting issues) diff --git a/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/project.pbxproj b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/project.pbxproj new file mode 100644 index 000000000..5db92414b --- /dev/null +++ b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/project.pbxproj @@ -0,0 +1,359 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 70; + objects = { + +/* Begin PBXFileReference section */ + 6CF05B8C2C53F5F200EF267F /* SynchronizedRootGroups.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SynchronizedRootGroups.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 6CF05BA32C53F97F00EF267F /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Exception/Exception.swift, + ); + target = 6CF05B8B2C53F5F200EF267F /* SynchronizedRootGroups */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 6CF05B9D2C53F64800EF267F /* SynchronizedRootGroups */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (6CF05BA32C53F97F00EF267F /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = SynchronizedRootGroups; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6CF05B892C53F5F200EF267F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6CF05B822C53F5F200EF267F = { + isa = PBXGroup; + children = ( + 6CF05B9D2C53F64800EF267F /* SynchronizedRootGroups */, + 6CF05B8D2C53F5F200EF267F /* Products */, + ); + sourceTree = ""; + }; + 6CF05B8D2C53F5F200EF267F /* Products */ = { + isa = PBXGroup; + children = ( + 6CF05B8C2C53F5F200EF267F /* SynchronizedRootGroups.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 6CF05B872C53F5F200EF267F /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 6CF05B8B2C53F5F200EF267F /* SynchronizedRootGroups */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6CF05B922C53F5F200EF267F /* Build configuration list for PBXNativeTarget "SynchronizedRootGroups" */; + buildPhases = ( + 6CF05B872C53F5F200EF267F /* Headers */, + 6CF05B882C53F5F200EF267F /* Sources */, + 6CF05B892C53F5F200EF267F /* Frameworks */, + 6CF05B8A2C53F5F200EF267F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 6CF05B9D2C53F64800EF267F /* SynchronizedRootGroups */, + ); + name = SynchronizedRootGroups; + packageProductDependencies = ( + ); + productName = SynchronizedRootGroups; + productReference = 6CF05B8C2C53F5F200EF267F /* SynchronizedRootGroups.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6CF05B832C53F5F200EF267F /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1600; + TargetAttributes = { + 6CF05B8B2C53F5F200EF267F = { + CreatedOnToolsVersion = 16.0; + LastSwiftMigration = 1600; + }; + }; + }; + buildConfigurationList = 6CF05B862C53F5F200EF267F /* Build configuration list for PBXProject "SynchronizedRootGroups" */; + compatibilityVersion = "Xcode 15.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6CF05B822C53F5F200EF267F; + productRefGroup = 6CF05B8D2C53F5F200EF267F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6CF05B8B2C53F5F200EF267F /* SynchronizedRootGroups */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6CF05B8A2C53F5F200EF267F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6CF05B882C53F5F200EF267F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 6CF05B932C53F5F200EF267F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.SynchronizedRootGroups; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 6CF05B942C53F5F200EF267F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = io.tuist.SynchronizedRootGroups; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 6CF05B952C53F5F200EF267F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 6CF05B962C53F5F200EF267F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6CF05B862C53F5F200EF267F /* Build configuration list for PBXProject "SynchronizedRootGroups" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6CF05B952C53F5F200EF267F /* Debug */, + 6CF05B962C53F5F200EF267F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6CF05B922C53F5F200EF267F /* Build configuration list for PBXNativeTarget "SynchronizedRootGroups" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6CF05B932C53F5F200EF267F /* Debug */, + 6CF05B942C53F5F200EF267F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6CF05B832C53F5F200EF267F /* Project object */; +} diff --git a/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/xcuserdata/pepicrft.xcuserdatad/xcschemes/xcschememanagement.plist b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/xcuserdata/pepicrft.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 000000000..e04d3354a --- /dev/null +++ b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/xcuserdata/pepicrft.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + SynchronizedRootGroups.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/Exception/Exception.swift b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/Exception/Exception.swift new file mode 100644 index 000000000..190cf0a74 --- /dev/null +++ b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/Exception/Exception.swift @@ -0,0 +1,6 @@ +// +// Exception.swift +// SynchronizedRootGroups +// +// Created by Pedro Piñera Buendía on 26.07.24. +// diff --git a/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/SynchronizedRootGroups.swift b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/SynchronizedRootGroups.swift new file mode 100644 index 000000000..9286b7807 --- /dev/null +++ b/Fixtures/SynchronizedRootGroups/SynchronizedRootGroups/SynchronizedRootGroups.swift @@ -0,0 +1,6 @@ +// +// SynchronizedRootGroups.swift +// SynchronizedRootGroups +// +// Created by Pedro Piñera Buendía on 26.07.24. +// diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift index 6d7a12156..b883cc8dd 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift @@ -1,23 +1,22 @@ import Foundation /// Class representing an element that may contain other elements. -public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXObject { - +public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXObject, PlistSerializable { // MARK: - Attributes - + /// A list of relative paths to children subfolders for which exceptions are applied. public var membershipExceptions: [String]? - + /// Changes the default header visibility (project) to public for the following headers. /// Every item in the list is the relative path inside the root synchronized group. public var publicHeaders: [String]? - + /// Changes the default header visibility (project) to private for the following headers. /// Every item in the list is the relative path inside the root synchronized group. public var privateHeaders: [String]? - + var targetReference: PBXObjectReference - + public var target: PBXTarget! { get { targetReference.getObject() as? PBXTarget @@ -26,17 +25,17 @@ public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXObject { targetReference = newValue.reference } } - + // MARK: - Init - + public init(target: PBXTarget, membershipExceptions: [String]?, publicHeaders: [String]?, privateHeaders: [String]?) { - self.targetReference = target.reference + targetReference = target.reference self.membershipExceptions = membershipExceptions self.publicHeaders = publicHeaders self.privateHeaders = privateHeaders super.init() } - + // MARK: - Decodable fileprivate enum CodingKeys: String, CodingKey { @@ -52,17 +51,34 @@ public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXObject { let objects = decoder.context.objects let targetReference: String = try container.decode(.target) self.targetReference = referenceRepository.getOrCreate(reference: targetReference, objects: objects) - self.membershipExceptions = try container.decodeIfPresent(.membershipExceptions) - self.publicHeaders = try container.decodeIfPresent(.publicHeaders) - self.privateHeaders = try container.decodeIfPresent(.privateHeaders) + membershipExceptions = try container.decodeIfPresent(.membershipExceptions) + publicHeaders = try container.decodeIfPresent(.publicHeaders) + privateHeaders = try container.decodeIfPresent(.privateHeaders) try super.init(from: decoder) } - + // MARK: - Equatable override func isEqual(to object: Any?) -> Bool { guard let rhs = object as? PBXFileSystemSynchronizedBuildFileExceptionSet else { return false } return isEqual(to: rhs) } - + + // MARK: - PlistSerializable + + func plistKeyAndValue(proj _: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { + var dictionary: [CommentedString: PlistValue] = [:] + dictionary["isa"] = .string(CommentedString(PBXFileSystemSynchronizedBuildFileExceptionSet.isa)) + if let membershipExceptions { + dictionary["membershipExceptions"] = .array(membershipExceptions.map { .string(CommentedString($0)) }) + } + if let publicHeaders { + dictionary["publicHeaders"] = .array(publicHeaders.map { .string(CommentedString($0)) }) + } + if let privateHeaders { + dictionary["privateHeaders"] = .array(privateHeaders.map { .string(CommentedString($0)) }) + } + dictionary["target"] = .string(CommentedString(target.reference.value, comment: target.name)) + return (key: CommentedString(reference, comment: "PBXFileSystemSynchronizedBuildFileExceptionSet"), value: .dictionary(dictionary)) + } } diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift index 23da01498..4768ea3f9 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift @@ -2,12 +2,11 @@ import Foundation import PathKit public class PBXFileSystemSynchronizedRootGroup: PBXFileElement { - /// It maps relative paths inside the synchronized root group to a particular file type. /// If a path doesn't have a particular file type specified, Xcode defaults to the default file type /// based on the extension of the file. public var explicitFileTypes: [String: String] - + /// Returns the references of the exceptions. var exceptionsReferences: [PBXObjectReference] @@ -21,10 +20,10 @@ public class PBXFileSystemSynchronizedRootGroup: PBXFileElement { exceptionsReferences.objects() } } - + /// A list of relative paths to children folder whose configuration is overriden. public var explicitFolders: [String] - + /// Initializes the file element with its properties. /// /// - Parameters: @@ -51,7 +50,7 @@ public class PBXFileSystemSynchronizedRootGroup: PBXFileElement { exceptions: [PBXFileSystemSynchronizedBuildFileExceptionSet] = [], explicitFolders: [String] = []) { self.explicitFileTypes = explicitFileTypes - self.exceptionsReferences = exceptions.references() + exceptionsReferences = exceptions.references() self.explicitFolders = explicitFolders super.init(sourceTree: sourceTree, path: path, @@ -62,38 +61,40 @@ public class PBXFileSystemSynchronizedRootGroup: PBXFileElement { tabWidth: tabWidth, wrapsLines: wrapsLines) } - + // MARK: - Decodable - + fileprivate enum CodingKeys: String, CodingKey { case explicitFileTypes case exceptions case explicitFolders } - + public required init(from decoder: Decoder) throws { let objects = decoder.context.objects let objectReferenceRepository = decoder.context.objectReferenceRepository let container = try decoder.container(keyedBy: CodingKeys.self) - self.explicitFileTypes = (try container.decodeIfPresent(.explicitFileTypes)) ?? [:] - let exceptionsReferences: [String] = (try container.decodeIfPresent(.exceptions)) ?? [] + explicitFileTypes = try (container.decodeIfPresent(.explicitFileTypes)) ?? [:] + let exceptionsReferences: [String] = try (container.decodeIfPresent(.exceptions)) ?? [] self.exceptionsReferences = exceptionsReferences.map { objectReferenceRepository.getOrCreate(reference: $0, objects: objects) } - self.explicitFolders = (try container.decodeIfPresent(.explicitFolders)) ?? [] + explicitFolders = try (container.decodeIfPresent(.explicitFolders)) ?? [] try super.init(from: decoder) } - + // MARK: - PlistSerializable + override var multiline: Bool { false } + override func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { var dictionary: [CommentedString: PlistValue] = try super.plistKeyAndValue(proj: proj, reference: reference).value.dictionary ?? [:] dictionary["isa"] = .string(CommentedString(type(of: self).isa)) - dictionary["exceptions"] = .array(self.exceptionsReferences.map({ exceptionReference in - return .string(CommentedString(exceptionReference.value, comment: "PBXFileSystemSynchronizedBuildFileExceptionSet")) + dictionary["exceptions"] = .array(exceptionsReferences.map { exceptionReference in + .string(CommentedString(exceptionReference.value, comment: "PBXFileSystemSynchronizedBuildFileExceptionSet")) + }) + dictionary["explicitFileTypes"] = .dictionary(Dictionary(uniqueKeysWithValues: explicitFileTypes.map { relativePath, fileType in + (CommentedString(relativePath), .string(CommentedString(fileType))) })) - dictionary["explicitFileTypes"] = .dictionary(Dictionary(uniqueKeysWithValues: self.explicitFileTypes.map({ (relativePath, fileType) in - return (CommentedString(relativePath), .string(CommentedString(fileType))) - }))) - dictionary["explicitFolders"] = .array(explicitFolders.map({ .string(CommentedString($0)) })) + dictionary["explicitFolders"] = .array(explicitFolders.map { .string(CommentedString($0)) }) return (key: CommentedString(reference, comment: name ?? path), value: .dictionary(dictionary)) diff --git a/Sources/XcodeProj/Objects/Files/PBXGroup.swift b/Sources/XcodeProj/Objects/Files/PBXGroup.swift index c3c913d5b..8b8d2814b 100644 --- a/Sources/XcodeProj/Objects/Files/PBXGroup.swift +++ b/Sources/XcodeProj/Objects/Files/PBXGroup.swift @@ -115,7 +115,7 @@ public extension PBXGroup { .objects() .first(where: { $0.name == name }) } - + /// Returns the synchronized root group with the given name contained in the given parent group. /// /// - Parameter groupName: group name. diff --git a/Sources/XcodeProj/Objects/Project/PBXObjectParser.swift b/Sources/XcodeProj/Objects/Project/PBXObjectParser.swift index ef80d0466..3c0ad0044 100644 --- a/Sources/XcodeProj/Objects/Project/PBXObjectParser.swift +++ b/Sources/XcodeProj/Objects/Project/PBXObjectParser.swift @@ -68,6 +68,10 @@ final class PBXObjectParser { return try decoder.decode(XCLocalSwiftPackageReference.self, from: data) case XCSwiftPackageProductDependency.isa: return try decoder.decode(XCSwiftPackageProductDependency.self, from: data) + case PBXFileSystemSynchronizedRootGroup.isa: + return try decoder.decode(PBXFileSystemSynchronizedRootGroup.self, from: data) + case PBXFileSystemSynchronizedBuildFileExceptionSet.isa: + return try decoder.decode(PBXFileSystemSynchronizedBuildFileExceptionSet.self, from: data) default: throw PBXObjectError.unknownElement(isa) } diff --git a/Sources/XcodeProj/Objects/Project/PBXObjects.swift b/Sources/XcodeProj/Objects/Project/PBXObjects.swift index 5fbf74e1a..34ff68b66 100644 --- a/Sources/XcodeProj/Objects/Project/PBXObjects.swift +++ b/Sources/XcodeProj/Objects/Project/PBXObjects.swift @@ -135,6 +135,16 @@ class PBXObjects: Equatable { lock.whileLocked { _swiftPackageProductDependencies } } + private var _fileSystemSynchronizedRootGroups: [PBXObjectReference: PBXFileSystemSynchronizedRootGroup] = [:] + var fileSystemSynchronizedRootGroups: [PBXObjectReference: PBXFileSystemSynchronizedRootGroup] { + lock.whileLocked { _fileSystemSynchronizedRootGroups } + } + + private var _fileSystemSynchronizedBuildFileExceptionSets: [PBXObjectReference: PBXFileSystemSynchronizedBuildFileExceptionSet] = [:] + var fileSystemSynchronizedBuildFileExceptionSets: [PBXObjectReference: PBXFileSystemSynchronizedBuildFileExceptionSet] { + lock.whileLocked { _fileSystemSynchronizedBuildFileExceptionSets } + } + // XCSwiftPackageProductDependency /// Initializes the project objects container @@ -218,6 +228,8 @@ class PBXObjects: Equatable { case let object as XCRemoteSwiftPackageReference: _remoteSwiftPackageReferences[objectReference] = object case let object as XCLocalSwiftPackageReference: _localSwiftPackageReferences[objectReference] = object case let object as XCSwiftPackageProductDependency: _swiftPackageProductDependencies[objectReference] = object + case let object as PBXFileSystemSynchronizedRootGroup: _fileSystemSynchronizedRootGroups[objectReference] = object + case let object as PBXFileSystemSynchronizedBuildFileExceptionSet: _fileSystemSynchronizedBuildFileExceptionSets[objectReference] = object default: fatalError("Unhandled PBXObject type for \(object), this is likely a bug / todo") } } diff --git a/Sources/XcodeProj/Objects/Project/PBXProjEncoder.swift b/Sources/XcodeProj/Objects/Project/PBXProjEncoder.swift index 9b0dd2f26..9ee78300c 100644 --- a/Sources/XcodeProj/Objects/Project/PBXProjEncoder.swift +++ b/Sources/XcodeProj/Objects/Project/PBXProjEncoder.swift @@ -113,6 +113,18 @@ final class PBXProjEncoder { outputSettings: outputSettings, stateHolder: &stateHolder, to: &output) + try write(section: "PBXFileSystemSynchronizedBuildFileExceptionSet", + proj: proj, + objects: proj.objects.fileSystemSynchronizedBuildFileExceptionSets, + outputSettings: outputSettings, + stateHolder: &stateHolder, + to: &output) + try write(section: "PBXFileSystemSynchronizedRootGroup", + proj: proj, + objects: proj.objects.fileSystemSynchronizedRootGroups, + outputSettings: outputSettings, + stateHolder: &stateHolder, + to: &output) try write(section: "PBXFrameworksBuildPhase", proj: proj, objects: proj.objects.frameworksBuildPhases, diff --git a/Sources/XcodeProj/Objects/Targets/PBXTarget.swift b/Sources/XcodeProj/Objects/Targets/PBXTarget.swift index 518b26736..fa73903fd 100644 --- a/Sources/XcodeProj/Objects/Targets/PBXTarget.swift +++ b/Sources/XcodeProj/Objects/Targets/PBXTarget.swift @@ -181,9 +181,9 @@ public class PBXTarget: PBXContainerItem { objectReferenceRepository.getOrCreate(reference: $0, objects: objects) } let fileSystemSynchronizedGroupsReferences: [String]? = try container.decodeIfPresent(.fileSystemSynchronizedGroups) - if let fileSystemSynchronizedGroupsReferences = fileSystemSynchronizedGroupsReferences { + if let fileSystemSynchronizedGroupsReferences { self.fileSystemSynchronizedGroupsReferences = fileSystemSynchronizedGroupsReferences.map { - objectReferenceRepository.getOrCreate(reference: $0, objects: objects) + objectReferenceRepository.getOrCreate(reference: $0, objects: objects) } } productType = try container.decodeIfPresent(.productType) diff --git a/Sources/XcodeProj/Scheme/XCScheme+ProfileAction.swift b/Sources/XcodeProj/Scheme/XCScheme+ProfileAction.swift index b3ccc4cf0..6c4864996 100644 --- a/Sources/XcodeProj/Scheme/XCScheme+ProfileAction.swift +++ b/Sources/XcodeProj/Scheme/XCScheme+ProfileAction.swift @@ -81,12 +81,8 @@ public extension XCScheme { commandlineArguments: CommandLineArguments? = nil, environmentVariables: [EnvironmentVariable]? = nil, enableTestabilityWhenProfilingTests: Bool = true, -<<<<<<< HEAD launchAutomaticallySubstyle: String? = nil ) { -======= - launchAutomaticallySubstyle: String? = nil) { ->>>>>>> be7969e7 (Fix some linting issues) self.init( runnable: buildableProductRunnable, buildConfiguration: buildConfiguration, diff --git a/Sources/XcodeProj/Utils/XCConfig.swift b/Sources/XcodeProj/Utils/XCConfig.swift index cd1846ce5..e1e585ea6 100644 --- a/Sources/XcodeProj/Utils/XCConfig.swift +++ b/Sources/XcodeProj/Utils/XCConfig.swift @@ -68,11 +68,7 @@ enum XCConfigParser { do { // first try to load the included xcconfig relative to the current xcconfig config = try XCConfig(path: path.parent() + includePath, projectPath: projectPath) -<<<<<<< HEAD } catch XCConfigError.notFound(_) where projectPath != nil { -======= - } catch XCConfigError.notFound(_)where projectPath != nil { ->>>>>>> be7969e7 (Fix some linting issues) // if that fails, try to load the included xcconfig relative to the project config = try? XCConfig(path: projectPath!.parent() + includePath, projectPath: projectPath) } catch { diff --git a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet+Fixtures.swift b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet+Fixtures.swift index d49b85791..67ad69832 100644 --- a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet+Fixtures.swift +++ b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet+Fixtures.swift @@ -5,9 +5,9 @@ extension PBXFileSystemSynchronizedBuildFileExceptionSet { membershipExceptions: [String]? = [], publicHeaders: [String]? = [], privateHeaders: [String]? = []) -> PBXFileSystemSynchronizedBuildFileExceptionSet { - return PBXFileSystemSynchronizedBuildFileExceptionSet(target: target, - membershipExceptions: membershipExceptions, - publicHeaders: publicHeaders, - privateHeaders: privateHeaders) + PBXFileSystemSynchronizedBuildFileExceptionSet(target: target, + membershipExceptions: membershipExceptions, + publicHeaders: publicHeaders, + privateHeaders: privateHeaders) } } diff --git a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSetTests.swift b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSetTests.swift index 799edaee3..842954846 100644 --- a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSetTests.swift +++ b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSetTests.swift @@ -5,23 +5,23 @@ import XCTest final class PBXFileSystemSynchronizedBuildFileExceptionSetTests: XCTestCase { var target: PBXTarget! var subject: PBXFileSystemSynchronizedBuildFileExceptionSet! - + override func setUp() { super.setUp() target = PBXTarget.fixture() subject = PBXFileSystemSynchronizedBuildFileExceptionSet.fixture(target: target) } - + override func tearDown() { target = nil subject = nil super.tearDown() } - + func test_itHasTheCorrectIsa() { XCTAssertEqual(PBXFileSystemSynchronizedBuildFileExceptionSet.isa, "PBXFileSystemSynchronizedBuildFileExceptionSet") } - + func test_equal_returnsTheCorrectValue() { let another = PBXFileSystemSynchronizedBuildFileExceptionSet.fixture(target: target) XCTAssertEqual(subject, another) diff --git a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroup+Fixtures.swift b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroup+Fixtures.swift index 7a14963af..c4eabf4f7 100644 --- a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroup+Fixtures.swift +++ b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroup+Fixtures.swift @@ -2,26 +2,26 @@ import XcodeProj extension PBXFileSystemSynchronizedRootGroup { static func fixture(sourceTree: PBXSourceTree? = nil, - path: String? = nil, - name: String? = nil, - includeInIndex: Bool? = nil, - usesTabs: Bool? = nil, - indentWidth: UInt? = nil, - tabWidth: UInt? = nil, - wrapsLines: Bool? = nil, - explicitFileTypes: [String: String] = [:], - exceptions: [PBXFileSystemSynchronizedBuildFileExceptionSet] = [], + path: String? = nil, + name: String? = nil, + includeInIndex: Bool? = nil, + usesTabs: Bool? = nil, + indentWidth: UInt? = nil, + tabWidth: UInt? = nil, + wrapsLines: Bool? = nil, + explicitFileTypes: [String: String] = [:], + exceptions: [PBXFileSystemSynchronizedBuildFileExceptionSet] = [], explicitFolders: [String] = []) -> PBXFileSystemSynchronizedRootGroup { - return PBXFileSystemSynchronizedRootGroup(sourceTree: sourceTree, - path: path, - name: name, - includeInIndex: includeInIndex, - usesTabs: usesTabs, - indentWidth: indentWidth, - tabWidth: tabWidth, - wrapsLines: wrapsLines, - explicitFileTypes: explicitFileTypes, - exceptions: exceptions, - explicitFolders: explicitFolders) + PBXFileSystemSynchronizedRootGroup(sourceTree: sourceTree, + path: path, + name: name, + includeInIndex: includeInIndex, + usesTabs: usesTabs, + indentWidth: indentWidth, + tabWidth: tabWidth, + wrapsLines: wrapsLines, + explicitFileTypes: explicitFileTypes, + exceptions: exceptions, + explicitFolders: explicitFolders) } } diff --git a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroupTests.swift b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroupTests.swift index 21894e0e0..374156104 100644 --- a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroupTests.swift +++ b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedRootGroupTests.swift @@ -6,7 +6,7 @@ final class PBXFileSystemSynchronizedRootGroupTests: XCTestCase { var subject: PBXFileSystemSynchronizedRootGroup! var target: PBXTarget! var exception: PBXFileSystemSynchronizedBuildFileExceptionSet! - + override func setUp() { super.setUp() target = PBXTarget.fixture() @@ -23,17 +23,17 @@ final class PBXFileSystemSynchronizedRootGroupTests: XCTestCase { exceptions: [exception], explicitFolders: []) } - + override func tearDown() { exception = nil subject = nil super.tearDown() } - + func test_itHasTheCorrectIsa() { XCTAssertEqual(PBXFileSystemSynchronizedRootGroup.isa, "PBXFileSystemSynchronizedRootGroup") } - + func test_equal_returnsTheCorrectValue() { let another = PBXFileSystemSynchronizedRootGroup(sourceTree: .group, path: "synchronized", diff --git a/Tests/XcodeProjTests/Objects/Project/PBXProjEncoderTests.swift b/Tests/XcodeProjTests/Objects/Project/PBXProjEncoderTests.swift index d60e08380..9ff5e3cbc 100644 --- a/Tests/XcodeProjTests/Objects/Project/PBXProjEncoderTests.swift +++ b/Tests/XcodeProjTests/Objects/Project/PBXProjEncoderTests.swift @@ -285,6 +285,38 @@ class PBXProjEncoderTests: XCTestCase { lines.validate(line: ");", after: line) } + // MARK: - File system synchronized root groups + + func test_fileSystemSynchronizedRootGroups_when_projectWithFileSystemSynchronizedRootGroups() throws { + // Given + try loadSynchronizedRootGroups() + let settings = PBXOutputSettings(projNavigatorFileOrder: .byFilenameGroupsFirst) + let lines = lines(fromFile: encodeProject(settings: settings)) + + let beginGroup = lines.findLine("/* Begin PBXFileSystemSynchronizedRootGroup section */") + var line = lines.validate(line: "6CF05B9D2C53F64800EF267F /* SynchronizedRootGroups */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (6CF05BA32C53F97F00EF267F /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = SynchronizedRootGroups; sourceTree = \"\"; };", after: beginGroup) + line = lines.validate(line: "/* End PBXFileSystemSynchronizedRootGroup section */", after: line) + } + + // MARK: - File system synchronized build file exception set + + func test_fileSystemSynchronizedBuildFileExceptionSets_when_projectWithFileSystemSynchronizedRootGroups() throws { + // Given + try loadSynchronizedRootGroups() + let settings = PBXOutputSettings(projNavigatorFileOrder: .byFilenameGroupsFirst) + let lines = lines(fromFile: encodeProject(settings: settings)) + + let beginGroup = lines.findLine("/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */") + var line = lines.validate(line: "6CF05BA32C53F97F00EF267F /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {", after: beginGroup) + line = lines.validate(line: "isa = PBXFileSystemSynchronizedBuildFileExceptionSet;", after: line) + line = lines.validate(line: "membershipExceptions = (", after: line) + line = lines.validate(line: "Exception/Exception.swift,", after: line) + line = lines.validate(line: ");", after: line) + line = lines.validate(line: "target = 6CF05B8B2C53F5F200EF267F /* SynchronizedRootGroups */;", after: line) + line = lines.validate(line: "};", after: line) + line = lines.validate(line: "/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */", after: line) + } + // MARK: - Build phases func test_build_phase_sources_unsorted_when_iOSProject() throws { @@ -457,6 +489,10 @@ class PBXProjEncoderTests: XCTestCase { proj = try PBXProj(jsonDictionary: iosProjectDictionary().1) } + private func loadSynchronizedRootGroups() throws { + proj = try PBXProj(jsonDictionary: synchronizedRootGroupsFixture().1) + } + private func loadFileSharedAcrossTargetsProject() throws { proj = try PBXProj(jsonDictionary: fileSharedAcrossTargetsDictionary().1) } diff --git a/Tests/XcodeProjTests/Tests/Fixtures.swift b/Tests/XcodeProjTests/Tests/Fixtures.swift index 40cc343f9..edb724a5b 100644 --- a/Tests/XcodeProjTests/Tests/Fixtures.swift +++ b/Tests/XcodeProjTests/Tests/Fixtures.swift @@ -6,6 +6,11 @@ func fixturesPath() -> Path { Path(#file).parent().parent().parent().parent() + "Fixtures" } +func synchronizedRootGroupsFixture() -> (Path, [String: Any]) { + let iosProject = fixturesPath() + "SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj/project.pbxproj" + return (iosProject, loadPlist(path: iosProject.string)!) +} + func iosProjectDictionary() -> (Path, [String: Any]) { let iosProject = fixturesPath() + "iOS/Project.xcodeproj/project.pbxproj" return (iosProject, loadPlist(path: iosProject.string)!) From 0f6328502366de1344727ce15df9e5d87cc5c74b Mon Sep 17 00:00:00 2001 From: Pedro Date: Thu, 8 Aug 2024 13:43:09 +0200 Subject: [PATCH 08/12] feat: Add optional exceptions to PBXFileSystemSynchronizedRootGroup --- .../PBXFileSystemSynchronizedRootGroup.swift | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift index 4768ea3f9..b03603c5b 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift @@ -8,16 +8,16 @@ public class PBXFileSystemSynchronizedRootGroup: PBXFileElement { public var explicitFileTypes: [String: String] /// Returns the references of the exceptions. - var exceptionsReferences: [PBXObjectReference] + var exceptionsReferences: [PBXObjectReference]? /// It returns a list of exception objects that override the configuration for some children /// in the synchronized root group. - public var exceptions: [PBXFileSystemSynchronizedBuildFileExceptionSet] { + public var exceptions: [PBXFileSystemSynchronizedBuildFileExceptionSet]? { set { - exceptionsReferences = newValue.references() + exceptionsReferences = newValue?.references() } get { - exceptionsReferences.objects() + exceptionsReferences?.objects() } } @@ -88,13 +88,15 @@ public class PBXFileSystemSynchronizedRootGroup: PBXFileElement { override func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { var dictionary: [CommentedString: PlistValue] = try super.plistKeyAndValue(proj: proj, reference: reference).value.dictionary ?? [:] dictionary["isa"] = .string(CommentedString(type(of: self).isa)) - dictionary["exceptions"] = .array(exceptionsReferences.map { exceptionReference in - .string(CommentedString(exceptionReference.value, comment: "PBXFileSystemSynchronizedBuildFileExceptionSet")) - }) - dictionary["explicitFileTypes"] = .dictionary(Dictionary(uniqueKeysWithValues: explicitFileTypes.map { relativePath, fileType in - (CommentedString(relativePath), .string(CommentedString(fileType))) - })) - dictionary["explicitFolders"] = .array(explicitFolders.map { .string(CommentedString($0)) }) + if let exceptionsReferences = self.exceptionsReferences { + dictionary["exceptions"] = .array(exceptionsReferences.map({ exceptionReference in + return .string(CommentedString(exceptionReference.value, comment: "PBXFileSystemSynchronizedBuildFileExceptionSet")) + })) + } + dictionary["explicitFileTypes"] = .dictionary(Dictionary(uniqueKeysWithValues: self.explicitFileTypes.map({ (relativePath, fileType) in + return (CommentedString(relativePath), .string(CommentedString(fileType))) + }))) + dictionary["explicitFolders"] = .array(explicitFolders.map({ .string(CommentedString($0)) })) return (key: CommentedString(reference, comment: name ?? path), value: .dictionary(dictionary)) From 1e5ba340e2aceb7c97d6da53e67b362619754c8f Mon Sep 17 00:00:00 2001 From: Pedro Date: Thu, 8 Aug 2024 14:07:27 +0200 Subject: [PATCH 09/12] fix!: Fix non-determinism reading and writing the packageProductDependencies attribute of PBXTarget --- .../PBXFileSystemSynchronizedRootGroup.swift | 16 ++++++------- .../Objects/Project/PBXObjects.swift | 15 +++++++++++- .../Objects/Project/PBXProject.swift | 4 ++-- .../XcodeProj/Objects/Targets/PBXTarget.swift | 24 ++++++++++++------- .../Objects/Project/PBXProjectTests.swift | 6 ++--- ....swift => XcodeProjIntegrationTests.swift} | 9 +++++++ 6 files changed, 51 insertions(+), 23 deletions(-) rename Tests/XcodeProjTests/Project/{XcodeProjTests.swift => XcodeProjIntegrationTests.swift} (89%) diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift index b03603c5b..cfe92fa97 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift @@ -88,15 +88,15 @@ public class PBXFileSystemSynchronizedRootGroup: PBXFileElement { override func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { var dictionary: [CommentedString: PlistValue] = try super.plistKeyAndValue(proj: proj, reference: reference).value.dictionary ?? [:] dictionary["isa"] = .string(CommentedString(type(of: self).isa)) - if let exceptionsReferences = self.exceptionsReferences { - dictionary["exceptions"] = .array(exceptionsReferences.map({ exceptionReference in - return .string(CommentedString(exceptionReference.value, comment: "PBXFileSystemSynchronizedBuildFileExceptionSet")) - })) + if let exceptionsReferences { + dictionary["exceptions"] = .array(exceptionsReferences.map { exceptionReference in + .string(CommentedString(exceptionReference.value, comment: "PBXFileSystemSynchronizedBuildFileExceptionSet")) + }) } - dictionary["explicitFileTypes"] = .dictionary(Dictionary(uniqueKeysWithValues: self.explicitFileTypes.map({ (relativePath, fileType) in - return (CommentedString(relativePath), .string(CommentedString(fileType))) - }))) - dictionary["explicitFolders"] = .array(explicitFolders.map({ .string(CommentedString($0)) })) + dictionary["explicitFileTypes"] = .dictionary(Dictionary(uniqueKeysWithValues: explicitFileTypes.map { relativePath, fileType in + (CommentedString(relativePath), .string(CommentedString(fileType))) + })) + dictionary["explicitFolders"] = .array(explicitFolders.map { .string(CommentedString($0)) }) return (key: CommentedString(reference, comment: name ?? path), value: .dictionary(dictionary)) diff --git a/Sources/XcodeProj/Objects/Project/PBXObjects.swift b/Sources/XcodeProj/Objects/Project/PBXObjects.swift index 34ff68b66..22cfe75cd 100644 --- a/Sources/XcodeProj/Objects/Project/PBXObjects.swift +++ b/Sources/XcodeProj/Objects/Project/PBXObjects.swift @@ -183,7 +183,9 @@ class PBXObjects: Equatable { lhs.carbonResourcesBuildPhases == rhs.carbonResourcesBuildPhases && lhs.buildRules == rhs.buildRules && lhs.swiftPackageProductDependencies == rhs._swiftPackageProductDependencies && - lhs.remoteSwiftPackageReferences == rhs.remoteSwiftPackageReferences + lhs.remoteSwiftPackageReferences == rhs.remoteSwiftPackageReferences && + lhs.fileSystemSynchronizedRootGroups == rhs.fileSystemSynchronizedRootGroups && + lhs.fileSystemSynchronizedBuildFileExceptionSets == rhs.fileSystemSynchronizedBuildFileExceptionSets } // MARK: - Helpers @@ -290,7 +292,12 @@ class PBXObjects: Equatable { return _remoteSwiftPackageReferences.remove(at: index).value } else if let index = swiftPackageProductDependencies.index(forKey: reference) { return _swiftPackageProductDependencies.remove(at: index).value + } else if let index = fileSystemSynchronizedRootGroups.index(forKey: reference) { + return _fileSystemSynchronizedRootGroups.remove(at: index).value + } else if let index = fileSystemSynchronizedBuildFileExceptionSets.index(forKey: reference) { + return _fileSystemSynchronizedBuildFileExceptionSets.remove(at: index).value } + return nil } @@ -352,6 +359,10 @@ class PBXObjects: Equatable { return object } else if let object = swiftPackageProductDependencies[reference] { return object + } else if let object = fileSystemSynchronizedRootGroups[reference] { + return object + } else if let object = fileSystemSynchronizedBuildFileExceptionSets[reference] { + return object } else { return nil } @@ -443,5 +454,7 @@ extension PBXObjects { carbonResourcesBuildPhases.values.forEach(closure) remoteSwiftPackageReferences.values.forEach(closure) swiftPackageProductDependencies.values.forEach(closure) + fileSystemSynchronizedRootGroups.values.forEach(closure) + fileSystemSynchronizedBuildFileExceptionSets.values.forEach(closure) } } diff --git a/Sources/XcodeProj/Objects/Project/PBXProject.swift b/Sources/XcodeProj/Objects/Project/PBXProject.swift index 79a3e1485..378309da8 100644 --- a/Sources/XcodeProj/Objects/Project/PBXProject.swift +++ b/Sources/XcodeProj/Objects/Project/PBXProject.swift @@ -443,7 +443,7 @@ extension PBXProject { productDependency = XCSwiftPackageProductDependency(productName: productName, package: reference) objects.add(object: productDependency) } - target.packageProductDependencies.append(productDependency) + target.packageProductDependencies?.append(productDependency) return productDependency } @@ -465,7 +465,7 @@ extension PBXProject { productDependency = XCSwiftPackageProductDependency(productName: productName) objects.add(object: productDependency) } - target.packageProductDependencies.append(productDependency) + target.packageProductDependencies?.append(productDependency) return productDependency } diff --git a/Sources/XcodeProj/Objects/Targets/PBXTarget.swift b/Sources/XcodeProj/Objects/Targets/PBXTarget.swift index fa73903fd..b23e0b47f 100644 --- a/Sources/XcodeProj/Objects/Targets/PBXTarget.swift +++ b/Sources/XcodeProj/Objects/Targets/PBXTarget.swift @@ -76,15 +76,15 @@ public class PBXTarget: PBXContainerItem { } /// Swift package product references. - var packageProductDependencyReferences: [PBXObjectReference] + var packageProductDependencyReferences: [PBXObjectReference]? /// Swift packages products. - public var packageProductDependencies: [XCSwiftPackageProductDependency] { + public var packageProductDependencies: [XCSwiftPackageProductDependency]? { set { - packageProductDependencyReferences = newValue.references() + packageProductDependencyReferences = newValue?.references() } get { - packageProductDependencyReferences.objects() + packageProductDependencyReferences?.objects() } } @@ -176,10 +176,15 @@ public class PBXTarget: PBXContainerItem { } else { productReference = nil } - let packageProductDependencyReferenceStrings: [String] = try container.decodeIfPresent(.packageProductDependencies) ?? [] - packageProductDependencyReferences = packageProductDependencyReferenceStrings.map { - objectReferenceRepository.getOrCreate(reference: $0, objects: objects) + let packageProductDependencyReferenceStrings: [String]? = try container.decodeIfPresent(.packageProductDependencies) + if let packageProductDependencyReferenceStrings { + packageProductDependencyReferences = packageProductDependencyReferenceStrings.map { + objectReferenceRepository.getOrCreate(reference: $0, objects: objects) + } + } else { + packageProductDependencyReferences = nil } + let fileSystemSynchronizedGroupsReferences: [String]? = try container.decodeIfPresent(.fileSystemSynchronizedGroups) if let fileSystemSynchronizedGroupsReferences { self.fileSystemSynchronizedGroupsReferences = fileSystemSynchronizedGroupsReferences.map { @@ -213,7 +218,7 @@ public class PBXTarget: PBXContainerItem { if let fileSystemSynchronizedGroupsReferences { dictionary["fileSystemSynchronizedGroups"] = .array(fileSystemSynchronizedGroupsReferences.map { fileSystemSynchronizedGroupReference in let fileSystemSynchronizedGroup: PBXFileSystemSynchronizedRootGroup? = fileSystemSynchronizedGroupReference.getObject() - return .string(CommentedString(fileSystemSynchronizedGroupReference.value, comment: fileSystemSynchronizedGroup?.name)) + return .string(CommentedString(fileSystemSynchronizedGroupReference.value, comment: fileSystemSynchronizedGroup?.path)) }) } @@ -228,11 +233,12 @@ public class PBXTarget: PBXContainerItem { let fileElement: PBXFileElement? = productReference.getObject() dictionary["productReference"] = .string(CommentedString(productReference.value, comment: fileElement?.fileName())) } - if !packageProductDependencies.isEmpty { + if let packageProductDependencies { dictionary["packageProductDependencies"] = .array(packageProductDependencies.map { PlistValue.string(.init($0.reference.value, comment: $0.productName)) }) } + return (key: CommentedString(reference, comment: name), value: .dictionary(dictionary)) } diff --git a/Tests/XcodeProjTests/Objects/Project/PBXProjectTests.swift b/Tests/XcodeProjTests/Objects/Project/PBXProjectTests.swift index f2b9e0912..5a343bfec 100644 --- a/Tests/XcodeProjTests/Objects/Project/PBXProjectTests.swift +++ b/Tests/XcodeProjTests/Objects/Project/PBXProjectTests.swift @@ -67,7 +67,7 @@ final class PBXProjectTests: XCTestCase { // Then XCTAssertEqual(packageProduct, objects.buildFiles.first?.value.product) XCTAssertEqual(packageProduct, objects.swiftPackageProductDependencies.first?.value) - XCTAssertEqual(packageProduct, target.packageProductDependencies.first) + XCTAssertEqual(packageProduct, target.packageProductDependencies?.first) XCTAssertEqual(objects.fileReferences.first?.value.name, "Product") @@ -240,8 +240,8 @@ final class PBXProjectTests: XCTestCase { XCTAssertEqual(packageProduct, secondPackageProduct) XCTAssertEqual(packageProduct, thirdPackageProduct) XCTAssertEqual(project.remotePackages.count, 1) - XCTAssertEqual(target.packageProductDependencies.count, 2) - XCTAssertEqual(secondTarget.packageProductDependencies.count, 1) + XCTAssertEqual(target.packageProductDependencies?.count, 2) + XCTAssertEqual(secondTarget.packageProductDependencies?.count, 1) XCTAssertNotEqual(buildPhase.files?.first?.hashValue, secondBuildPhase.files?.first?.hashValue) XCTAssertEqual(objects.swiftPackageProductDependencies.count, 2) diff --git a/Tests/XcodeProjTests/Project/XcodeProjTests.swift b/Tests/XcodeProjTests/Project/XcodeProjIntegrationTests.swift similarity index 89% rename from Tests/XcodeProjTests/Project/XcodeProjTests.swift rename to Tests/XcodeProjTests/Project/XcodeProjIntegrationTests.swift index 424767edd..8bcb72580 100644 --- a/Tests/XcodeProjTests/Project/XcodeProjTests.swift +++ b/Tests/XcodeProjTests/Project/XcodeProjIntegrationTests.swift @@ -25,6 +25,11 @@ final class XcodeProjIntegrationTests: XCTestCase { initModel: XcodeProj.init(path:)) } + func test_read_write_produces_no_diff_when_synchronizedRootGroupsFixture() throws { + try testReadWriteProducesNoDiff(from: synchronizedRootGroupsFixturePath, + initModel: XcodeProj.init(path:)) + } + func test_initialize_PBXProj_with_data() throws { // Given let pbxprojPath = iosProjectPath + "project.pbxproj" @@ -90,4 +95,8 @@ final class XcodeProjIntegrationTests: XCTestCase { private var iosProjectPath: Path { fixturesPath() + "iOS/Project.xcodeproj" } + + private var synchronizedRootGroupsFixturePath: Path { + fixturesPath() + "SynchronizedRootGroups/SynchronizedRootGroups.xcodeproj" + } } From fe6311f9814f62e6561f36fc60d6a4e215774681 Mon Sep 17 00:00:00 2001 From: Pedro Date: Thu, 8 Aug 2024 15:56:38 +0200 Subject: [PATCH 10/12] Fix issues after rebasing --- Sources/XcodeProj/Utils/ReferenceGenerator.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/XcodeProj/Utils/ReferenceGenerator.swift b/Sources/XcodeProj/Utils/ReferenceGenerator.swift index 823c582bd..a098c8298 100644 --- a/Sources/XcodeProj/Utils/ReferenceGenerator.swift +++ b/Sources/XcodeProj/Utils/ReferenceGenerator.swift @@ -100,7 +100,7 @@ final class ReferenceGenerator: ReferenceGenerating { identifiers.append(target.name) // Packages - for packageProductDependency in target.packageProductDependencies { + target.packageProductDependencies?.forEach { packageProductDependency in var identifiers = identifiers identifiers.append(packageProductDependency.productName) fixReference(for: packageProductDependency, identifiers: identifiers) From 029c3dd9a3f27f376973d63596404e2ba1f0344e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Pi=C3=B1era=20Buend=C3=ADa?= <663605+pepicrft@users.noreply.github.com> Date: Sat, 10 Aug 2024 10:49:28 +0200 Subject: [PATCH 11/12] Update Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift Co-authored-by: Kas --- .../Objects/Files/PBXFileSystemSynchronizedRootGroup.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift index cfe92fa97..816ae9268 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift @@ -88,7 +88,7 @@ public class PBXFileSystemSynchronizedRootGroup: PBXFileElement { override func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { var dictionary: [CommentedString: PlistValue] = try super.plistKeyAndValue(proj: proj, reference: reference).value.dictionary ?? [:] dictionary["isa"] = .string(CommentedString(type(of: self).isa)) - if let exceptionsReferences { + if let exceptionsReferences, !exceptionReferences.isEmpty { dictionary["exceptions"] = .array(exceptionsReferences.map { exceptionReference in .string(CommentedString(exceptionReference.value, comment: "PBXFileSystemSynchronizedBuildFileExceptionSet")) }) From a73dee4bba3d7ee4c273925ce38a3a12e9e34abc Mon Sep 17 00:00:00 2001 From: Pedro Date: Sat, 10 Aug 2024 18:20:16 +0200 Subject: [PATCH 12/12] Add missing attributes --- ...temSynchronizedBuildFileExceptionSet.swift | 28 ++++++++++++++++++- .../PBXFileSystemSynchronizedRootGroup.swift | 2 +- ...onizedBuildFileExceptionSet+Fixtures.swift | 8 ++++-- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift index b883cc8dd..572380cf8 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet.swift @@ -15,6 +15,16 @@ public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXObject, PlistSer /// Every item in the list is the relative path inside the root synchronized group. public var privateHeaders: [String]? + /// Additional compiler flags by relative path. + /// Every item in the list is the relative path inside the root synchronized group. + /// The value is the additional compiler flags. + public var additionalCompilerFlagsByRelativePath: [String: String]? + + /// Attributes by relative path. + /// Every item in the list is the relative path inside the root synchronized group. + /// This is used for example when linking frameworks to specify that they are optional with the attribute "Weak" + public var attributesByRelativePath: [String: [String]]? + var targetReference: PBXObjectReference public var target: PBXTarget! { @@ -28,11 +38,13 @@ public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXObject, PlistSer // MARK: - Init - public init(target: PBXTarget, membershipExceptions: [String]?, publicHeaders: [String]?, privateHeaders: [String]?) { + public init(target: PBXTarget, membershipExceptions: [String]?, publicHeaders: [String]?, privateHeaders: [String]?, additionalCompilerFlagsByRelativePath: [String: String]?, attributesByRelativePath: [String: [String]]?) { targetReference = target.reference self.membershipExceptions = membershipExceptions self.publicHeaders = publicHeaders self.privateHeaders = privateHeaders + self.additionalCompilerFlagsByRelativePath = additionalCompilerFlagsByRelativePath + self.attributesByRelativePath = attributesByRelativePath super.init() } @@ -43,6 +55,8 @@ public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXObject, PlistSer case membershipExceptions case publicHeaders case privateHeaders + case additionalCompilerFlagsByRelativePath + case attributesByRelativePath } public required init(from decoder: Decoder) throws { @@ -54,6 +68,8 @@ public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXObject, PlistSer membershipExceptions = try container.decodeIfPresent(.membershipExceptions) publicHeaders = try container.decodeIfPresent(.publicHeaders) privateHeaders = try container.decodeIfPresent(.privateHeaders) + additionalCompilerFlagsByRelativePath = try container.decodeIfPresent(.additionalCompilerFlagsByRelativePath) + attributesByRelativePath = try container.decodeIfPresent(.attributesByRelativePath) try super.init(from: decoder) } @@ -78,6 +94,16 @@ public class PBXFileSystemSynchronizedBuildFileExceptionSet: PBXObject, PlistSer if let privateHeaders { dictionary["privateHeaders"] = .array(privateHeaders.map { .string(CommentedString($0)) }) } + if let additionalCompilerFlagsByRelativePath { + dictionary["additionalCompilerFlagsByRelativePath"] = .dictionary(Dictionary(uniqueKeysWithValues: additionalCompilerFlagsByRelativePath.map { key, value in + (CommentedString(key), PlistValue.string(CommentedString(value))) + })) + } + if let attributesByRelativePath { + dictionary["attributesByRelativePath"] = .dictionary(Dictionary(uniqueKeysWithValues: attributesByRelativePath.map { key, value in + (CommentedString(key), .array(value.map { .string(CommentedString($0)) })) + })) + } dictionary["target"] = .string(CommentedString(target.reference.value, comment: target.name)) return (key: CommentedString(reference, comment: "PBXFileSystemSynchronizedBuildFileExceptionSet"), value: .dictionary(dictionary)) } diff --git a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift index 816ae9268..5df7fe342 100644 --- a/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift +++ b/Sources/XcodeProj/Objects/Files/PBXFileSystemSynchronizedRootGroup.swift @@ -88,7 +88,7 @@ public class PBXFileSystemSynchronizedRootGroup: PBXFileElement { override func plistKeyAndValue(proj: PBXProj, reference: String) throws -> (key: CommentedString, value: PlistValue) { var dictionary: [CommentedString: PlistValue] = try super.plistKeyAndValue(proj: proj, reference: reference).value.dictionary ?? [:] dictionary["isa"] = .string(CommentedString(type(of: self).isa)) - if let exceptionsReferences, !exceptionReferences.isEmpty { + if let exceptionsReferences, !exceptionsReferences.isEmpty { dictionary["exceptions"] = .array(exceptionsReferences.map { exceptionReference in .string(CommentedString(exceptionReference.value, comment: "PBXFileSystemSynchronizedBuildFileExceptionSet")) }) diff --git a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet+Fixtures.swift b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet+Fixtures.swift index 67ad69832..17e0b1f0d 100644 --- a/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet+Fixtures.swift +++ b/Tests/XcodeProjTests/Objects/Files/PBXFileSystemSynchronizedBuildFileExceptionSet+Fixtures.swift @@ -4,10 +4,14 @@ extension PBXFileSystemSynchronizedBuildFileExceptionSet { static func fixture(target: PBXTarget = .fixture(), membershipExceptions: [String]? = [], publicHeaders: [String]? = [], - privateHeaders: [String]? = []) -> PBXFileSystemSynchronizedBuildFileExceptionSet { + privateHeaders: [String]? = [], + additionalCompilerFlagsByRelativePath: [String: String]? = nil, + attributesByRelativePath: [String: [String]]? = nil) -> PBXFileSystemSynchronizedBuildFileExceptionSet { PBXFileSystemSynchronizedBuildFileExceptionSet(target: target, membershipExceptions: membershipExceptions, publicHeaders: publicHeaders, - privateHeaders: privateHeaders) + privateHeaders: privateHeaders, + additionalCompilerFlagsByRelativePath: additionalCompilerFlagsByRelativePath, + attributesByRelativePath: attributesByRelativePath) } }