Skip to content

Commit 001c74a

Browse files
MaxDesiatovxedin
andauthored
[6.0] Improvements to prebuilt/provided library handling (#7713)
Cherry-pick of #7496 and #7433, excluding [16b4142](16b4142). **Explanation**: The idea is to enable provided libraries to be a part of a toolchain, which means that instead of completely eliding them from a package graph, they need to be handled post-resolution in a special way and reflected in a build plan for Swift targets (as compiler and linker arguments). Unified handling of `config.json` and `provided-libraries.json` resources and dropped use of `Bundle` type (config should always be a part of SDK or a toolchain). **Scope**: Localized, touches modules graph resolution and build planning only in certain configurations. **Risk**: Low. While the change has a somewhat broad scope, it's NFC for most builds and has been incubated on `main` for 2 months with no known issues. **Testing**: New test cases added and existing ones updated. **Issue**: rdar://125531670 **Reviewer**: @MaxDesiatov and @bnbarham --------- Co-authored-by: Pavel Yaskevich <[email protected]>
1 parent 74a045f commit 001c74a

File tree

49 files changed

+594
-270
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+594
-270
lines changed

Sources/Build/BuildDescription/ProductBuildDescription.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription
4646
// Computed during build planning.
4747
var dylibs: [ProductBuildDescription] = []
4848

49+
/// The list of provided libraries that are going to be used by this product.
50+
var providedLibraries: [String: AbsolutePath] = [:]
51+
4952
/// Any additional flags to be added. These flags are expected to be computed during build planning.
5053
var additionalFlags: [String] = []
5154

@@ -157,6 +160,8 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription
157160
args += ["-F", self.buildParameters.buildPath.pathString]
158161
}
159162

163+
self.providedLibraries.forEach { args += ["-L", $1.pathString, "-l", $0] }
164+
160165
args += ["-L", self.buildParameters.buildPath.pathString]
161166
args += try ["-o", binaryPath.pathString]
162167
args += ["-module-name", self.product.name.spm_mangledToC99ExtendedIdentifier()]

Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,8 @@ extension LLBuildManifestBuilder {
429429
if target.underlying is BinaryTarget { return }
430430
// Ignore Plugin Targets.
431431
if target.underlying is PluginTarget { return }
432+
// Ignore Provided Libraries.
433+
if target.underlying is ProvidedLibraryTarget { return }
432434

433435
// Depend on the binary for executable targets.
434436
if target.type == .executable && !prepareForIndexing {

Sources/Build/BuildOperation.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
275275

276276
// TODO: Currently this function will only match frameworks.
277277
func detectUnexpressedDependencies(
278-
availableLibraries: [LibraryMetadata],
278+
availableLibraries: [ProvidedLibrary],
279279
targetDependencyMap: [String: [String]]?
280280
) {
281281
// Ensure we only emit these once, regardless of how many builds are being done.
@@ -285,8 +285,8 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
285285
Self.didEmitUnexpressedDependencies = true
286286

287287
let availableFrameworks = Dictionary<String, PackageIdentity>(uniqueKeysWithValues: availableLibraries.compactMap {
288-
if let identity = Set($0.identities.map(\.identity)).spm_only {
289-
return ("\($0.productName!).framework", identity)
288+
if let identity = Set($0.metadata.identities.map(\.identity)).spm_only {
289+
return ("\($0.metadata.productName).framework", identity)
290290
} else {
291291
return nil
292292
}

Sources/Build/BuildPlan/BuildPlan+Product.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ extension BuildPlan {
116116
}
117117
buildProduct.libraryBinaryPaths = dependencies.libraryBinaryPaths
118118

119+
buildProduct.providedLibraries = dependencies.providedLibraries
120+
119121
buildProduct.availableTools = dependencies.availableTools
120122
}
121123

@@ -128,6 +130,7 @@ extension BuildPlan {
128130
staticTargets: [ResolvedModule],
129131
systemModules: [ResolvedModule],
130132
libraryBinaryPaths: Set<AbsolutePath>,
133+
providedLibraries: [String: AbsolutePath],
131134
availableTools: [String: AbsolutePath]
132135
) {
133136
/* Prior to tools-version 5.9, we used to erroneously recursively traverse executable/plugin dependencies and statically include their
@@ -205,6 +208,7 @@ extension BuildPlan {
205208
var staticTargets = [ResolvedModule]()
206209
var systemModules = [ResolvedModule]()
207210
var libraryBinaryPaths: Set<AbsolutePath> = []
211+
var providedLibraries = [String: AbsolutePath]()
208212
var availableTools = [String: AbsolutePath]()
209213

210214
for dependency in allTargets {
@@ -258,6 +262,8 @@ extension BuildPlan {
258262
}
259263
case .plugin:
260264
continue
265+
case .providedLibrary:
266+
providedLibraries[target.name] = target.underlying.path
261267
}
262268

263269
case .product(let product, _):
@@ -275,7 +281,7 @@ extension BuildPlan {
275281
}
276282
}
277283

278-
return (linkLibraries, staticTargets, systemModules, libraryBinaryPaths, availableTools)
284+
return (linkLibraries, staticTargets, systemModules, libraryBinaryPaths, providedLibraries, availableTools)
279285
}
280286

281287
/// Extracts the artifacts from an artifactsArchive

Sources/Build/BuildPlan/BuildPlan+Swift.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import struct Basics.InternalError
1414
import class PackageModel.BinaryTarget
1515
import class PackageModel.ClangTarget
1616
import class PackageModel.SystemLibraryTarget
17+
import class PackageModel.ProvidedLibraryTarget
1718

1819
extension BuildPlan {
1920
func plan(swiftTarget: SwiftTargetBuildDescription) throws {
@@ -48,6 +49,10 @@ extension BuildPlan {
4849
swiftTarget.libraryBinaryPaths.insert(library.libraryPath)
4950
}
5051
}
52+
case let target as ProvidedLibraryTarget:
53+
swiftTarget.additionalFlags += [
54+
"-I", target.path.pathString
55+
]
5156
default:
5257
break
5358
}

Sources/Build/BuildPlan/BuildPlan.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
419419
toolsVersion: toolsVersion,
420420
fileSystem: fileSystem
421421
))
422-
case is SystemLibraryTarget, is BinaryTarget:
422+
case is SystemLibraryTarget, is BinaryTarget, is ProvidedLibraryTarget:
423423
break
424424
default:
425425
throw InternalError("unhandled \(target.underlying)")

Sources/Commands/PackageCommands/EditCommands.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ extension SwiftPackageCommand {
7272
packageName: packageName,
7373
forceRemove: shouldForceRemove,
7474
root: swiftCommandState.getWorkspaceRoot(),
75-
availableLibraries: swiftCommandState.getHostToolchain().providedLibraries,
7675
observabilityScope: swiftCommandState.observabilityScope
7776
)
7877
}

Sources/Commands/PackageCommands/Update.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ extension SwiftPackageCommand {
7171
case .removed:
7272
report += "\n"
7373
report += "- \(package.identity) \(currentVersion)"
74-
case .unchanged:
74+
case .unchanged, .usesLibrary:
7575
continue
7676
}
7777
}

Sources/Commands/Snippets/Cards/TopCard.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ fileprivate extension Target.Kind {
151151
return "snippets"
152152
case .macro:
153153
return "macros"
154+
case .providedLibrary:
155+
return "provided libraries"
154156
}
155157
}
156158
}

Sources/CoreCommands/SwiftCommandState.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,6 @@ public final class SwiftCommandState {
622622
explicitProduct: explicitProduct,
623623
forceResolvedVersions: options.resolver.forceResolvedVersions,
624624
testEntryPointPath: testEntryPointPath,
625-
availableLibraries: self.getHostToolchain().providedLibraries,
626625
observabilityScope: self.observabilityScope
627626
)
628627

Sources/PackageGraph/BoundVersion.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
import struct PackageModel.ProvidedLibrary
1314
import struct TSCUtility.Version
1415

1516
/// A bound version for a package within an assignment.
@@ -22,7 +23,7 @@ public enum BoundVersion: Equatable, Hashable {
2223
case excluded
2324

2425
/// The version of the package to include.
25-
case version(Version)
26+
case version(Version, library: ProvidedLibrary? = nil)
2627

2728
/// The package assignment is unversioned.
2829
case unversioned
@@ -36,7 +37,7 @@ extension BoundVersion: CustomStringConvertible {
3637
switch self {
3738
case .excluded:
3839
return "excluded"
39-
case .version(let version):
40+
case .version(let version, _):
4041
return version.description
4142
case .unversioned:
4243
return "unversioned"

Sources/PackageGraph/ModulesGraph+Loading.swift

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ extension ModulesGraph {
3434
customPlatformsRegistry: PlatformRegistry? = .none,
3535
customXCTestMinimumDeploymentTargets: [PackageModel.Platform: PlatformVersion]? = .none,
3636
testEntryPointPath: AbsolutePath? = nil,
37-
availableLibraries: [LibraryMetadata],
3837
fileSystem: FileSystem,
3938
observabilityScope: ObservabilityScope
4039
) throws -> ModulesGraph {
@@ -172,7 +171,6 @@ extension ModulesGraph {
172171
unsafeAllowedPackages: unsafeAllowedPackages,
173172
platformRegistry: customPlatformsRegistry ?? .default,
174173
platformVersionProvider: platformVersionProvider,
175-
availableLibraries: availableLibraries,
176174
fileSystem: fileSystem,
177175
observabilityScope: observabilityScope
178176
)
@@ -272,7 +270,6 @@ private func createResolvedPackages(
272270
unsafeAllowedPackages: Set<PackageReference>,
273271
platformRegistry: PlatformRegistry,
274272
platformVersionProvider: PlatformVersionProvider,
275-
availableLibraries: [LibraryMetadata],
276273
fileSystem: FileSystem,
277274
observabilityScope: ObservabilityScope
278275
) throws -> IdentifiableSet<ResolvedPackage> {
@@ -551,32 +548,26 @@ private func createResolvedPackages(
551548
t.name != productRef.name
552549
}
553550

554-
let identitiesAvailableInSDK = availableLibraries.flatMap { $0.identities.map { $0.identity } }
555-
// TODO: Do we have to care about "name" vs. identity here?
556-
if let name = productRef.package, identitiesAvailableInSDK.contains(PackageIdentity.plain(name)) {
557-
// Do not emit any diagnostic.
558-
} else {
559-
// Find a product name from the available product dependencies that is most similar to the required product name.
560-
let bestMatchedProductName = bestMatch(for: productRef.name, from: Array(allTargetNames))
561-
var packageContainingBestMatchedProduct: String?
562-
if let bestMatchedProductName, productRef.name == bestMatchedProductName {
563-
let dependentPackages = packageBuilder.dependencies.map(\.package)
564-
for p in dependentPackages where p.targets.contains(where: { $0.name == bestMatchedProductName }) {
565-
packageContainingBestMatchedProduct = p.identity.description
566-
break
567-
}
551+
// Find a product name from the available product dependencies that is most similar to the required product name.
552+
let bestMatchedProductName = bestMatch(for: productRef.name, from: Array(allTargetNames))
553+
var packageContainingBestMatchedProduct: String?
554+
if let bestMatchedProductName, productRef.name == bestMatchedProductName {
555+
let dependentPackages = packageBuilder.dependencies.map(\.package)
556+
for p in dependentPackages where p.targets.contains(where: { $0.name == bestMatchedProductName }) {
557+
packageContainingBestMatchedProduct = p.identity.description
558+
break
568559
}
569-
let error = PackageGraphError.productDependencyNotFound(
570-
package: package.identity.description,
571-
targetName: targetBuilder.target.name,
572-
dependencyProductName: productRef.name,
573-
dependencyPackageName: productRef.package,
574-
dependencyProductInDecl: !declProductsAsDependency.isEmpty,
575-
similarProductName: bestMatchedProductName,
576-
packageContainingSimilarProduct: packageContainingBestMatchedProduct
577-
)
578-
packageObservabilityScope.emit(error)
579560
}
561+
let error = PackageGraphError.productDependencyNotFound(
562+
package: package.identity.description,
563+
targetName: targetBuilder.target.name,
564+
dependencyProductName: productRef.name,
565+
dependencyPackageName: productRef.package,
566+
dependencyProductInDecl: !declProductsAsDependency.isEmpty,
567+
similarProductName: bestMatchedProductName,
568+
packageContainingSimilarProduct: packageContainingBestMatchedProduct
569+
)
570+
packageObservabilityScope.emit(error)
580571
}
581572
continue
582573
}

Sources/PackageGraph/Resolution/PubGrub/ContainerProvider.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ final class ContainerProvider {
9797
) { result in
9898
let result = result.tryMap { container -> PubGrubPackageContainer in
9999
let pubGrubContainer = PubGrubPackageContainer(underlying: container, pins: self.pins)
100+
100101
// only cache positive results
101102
self.containersCache[package] = pubGrubContainer
102103
return pubGrubContainer
@@ -107,12 +108,9 @@ final class ContainerProvider {
107108
}
108109

109110
/// Starts prefetching the given containers.
110-
func prefetch(containers identifiers: [PackageReference], availableLibraries: [LibraryMetadata]) {
111-
let filteredIdentifiers = identifiers.filter {
112-
$0.matchingPrebuiltLibrary(in: availableLibraries) == nil
113-
}
111+
func prefetch(containers identifiers: [PackageReference]) {
114112
// Process each container.
115-
for identifier in filteredIdentifiers {
113+
for identifier in identifiers {
116114
var needsFetching = false
117115
self.prefetches.memoize(identifier) {
118116
let group = DispatchGroup()

0 commit comments

Comments
 (0)