Skip to content

Commit

Permalink
fix: Fix non-determinism with the packageProductDependencies attirbut…
Browse files Browse the repository at this point in the history
…e of PBXTarget
  • Loading branch information
pepicrft committed Aug 8, 2024
1 parent 0ae352f commit eb64294
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 84 deletions.
147 changes: 80 additions & 67 deletions Sources/XcodeProj/Objects/Project/PBXObjects.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,133 +3,133 @@ import Foundation
// swiftlint:disable type_body_length
class PBXObjects: Equatable {
// MARK: - Properties

private let lock = NSRecursiveLock()

private var _projects: [PBXObjectReference: PBXProject] = [:]
var projects: [PBXObjectReference: PBXProject] {
lock.whileLocked { _projects }
}

private var _referenceProxies: [PBXObjectReference: PBXReferenceProxy] = [:]
var referenceProxies: [PBXObjectReference: PBXReferenceProxy] {
lock.whileLocked { _referenceProxies }
}

// File elements
private var _fileReferences: [PBXObjectReference: PBXFileReference] = [:]
var fileReferences: [PBXObjectReference: PBXFileReference] {
lock.whileLocked { _fileReferences }
}

private var _versionGroups: [PBXObjectReference: XCVersionGroup] = [:]
var versionGroups: [PBXObjectReference: XCVersionGroup] {
lock.whileLocked { _versionGroups }
}

private var _variantGroups: [PBXObjectReference: PBXVariantGroup] = [:]
var variantGroups: [PBXObjectReference: PBXVariantGroup] {
lock.whileLocked { _variantGroups }
}

private var _groups: [PBXObjectReference: PBXGroup] = [:]
var groups: [PBXObjectReference: PBXGroup] {
lock.whileLocked { _groups }
}

// Configuration
private var _buildConfigurations: [PBXObjectReference: XCBuildConfiguration] = [:]
var buildConfigurations: [PBXObjectReference: XCBuildConfiguration] {
lock.whileLocked { _buildConfigurations }
}

private var _configurationLists: [PBXObjectReference: XCConfigurationList] = [:]
var configurationLists: [PBXObjectReference: XCConfigurationList] {
lock.whileLocked { _configurationLists }
}

// Targets
private var _legacyTargets: [PBXObjectReference: PBXLegacyTarget] = [:]
var legacyTargets: [PBXObjectReference: PBXLegacyTarget] {
lock.whileLocked { _legacyTargets }
}

private var _aggregateTargets: [PBXObjectReference: PBXAggregateTarget] = [:]
var aggregateTargets: [PBXObjectReference: PBXAggregateTarget] {
lock.whileLocked { _aggregateTargets }
}

private var _nativeTargets: [PBXObjectReference: PBXNativeTarget] = [:]
var nativeTargets: [PBXObjectReference: PBXNativeTarget] {
lock.whileLocked { _nativeTargets }
}

private var _targetDependencies: [PBXObjectReference: PBXTargetDependency] = [:]
var targetDependencies: [PBXObjectReference: PBXTargetDependency] {
lock.whileLocked { _targetDependencies }
}

private var _containerItemProxies: [PBXObjectReference: PBXContainerItemProxy] = [:]
var containerItemProxies: [PBXObjectReference: PBXContainerItemProxy] {
lock.whileLocked { _containerItemProxies }
}

private var _buildRules: [PBXObjectReference: PBXBuildRule] = [:]
var buildRules: [PBXObjectReference: PBXBuildRule] {
lock.whileLocked { _buildRules }
}

// Build Phases
private var _buildFiles: [PBXObjectReference: PBXBuildFile] = [:]
var buildFiles: [PBXObjectReference: PBXBuildFile] {
lock.whileLocked { _buildFiles }
}

private var _copyFilesBuildPhases: [PBXObjectReference: PBXCopyFilesBuildPhase] = [:]
var copyFilesBuildPhases: [PBXObjectReference: PBXCopyFilesBuildPhase] {
lock.whileLocked { _copyFilesBuildPhases }
}

private var _shellScriptBuildPhases: [PBXObjectReference: PBXShellScriptBuildPhase] = [:]
var shellScriptBuildPhases: [PBXObjectReference: PBXShellScriptBuildPhase] {
lock.whileLocked { _shellScriptBuildPhases }
}

private var _resourcesBuildPhases: [PBXObjectReference: PBXResourcesBuildPhase] = [:]
var resourcesBuildPhases: [PBXObjectReference: PBXResourcesBuildPhase] {
lock.whileLocked { _resourcesBuildPhases }
}

private var _frameworksBuildPhases: [PBXObjectReference: PBXFrameworksBuildPhase] = [:]
var frameworksBuildPhases: [PBXObjectReference: PBXFrameworksBuildPhase] {
lock.whileLocked { _frameworksBuildPhases }
}

private var _headersBuildPhases: [PBXObjectReference: PBXHeadersBuildPhase] = [:]
var headersBuildPhases: [PBXObjectReference: PBXHeadersBuildPhase] {
lock.whileLocked { _headersBuildPhases }
}

private var _sourcesBuildPhases: [PBXObjectReference: PBXSourcesBuildPhase] = [:]
var sourcesBuildPhases: [PBXObjectReference: PBXSourcesBuildPhase] {
lock.whileLocked { _sourcesBuildPhases }
}

private var _carbonResourcesBuildPhases: [PBXObjectReference: PBXRezBuildPhase] = [:]
var carbonResourcesBuildPhases: [PBXObjectReference: PBXRezBuildPhase] {
lock.whileLocked { _carbonResourcesBuildPhases }
}

private var _remoteSwiftPackageReferences: [PBXObjectReference: XCRemoteSwiftPackageReference] = [:]
var remoteSwiftPackageReferences: [PBXObjectReference: XCRemoteSwiftPackageReference] {
lock.whileLocked { _remoteSwiftPackageReferences }
}

private var _localSwiftPackageReferences: [PBXObjectReference: XCLocalSwiftPackageReference] = [:]
var localSwiftPackageReferences: [PBXObjectReference: XCLocalSwiftPackageReference] {
lock.whileLocked { _localSwiftPackageReferences }
}

private var _swiftPackageProductDependencies: [PBXObjectReference: XCSwiftPackageProductDependency] = [:]
var swiftPackageProductDependencies: [PBXObjectReference: XCSwiftPackageProductDependency] {
lock.whileLocked { _swiftPackageProductDependencies }
Expand All @@ -144,9 +144,9 @@ class PBXObjects: Equatable {
var fileSystemSynchronizedBuildFileExceptionSets: [PBXObjectReference: PBXFileSystemSynchronizedBuildFileExceptionSet] {
lock.whileLocked { _fileSystemSynchronizedBuildFileExceptionSets }
}

// XCSwiftPackageProductDependency

/// Initializes the project objects container
///
/// - Parameters:
Expand All @@ -156,38 +156,40 @@ class PBXObjects: Equatable {
self.add(object: $0)
}
}

// MARK: - Equatable

public static func == (lhs: PBXObjects, rhs: PBXObjects) -> Bool {
lhs.buildFiles == rhs.buildFiles &&
lhs.legacyTargets == rhs.legacyTargets &&
lhs.aggregateTargets == rhs.aggregateTargets &&
lhs.containerItemProxies == rhs.containerItemProxies &&
lhs.copyFilesBuildPhases == rhs.copyFilesBuildPhases &&
lhs.groups == rhs.groups &&
lhs.configurationLists == rhs.configurationLists &&
lhs.buildConfigurations == rhs.buildConfigurations &&
lhs.variantGroups == rhs.variantGroups &&
lhs.targetDependencies == rhs.targetDependencies &&
lhs.sourcesBuildPhases == rhs.sourcesBuildPhases &&
lhs.shellScriptBuildPhases == rhs.shellScriptBuildPhases &&
lhs.resourcesBuildPhases == rhs.resourcesBuildPhases &&
lhs.frameworksBuildPhases == rhs.frameworksBuildPhases &&
lhs.headersBuildPhases == rhs.headersBuildPhases &&
lhs.nativeTargets == rhs.nativeTargets &&
lhs.fileReferences == rhs.fileReferences &&
lhs.projects == rhs.projects &&
lhs.versionGroups == rhs.versionGroups &&
lhs.referenceProxies == rhs.referenceProxies &&
lhs.carbonResourcesBuildPhases == rhs.carbonResourcesBuildPhases &&
lhs.buildRules == rhs.buildRules &&
lhs.swiftPackageProductDependencies == rhs._swiftPackageProductDependencies &&
lhs.remoteSwiftPackageReferences == rhs.remoteSwiftPackageReferences
lhs.legacyTargets == rhs.legacyTargets &&
lhs.aggregateTargets == rhs.aggregateTargets &&
lhs.containerItemProxies == rhs.containerItemProxies &&
lhs.copyFilesBuildPhases == rhs.copyFilesBuildPhases &&
lhs.groups == rhs.groups &&
lhs.configurationLists == rhs.configurationLists &&
lhs.buildConfigurations == rhs.buildConfigurations &&
lhs.variantGroups == rhs.variantGroups &&
lhs.targetDependencies == rhs.targetDependencies &&
lhs.sourcesBuildPhases == rhs.sourcesBuildPhases &&
lhs.shellScriptBuildPhases == rhs.shellScriptBuildPhases &&
lhs.resourcesBuildPhases == rhs.resourcesBuildPhases &&
lhs.frameworksBuildPhases == rhs.frameworksBuildPhases &&
lhs.headersBuildPhases == rhs.headersBuildPhases &&
lhs.nativeTargets == rhs.nativeTargets &&
lhs.fileReferences == rhs.fileReferences &&
lhs.projects == rhs.projects &&
lhs.versionGroups == rhs.versionGroups &&
lhs.referenceProxies == rhs.referenceProxies &&
lhs.carbonResourcesBuildPhases == rhs.carbonResourcesBuildPhases &&
lhs.buildRules == rhs.buildRules &&
lhs.swiftPackageProductDependencies == rhs._swiftPackageProductDependencies &&
lhs.remoteSwiftPackageReferences == rhs.remoteSwiftPackageReferences &&
lhs.fileSystemSynchronizedRootGroups == rhs.fileSystemSynchronizedRootGroups &&
lhs.fileSystemSynchronizedBuildFileExceptionSets == rhs.fileSystemSynchronizedBuildFileExceptionSets
}

// MARK: - Helpers

/// Add a new object.
///
/// - Parameters:
Expand All @@ -199,13 +201,13 @@ class PBXObjects: Equatable {
}
let objectReference: PBXObjectReference = object.reference
objectReference.objects = self

switch object {
// subclasses of PBXGroup; must be tested before PBXGroup
// subclasses of PBXGroup; must be tested before PBXGroup
case let object as PBXVariantGroup: _variantGroups[objectReference] = object
case let object as XCVersionGroup: _versionGroups[objectReference] = object

// everything else
// everything else
case let object as PBXBuildFile: _buildFiles[objectReference] = object
case let object as PBXAggregateTarget: _aggregateTargets[objectReference] = object
case let object as PBXLegacyTarget: _legacyTargets[objectReference] = object
Expand All @@ -231,11 +233,11 @@ class PBXObjects: Equatable {
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")
}
}

/// Deletes the object with the given reference.
///
/// - Parameter reference: referenc of the object to be deleted.
Expand Down Expand Up @@ -292,10 +294,15 @@ 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
}

/// It returns the object with the given reference.
///
/// - Parameter reference: Xcode reference.
Expand Down Expand Up @@ -354,6 +361,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
}
Expand All @@ -378,16 +389,16 @@ extension PBXObjects {
targets.append(contentsOf: filter(aggregateTargets))
return targets
}

/// Invalidates all the objects references.
func invalidateReferences() {
forEach {
$0.reference.invalidate()
}
}

// MARK: - Computed Properties

var buildPhases: [PBXObjectReference: PBXBuildPhase] {
var phases: [PBXObjectReference: PBXBuildPhase] = [:]
phases.merge(copyFilesBuildPhases as [PBXObjectReference: PBXBuildPhase], uniquingKeysWith: { first, _ in first })
Expand All @@ -399,7 +410,7 @@ extension PBXObjects {
phases.merge(frameworksBuildPhases as [PBXObjectReference: PBXBuildPhase], uniquingKeysWith: { first, _ in first })
return phases
}

// This dictionary is used to quickly get a connection between the build phase and the build files of this phase.
// This is used to decode build files. (we need the name of the build phase)
// Otherwise, we would have to go through all the build phases for each file.
Expand All @@ -416,7 +427,7 @@ extension PBXObjects {
}
return Dictionary(values.flatMap { $0 }.map { ($0.buildFile.reference, $0) }, uniquingKeysWith: { first, _ in first })
}

/// Runs the given closure for each of the objects that are part of the project.
///
/// - Parameter closure: closure to be run.
Expand Down Expand Up @@ -445,5 +456,7 @@ extension PBXObjects {
carbonResourcesBuildPhases.values.forEach(closure)
remoteSwiftPackageReferences.values.forEach(closure)
swiftPackageProductDependencies.values.forEach(closure)
fileSystemSynchronizedRootGroups.values.forEach(closure)
fileSystemSynchronizedBuildFileExceptionSets.values.forEach(closure)
}
}
4 changes: 2 additions & 2 deletions Sources/XcodeProj/Objects/Project/PBXProject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -465,7 +465,7 @@ extension PBXProject {
productDependency = XCSwiftPackageProductDependency(productName: productName)
objects.add(object: productDependency)
}
target.packageProductDependencies.append(productDependency)
target.packageProductDependencies?.append(productDependency)

return productDependency
}
Expand Down
Loading

0 comments on commit eb64294

Please sign in to comment.