Skip to content

Commit 7c3e3ec

Browse files
authored
Merge pull request #801 from owenv/owenv/artifact-api
Add API to query information about artifacts produced by configured targets
2 parents 7d824ea + b88bbbc commit 7c3e3ec

File tree

12 files changed

+218
-17
lines changed

12 files changed

+218
-17
lines changed

Sources/SWBBuildService/BuildDescriptionMessages.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ struct BuildDescriptionConfiguredTargetsMsg: MessageHandler {
125125
identifier: ConfiguredTargetIdentifier(rawGUID: configuredTarget.guid.stringValue, targetGUID: TargetGUID(rawValue: configuredTarget.target.guid)),
126126
name: configuredTarget.target.name,
127127
dependencies: Set(dependencyRelationships?.flatMap(\.targetDependencies).compactMap { configuredTargetIdentifiersByGUID[$0.guid] } ?? []),
128-
toolchain: toolchain
128+
toolchain: toolchain,
129+
artifactInfo: buildDescription.artifactInfoPerTarget[configuredTarget]
129130
)
130131
}
131132
return BuildDescriptionConfiguredTargetsResponse(configuredTargets: targetInfos)

Sources/SWBCore/SpecImplementations/ProductTypes.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ public class ProductTypeSpec : Spec, SpecType, @unchecked Sendable {
6969
return nil
7070
}
7171

72+
public func artifactInfo(in scope: MacroEvaluationScope) -> ArtifactInfo? {
73+
// Customization point for subclasses
74+
return nil
75+
}
76+
7277
/// Whether this product type supports having compiler sanitizer libraries embedded in it.
7378
public let canEmbedCompilerSanitizerLibraries: Bool
7479

@@ -529,6 +534,11 @@ public class FrameworkProductTypeSpec : BundleProductTypeSpec, @unchecked Sendab
529534
return descriptors
530535
}
531536

537+
public override func artifactInfo(in scope: MacroEvaluationScope) -> ArtifactInfo? {
538+
let path = scope.evaluate(BuiltinMacros.TARGET_BUILD_DIR).join(scope.evaluate(BuiltinMacros.FULL_PRODUCT_NAME))
539+
return ArtifactInfo(kind: .framework, path: path)
540+
}
541+
532542
/*
533543
/// Build setting expressions to evaluate to determine how to create symbolic links for the product structure.
534544
static let productStructureSymlinkBuildSettings = [SymlinkDescriptor]([
@@ -815,6 +825,12 @@ public final class DynamicLibraryProductTypeSpec : LibraryProductTypeSpec, @unch
815825
}
816826
return ([], [])
817827
}
828+
829+
public override func artifactInfo(in scope: MacroEvaluationScope) -> ArtifactInfo? {
830+
let path = scope.evaluate(BuiltinMacros.TARGET_BUILD_DIR).join(scope.evaluate(BuiltinMacros.FULL_PRODUCT_NAME))
831+
return ArtifactInfo(kind: .dynamicLibrary, path: path)
832+
}
833+
818834
}
819835

820836
public final class StaticLibraryProductTypeSpec : LibraryProductTypeSpec, @unchecked Sendable {
@@ -827,12 +843,22 @@ public final class StaticLibraryProductTypeSpec : LibraryProductTypeSpec, @unche
827843
public override var supportsDefinesModule: Bool {
828844
return true
829845
}
846+
847+
public override func artifactInfo(in scope: MacroEvaluationScope) -> ArtifactInfo? {
848+
let path = scope.evaluate(BuiltinMacros.TARGET_BUILD_DIR).join(scope.evaluate(BuiltinMacros.FULL_PRODUCT_NAME))
849+
return ArtifactInfo(kind: .staticLibrary, path: path)
850+
}
830851
}
831852

832853
public final class ToolProductTypeSpec : StandaloneExecutableProductTypeSpec, @unchecked Sendable {
833854
class public override var className: String {
834855
return "PBXToolProductType"
835856
}
857+
858+
public override func artifactInfo(in scope: MacroEvaluationScope) -> ArtifactInfo? {
859+
let path = scope.evaluate(BuiltinMacros.TARGET_BUILD_DIR).join(scope.evaluate(BuiltinMacros.FULL_PRODUCT_NAME))
860+
return ArtifactInfo(kind: .executable, path: path)
861+
}
836862
}
837863

838864
/// Describes a symbolic link to create.

Sources/SWBProtocol/BuildDescriptionMessages.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,14 @@ public struct BuildDescriptionConfiguredTargetsResponse: Message, SerializableCo
7676
/// `nil` if the toolchain for this target could not be determined due to an error.
7777
public let toolchain: Path?
7878

79-
public init(identifier: ConfiguredTargetIdentifier, name: String, dependencies: Set<ConfiguredTargetIdentifier>, toolchain: Path?) {
79+
public let artifactInfo: ArtifactInfo?
80+
81+
public init(identifier: ConfiguredTargetIdentifier, name: String, dependencies: Set<ConfiguredTargetIdentifier>, toolchain: Path?, artifactInfo: ArtifactInfo?) {
8082
self.identifier = identifier
8183
self.name = name
8284
self.dependencies = dependencies
8385
self.toolchain = toolchain
86+
self.artifactInfo = artifactInfo
8487
}
8588
}
8689

Sources/SWBTaskExecution/BuildDescription.swift

Lines changed: 16 additions & 7 deletions
Large diffs are not rendered by default.

Sources/SWBTaskExecution/BuildDescriptionManager.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ package final class BuildDescriptionManager: Sendable {
183183
var settingsPerTarget = [ConfiguredTarget:Settings]()
184184
var rootPathsPerTarget = [ConfiguredTarget:[Path]]()
185185
var moduleCachePathsPerTarget = [ConfiguredTarget: [Path]]()
186+
var artifactInfoPerTarget = [ConfiguredTarget: ArtifactInfo]()
186187

187188
var casValidationInfos: OrderedSet<BuildDescription.CASValidationInfo> = []
188189
let buildGraph = planRequest.buildGraph
@@ -205,6 +206,8 @@ package final class BuildDescriptionManager: Sendable {
205206
settings.globalScope.evaluate(BuiltinMacros.CLANG_EXPLICIT_MODULES_OUTPUT_PATH),
206207
]
207208

209+
artifactInfoPerTarget[target] = settings.productType?.artifactInfo(in: settings.globalScope)
210+
208211
if shouldValidateCAS, settings.globalScope.evaluate(BuiltinMacros.CLANG_ENABLE_COMPILE_CACHE) || settings.globalScope.evaluate(BuiltinMacros.SWIFT_ENABLE_COMPILE_CACHE) {
209212
// FIXME: currently we only handle the compiler cache here, because the plugin configuration for the generic CAS is not configured by build settings.
210213
for purpose in [CASOptions.Purpose.compiler(.c)] {
@@ -249,7 +252,7 @@ package final class BuildDescriptionManager: Sendable {
249252
}
250253

251254
// Create the build description.
252-
return try await BuildDescription.construct(workspace: buildGraph.workspaceContext.workspace, tasks: plan.tasks, path: path, signature: signature, buildCommand: planRequest.buildRequest.buildCommand, diagnostics: planningDiagnostics, indexingInfo: [], fs: fs, bypassActualTasks: bypassActualTasks, targetsBuildInParallel: buildGraph.targetsBuildInParallel, emitFrontendCommandLines: plan.emitFrontendCommandLines, moduleSessionFilePath: planRequest.workspaceContext.getModuleSessionFilePath(planRequest.buildRequest.parameters), invalidationPaths: plan.invalidationPaths, recursiveSearchPathResults: plan.recursiveSearchPathResults, copiedPathMap: plan.copiedPathMap, rootPathsPerTarget: rootPathsPerTarget, moduleCachePathsPerTarget: moduleCachePathsPerTarget, casValidationInfos: casValidationInfos.elements, staleFileRemovalIdentifierPerTarget: staleFileRemovalIdentifierPerTarget, settingsPerTarget: settingsPerTarget, delegate: delegate, targetDependencies: buildGraph.targetDependenciesByGuid, definingTargetsByModuleName: definingTargetsByModuleName, capturedBuildInfo: capturedBuildInfo, userPreferences: buildGraph.workspaceContext.userPreferences)
255+
return try await BuildDescription.construct(workspace: buildGraph.workspaceContext.workspace, tasks: plan.tasks, path: path, signature: signature, buildCommand: planRequest.buildRequest.buildCommand, diagnostics: planningDiagnostics, indexingInfo: [], fs: fs, bypassActualTasks: bypassActualTasks, targetsBuildInParallel: buildGraph.targetsBuildInParallel, emitFrontendCommandLines: plan.emitFrontendCommandLines, moduleSessionFilePath: planRequest.workspaceContext.getModuleSessionFilePath(planRequest.buildRequest.parameters), invalidationPaths: plan.invalidationPaths, recursiveSearchPathResults: plan.recursiveSearchPathResults, copiedPathMap: plan.copiedPathMap, rootPathsPerTarget: rootPathsPerTarget, moduleCachePathsPerTarget: moduleCachePathsPerTarget, artifactInfoPerTarget: artifactInfoPerTarget, casValidationInfos: casValidationInfos.elements, staleFileRemovalIdentifierPerTarget: staleFileRemovalIdentifierPerTarget, settingsPerTarget: settingsPerTarget, delegate: delegate, targetDependencies: buildGraph.targetDependenciesByGuid, definingTargetsByModuleName: definingTargetsByModuleName, capturedBuildInfo: capturedBuildInfo, userPreferences: buildGraph.workspaceContext.userPreferences)
253256
}
254257

255258
/// Encapsulates the two ways `getNewOrCachedBuildDescription` can be called, whether we want to retrieve or create a build description based on a plan or whether we have an explicit build description ID that we want to retrieve and we don't need to create a new one.

Sources/SWBTestSupport/TaskExecutionTestSupport.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ package struct TestManifest: Sendable {
9494

9595
extension BuildDescription {
9696
/// Convenience testing method which omits the `capturedBuildInfo:` parameter.
97-
static package func construct(workspace: Workspace, tasks: [any PlannedTask], path: Path, signature: BuildDescriptionSignature, buildCommand: BuildCommand, diagnostics: [ConfiguredTarget?: [Diagnostic]] = [:], indexingInfo: [(forTarget: ConfiguredTarget?, path: Path, indexingInfo: any SourceFileIndexingInfo)] = [], fs: any FSProxy = localFS, bypassActualTasks: Bool = false, moduleSessionFilePath: Path? = nil, invalidationPaths: [Path] = [], recursiveSearchPathResults: [RecursiveSearchPathResolver.CachedResult] = [], copiedPathMap: [String: String] = [:], rootPathsPerTarget: [ConfiguredTarget:[Path]] = [:], moduleCachePathsPerTarget: [ConfiguredTarget: [Path]] = [:], casValidationInfos: [BuildDescription.CASValidationInfo] = [], staleFileRemovalIdentifierPerTarget: [ConfiguredTarget: String] = [:], settingsPerTarget: [ConfiguredTarget: Settings] = [:], delegate: any BuildDescriptionConstructionDelegate, targetDependencies: [TargetDependencyRelationship] = [], definingTargetsByModuleName: [String: OrderedSet<ConfiguredTarget>] = [:]) async throws -> BuildDescription? {
98-
return try await construct(workspace: workspace, tasks: tasks, path: path, signature: signature, buildCommand: buildCommand, diagnostics: diagnostics, indexingInfo: indexingInfo, fs: fs, bypassActualTasks: bypassActualTasks, moduleSessionFilePath: moduleSessionFilePath, invalidationPaths: invalidationPaths, recursiveSearchPathResults: recursiveSearchPathResults, copiedPathMap: copiedPathMap, rootPathsPerTarget: rootPathsPerTarget, moduleCachePathsPerTarget: moduleCachePathsPerTarget, casValidationInfos: casValidationInfos, staleFileRemovalIdentifierPerTarget: staleFileRemovalIdentifierPerTarget, settingsPerTarget: settingsPerTarget, delegate: delegate, targetDependencies: targetDependencies, definingTargetsByModuleName: definingTargetsByModuleName, capturedBuildInfo: nil, userPreferences: .defaultForTesting)
97+
static package func construct(workspace: Workspace, tasks: [any PlannedTask], path: Path, signature: BuildDescriptionSignature, buildCommand: BuildCommand, diagnostics: [ConfiguredTarget?: [Diagnostic]] = [:], indexingInfo: [(forTarget: ConfiguredTarget?, path: Path, indexingInfo: any SourceFileIndexingInfo)] = [], fs: any FSProxy = localFS, bypassActualTasks: Bool = false, moduleSessionFilePath: Path? = nil, invalidationPaths: [Path] = [], recursiveSearchPathResults: [RecursiveSearchPathResolver.CachedResult] = [], copiedPathMap: [String: String] = [:], rootPathsPerTarget: [ConfiguredTarget:[Path]] = [:], moduleCachePathsPerTarget: [ConfiguredTarget: [Path]] = [:], artifactInfoPerTarget: [ConfiguredTarget: ArtifactInfo] = [:], casValidationInfos: [BuildDescription.CASValidationInfo] = [], staleFileRemovalIdentifierPerTarget: [ConfiguredTarget: String] = [:], settingsPerTarget: [ConfiguredTarget: Settings] = [:], delegate: any BuildDescriptionConstructionDelegate, targetDependencies: [TargetDependencyRelationship] = [], definingTargetsByModuleName: [String: OrderedSet<ConfiguredTarget>] = [:]) async throws -> BuildDescription? {
98+
return try await construct(workspace: workspace, tasks: tasks, path: path, signature: signature, buildCommand: buildCommand, diagnostics: diagnostics, indexingInfo: indexingInfo, fs: fs, bypassActualTasks: bypassActualTasks, moduleSessionFilePath: moduleSessionFilePath, invalidationPaths: invalidationPaths, recursiveSearchPathResults: recursiveSearchPathResults, copiedPathMap: copiedPathMap, rootPathsPerTarget: rootPathsPerTarget, moduleCachePathsPerTarget: moduleCachePathsPerTarget, artifactInfoPerTarget: artifactInfoPerTarget, casValidationInfos: casValidationInfos, staleFileRemovalIdentifierPerTarget: staleFileRemovalIdentifierPerTarget, settingsPerTarget: settingsPerTarget, delegate: delegate, targetDependencies: targetDependencies, definingTargetsByModuleName: definingTargetsByModuleName, capturedBuildInfo: nil, userPreferences: .defaultForTesting)
9999
}
100100
}
101101

Sources/SWBUtil/ArtifactInfo.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
///. Describes high-level information about an artifact produced by a particular ConfiguredTarget, suitable for consumption by build tools like SwiftPM command plugins.
14+
public struct ArtifactInfo: Equatable, Hashable, Sendable, SerializableCodable {
15+
public enum Kind: Equatable, Hashable, Sendable, SerializableCodable {
16+
case executable
17+
case staticLibrary
18+
case dynamicLibrary
19+
case framework
20+
}
21+
22+
public let kind: Kind
23+
public let path: Path
24+
25+
public init(kind: Kind, path: Path) {
26+
self.kind = kind
27+
self.path = path
28+
}
29+
}

Sources/SWBUtil/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ add_library(SWBUtil
1212
Architecture.swift
1313
ArgumentSplitting.swift
1414
Array.swift
15+
ArtifactInfo.swift
1516
AsyncCache.swift
1617
AsyncFlatteningSequence.swift
1718
AsyncIteratorProtocol.swift

Sources/SwiftBuild/SWBBuildServiceSession.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,11 @@ public final class SWBBuildServiceSession: Sendable {
181181
try await createBuildOperation(request: request, delegate: delegate, onlyCreateBuildDescription: false, retainBuildDescription: false)
182182
}
183183

184+
@_disfavoredOverload
185+
public func createBuildOperation(request: SWBBuildRequest, delegate: any SWBPlanningOperationDelegate, retainBuildDescription: Bool) async throws -> SWBBuildOperation {
186+
try await createBuildOperation(request: request, delegate: delegate, onlyCreateBuildDescription: false, retainBuildDescription: retainBuildDescription)
187+
}
188+
184189
@_disfavoredOverload
185190
public func createBuildOperationForBuildDescriptionOnly(request: SWBBuildRequest, delegate: any SWBPlanningOperationDelegate) async throws -> SWBBuildOperation {
186191
try await createBuildOperation(request: request, delegate: delegate, onlyCreateBuildDescription: true, retainBuildDescription: false)

Sources/SwiftBuild/SWBConfiguredTargetInfo.swift

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,34 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
import SWBProtocol
14+
import SWBUtil
1415

15-
public struct SWBConfiguredTargetInfo {
16+
public struct SWBArtifactInfo: Sendable {
17+
public enum Kind: Sendable {
18+
case executable
19+
case staticLibrary
20+
case dynamicLibrary
21+
case framework
22+
}
23+
public var kind: Kind
24+
public var path: String
25+
26+
init(_ info: ArtifactInfo) {
27+
switch info.kind {
28+
case .executable:
29+
self.kind = .executable
30+
case .staticLibrary:
31+
self.kind = .staticLibrary
32+
case .dynamicLibrary:
33+
self.kind = .dynamicLibrary
34+
case .framework:
35+
self.kind = .framework
36+
}
37+
self.path = info.path.str
38+
}
39+
}
40+
41+
public struct SWBConfiguredTargetInfo: Sendable {
1642
/// The GUID of this configured target
1743
public let identifier: SWBConfiguredTargetIdentifier
1844

@@ -27,19 +53,23 @@ public struct SWBConfiguredTargetInfo {
2753
/// `nil` if the toolchain for this target could not be determined due to an error.
2854
public let toolchain: AbsolutePath?
2955

30-
public init(identifier: SWBConfiguredTargetIdentifier, name: String, dependencies: Set<SWBConfiguredTargetIdentifier>, toolchain: AbsolutePath?) {
56+
public let artifactInfo: SWBArtifactInfo?
57+
58+
public init(identifier: SWBConfiguredTargetIdentifier, name: String, dependencies: Set<SWBConfiguredTargetIdentifier>, toolchain: AbsolutePath?, artifactInfo: SWBArtifactInfo?) {
3159
self.identifier = identifier
3260
self.name = name
3361
self.dependencies = dependencies
3462
self.toolchain = toolchain
63+
self.artifactInfo = artifactInfo
3564
}
3665

3766
init(_ configuredTargetInfo: BuildDescriptionConfiguredTargetsResponse.ConfiguredTargetInfo) {
3867
self.init(
3968
identifier: SWBConfiguredTargetIdentifier(configuredTargetIdentifier: configuredTargetInfo.identifier),
4069
name: configuredTargetInfo.name,
4170
dependencies: Set(configuredTargetInfo.dependencies.map { SWBConfiguredTargetIdentifier(configuredTargetIdentifier: $0) }),
42-
toolchain: AbsolutePath(configuredTargetInfo.toolchain)
71+
toolchain: AbsolutePath(configuredTargetInfo.toolchain),
72+
artifactInfo: configuredTargetInfo.artifactInfo.map { SWBArtifactInfo($0) }
4373
)
4474
}
4575
}

0 commit comments

Comments
 (0)