Skip to content

[NFC] Clean up package graph caching code #7128

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 20 additions & 13 deletions Sources/PackageGraph/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
add_library(PackageGraph
BoundVersion.swift
DependencyMirrors.swift
DependencyResolutionNode.swift
DependencyResolver.swift
Diagnostics.swift
GraphLoadingNode.swift
ModuleAliasTracker.swift
Expand All @@ -21,17 +19,26 @@ add_library(PackageGraph
PackageModel+Extensions.swift
PackageRequirement.swift
PinsStore.swift
PubGrub/Assignment.swift
PubGrub/ContainerProvider.swift
PubGrub/DiagnosticReportBuilder.swift
PubGrub/Incompatibility.swift
PubGrub/PartialSolution.swift
PubGrub/PubGrubDependencyResolver.swift
PubGrub/PubGrubPackageContainer.swift
PubGrub/Term.swift
ResolvedPackage.swift
ResolvedProduct.swift
ResolvedTarget.swift
Resolution/Caching/DuplicateProductsChecker.swift
Resolution/Caching/Cacheable.swift
Resolution/Caching/CachedResolvedPackage.swift
Resolution/Caching/CachedResolvedProduct.swift
Resolution/Caching/CachedResolvedTarget.swift
Resolution/PubGrub/Assignment.swift
Resolution/PubGrub/ContainerProvider.swift
Resolution/PubGrub/DiagnosticReportBuilder.swift
Resolution/PubGrub/Incompatibility.swift
Resolution/PubGrub/PartialSolution.swift
Resolution/PubGrub/PubGrubDependencyResolver.swift
Resolution/PubGrub/PubGrubPackageContainer.swift
Resolution/PubGrub/Term.swift
Resolution/DependencyResolutionNode.swift
Resolution/DependencyResolverBinding.swift
Resolution/DependencyResolverDelegate.swift
Resolution/DependencyResolverError.swift
Resolution/ResolvedPackage.swift
Resolution/ResolvedProduct.swift
Resolution/ResolvedTarget.swift
Version+Extensions.swift
VersionSetSpecifier.swift)
target_link_libraries(PackageGraph PUBLIC
Expand Down
2 changes: 1 addition & 1 deletion Sources/PackageGraph/GraphLoadingNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import PackageModel
///
/// This node uses the product filter that was already finalized during resolution.
///
/// - SeeAlso: DependencyResolutionNode
/// - SeeAlso: ``DependencyResolutionNode``
public struct GraphLoadingNode: Equatable, Hashable {

/// The package identity.
Expand Down
471 changes: 127 additions & 344 deletions Sources/PackageGraph/PackageGraph+Loading.swift

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions Sources/PackageGraph/Resolution/Caching/Cacheable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

/// A generic caching container for `Resolved` models.
class Cacheable<T> {
/// The constructed object, available after the first call to `construct()`.
private var _constructedObject: T?

/// Construct the object with the accumulated data.
///
/// Note that once the object is constructed, future calls to
/// this method will return the same object.
final func construct() throws -> T {
if let _constructedObject {
return _constructedObject
}
let constructedObject = try self.constructImpl()
_constructedObject = constructedObject
return constructedObject
}

/// The object construction implementation.
func constructImpl() throws -> T {
fatalError("Should be implemented by subclasses")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import class PackageModel.Package
import struct PackageModel.PackageIdentity
import enum PackageModel.ProductFilter
import struct PackageModel.RegistryReleaseMetadata
import struct PackageModel.SupportedPlatforms

/// Caching container for resolved packages.
final class CachedResolvedPackage: Cacheable<ResolvedPackage> {
/// The package reference.
let package: Package

/// The product filter applied to the package.
let productFilter: ProductFilter

/// Package can vend unsafe products
let isAllowedToVendUnsafeProducts: Bool

/// Package can be overridden
let allowedToOverride: Bool

/// The targets in the package.
var targets: [CachedResolvedTarget] = []

/// The products in this package.
var products: [CachedResolvedProduct] = []

/// The dependencies of this package.
var dependencies: [CachedResolvedPackage] = []

/// Map from package identity to the local name for target dependency resolution that has been given to that package
/// through the dependency declaration.
var dependencyNamesForTargetDependencyResolutionOnly: [PackageIdentity: String] = [:]

/// The defaultLocalization for this package.
var defaultLocalization: String? = nil

/// The platforms supported by this package.
var platforms: SupportedPlatforms = .init(declared: [], derivedXCTestPlatformProvider: .none)

/// If the given package's source is a registry release, this provides additional metadata and signature
/// information.
var registryMetadata: RegistryReleaseMetadata?

init(
_ package: Package,
productFilter: ProductFilter,
isAllowedToVendUnsafeProducts: Bool,
allowedToOverride: Bool
) {
self.package = package
self.productFilter = productFilter
self.isAllowedToVendUnsafeProducts = isAllowedToVendUnsafeProducts
self.allowedToOverride = allowedToOverride
}

override func constructImpl() throws -> ResolvedPackage {
try ResolvedPackage(
package: self.package,
defaultLocalization: self.defaultLocalization,
platforms: self.platforms,
dependencies: self.dependencies.map { try $0.construct() },
targets: self.targets.map { try $0.construct() },
products: self.products.map { try $0.construct() },
registryMetadata: self.registryMetadata
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import class PackageModel.Product

/// Caching container for resolved products.
final class CachedResolvedProduct: Cacheable<ResolvedProduct> {
/// The reference to its package.
unowned let cachedPackage: CachedResolvedPackage

/// The product reference.
let product: Product

/// Cached resolved targets in the product.
let targets: [CachedResolvedTarget]

init(product: Product, cachedPackage: CachedResolvedPackage, targets: [CachedResolvedTarget]) {
self.product = product
self.cachedPackage = cachedPackage
self.targets = targets
}

override func constructImpl() throws -> ResolvedProduct {
try ResolvedProduct(
product: self.product,
targets: self.targets.map { try $0.construct() }
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import struct Basics.DiagnosticsEmitter
import struct Basics.ObservabilityMetadata
import class Basics.ObservabilityScope
import protocol PackageModel.PackageConditionProtocol
import struct PackageModel.SupportedPlatforms
import class PackageModel.Target

/// Caching container for resolved targets.
final class CachedResolvedTarget: Cacheable<ResolvedTarget> {
/// Enumeration to represent target dependencies.
enum Dependency {
/// Dependency to another target, with conditions.
case target(_ target: CachedResolvedTarget, conditions: [PackageConditionProtocol])

/// Dependency to a product, with conditions.
case product(_ product: CachedResolvedProduct, conditions: [PackageConditionProtocol])
}

/// The target reference.
let target: Target

/// DiagnosticsEmitter with which to emit diagnostics
let diagnosticsEmitter: DiagnosticsEmitter

/// The target dependencies of this target.
var dependencies: [Dependency] = []

/// The defaultLocalization for this package
var defaultLocalization: String? = nil

/// The platforms supported by this package.
var platforms: SupportedPlatforms = .init(declared: [], derivedXCTestPlatformProvider: .none)

init(
target: Target,
observabilityScope: ObservabilityScope
) {
self.target = target
self.diagnosticsEmitter = observabilityScope.makeDiagnosticsEmitter {
var metadata = ObservabilityMetadata()
metadata.targetName = target.name
return metadata
}
}

func diagnoseInvalidUseOfUnsafeFlags(_ product: ResolvedProduct) throws {
// Diagnose if any target in this product uses an unsafe flag.
for target in try product.recursiveTargetDependencies() {
if target.underlyingTarget.usesUnsafeFlags {
self.diagnosticsEmitter.emit(.productUsesUnsafeFlags(product: product.name, target: target.name))
}
}
}

override func constructImpl() throws -> ResolvedTarget {
let dependencies = try self.dependencies.map { dependency -> ResolvedTarget.Dependency in
switch dependency {
case .target(let cachedTargetDependency, let conditions):
try self.target.validateDependency(target: cachedTargetDependency.target)
return try .target(cachedTargetDependency.construct(), conditions: conditions)
case .product(let cachedProductDependency, let conditions):
try self.target.validateDependency(
product: cachedProductDependency.product,
productPackage: cachedProductDependency.cachedPackage.package.identity
)
let product = try cachedProductDependency.construct()
if !cachedProductDependency.cachedPackage.isAllowedToVendUnsafeProducts {
try self.diagnoseInvalidUseOfUnsafeFlags(product)
}
return .product(product, conditions: conditions)
}
}

return ResolvedTarget(
target: self.target,
dependencies: dependencies,
defaultLocalization: self.defaultLocalization,
platforms: self.platforms
)
}
}
Loading