Skip to content
Merged
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
3 changes: 2 additions & 1 deletion Sources/SWBBuildService/BuildDescriptionMessages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ struct BuildDescriptionConfiguredTargetsMsg: MessageHandler {
identifier: ConfiguredTargetIdentifier(rawGUID: configuredTarget.guid.stringValue, targetGUID: TargetGUID(rawValue: configuredTarget.target.guid)),
name: configuredTarget.target.name,
dependencies: Set(dependencyRelationships?.flatMap(\.targetDependencies).compactMap { configuredTargetIdentifiersByGUID[$0.guid] } ?? []),
toolchain: toolchain
toolchain: toolchain,
artifactInfo: buildDescription.artifactInfoPerTarget[configuredTarget]
)
}
return BuildDescriptionConfiguredTargetsResponse(configuredTargets: targetInfos)
Expand Down
26 changes: 26 additions & 0 deletions Sources/SWBCore/SpecImplementations/ProductTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ public class ProductTypeSpec : Spec, SpecType, @unchecked Sendable {
return nil
}

public func artifactInfo(in scope: MacroEvaluationScope) -> ArtifactInfo? {
// Customization point for subclasses
return nil
}

/// Whether this product type supports having compiler sanitizer libraries embedded in it.
public let canEmbedCompilerSanitizerLibraries: Bool

Expand Down Expand Up @@ -529,6 +534,11 @@ public class FrameworkProductTypeSpec : BundleProductTypeSpec, @unchecked Sendab
return descriptors
}

public override func artifactInfo(in scope: MacroEvaluationScope) -> ArtifactInfo? {
let path = scope.evaluate(BuiltinMacros.TARGET_BUILD_DIR).join(scope.evaluate(BuiltinMacros.FULL_PRODUCT_NAME))
return ArtifactInfo(kind: .framework, path: path)
}

/*
/// Build setting expressions to evaluate to determine how to create symbolic links for the product structure.
static let productStructureSymlinkBuildSettings = [SymlinkDescriptor]([
Expand Down Expand Up @@ -815,6 +825,12 @@ public final class DynamicLibraryProductTypeSpec : LibraryProductTypeSpec, @unch
}
return ([], [])
}

public override func artifactInfo(in scope: MacroEvaluationScope) -> ArtifactInfo? {
let path = scope.evaluate(BuiltinMacros.TARGET_BUILD_DIR).join(scope.evaluate(BuiltinMacros.FULL_PRODUCT_NAME))
return ArtifactInfo(kind: .dynamicLibrary, path: path)
}

}

public final class StaticLibraryProductTypeSpec : LibraryProductTypeSpec, @unchecked Sendable {
Expand All @@ -827,12 +843,22 @@ public final class StaticLibraryProductTypeSpec : LibraryProductTypeSpec, @unche
public override var supportsDefinesModule: Bool {
return true
}

public override func artifactInfo(in scope: MacroEvaluationScope) -> ArtifactInfo? {
let path = scope.evaluate(BuiltinMacros.TARGET_BUILD_DIR).join(scope.evaluate(BuiltinMacros.FULL_PRODUCT_NAME))
return ArtifactInfo(kind: .staticLibrary, path: path)
}
}

public final class ToolProductTypeSpec : StandaloneExecutableProductTypeSpec, @unchecked Sendable {
class public override var className: String {
return "PBXToolProductType"
}

public override func artifactInfo(in scope: MacroEvaluationScope) -> ArtifactInfo? {
let path = scope.evaluate(BuiltinMacros.TARGET_BUILD_DIR).join(scope.evaluate(BuiltinMacros.FULL_PRODUCT_NAME))
return ArtifactInfo(kind: .executable, path: path)
}
}

/// Describes a symbolic link to create.
Expand Down
5 changes: 4 additions & 1 deletion Sources/SWBProtocol/BuildDescriptionMessages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,14 @@ public struct BuildDescriptionConfiguredTargetsResponse: Message, SerializableCo
/// `nil` if the toolchain for this target could not be determined due to an error.
public let toolchain: Path?

public init(identifier: ConfiguredTargetIdentifier, name: String, dependencies: Set<ConfiguredTargetIdentifier>, toolchain: Path?) {
public let artifactInfo: ArtifactInfo?

public init(identifier: ConfiguredTargetIdentifier, name: String, dependencies: Set<ConfiguredTargetIdentifier>, toolchain: Path?, artifactInfo: ArtifactInfo?) {
self.identifier = identifier
self.name = name
self.dependencies = dependencies
self.toolchain = toolchain
self.artifactInfo = artifactInfo
}
}

Expand Down
23 changes: 16 additions & 7 deletions Sources/SWBTaskExecution/BuildDescription.swift

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion Sources/SWBTaskExecution/BuildDescriptionManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ package final class BuildDescriptionManager: Sendable {
var settingsPerTarget = [ConfiguredTarget:Settings]()
var rootPathsPerTarget = [ConfiguredTarget:[Path]]()
var moduleCachePathsPerTarget = [ConfiguredTarget: [Path]]()
var artifactInfoPerTarget = [ConfiguredTarget: ArtifactInfo]()

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

artifactInfoPerTarget[target] = settings.productType?.artifactInfo(in: settings.globalScope)

if shouldValidateCAS, settings.globalScope.evaluate(BuiltinMacros.CLANG_ENABLE_COMPILE_CACHE) || settings.globalScope.evaluate(BuiltinMacros.SWIFT_ENABLE_COMPILE_CACHE) {
// FIXME: currently we only handle the compiler cache here, because the plugin configuration for the generic CAS is not configured by build settings.
for purpose in [CASOptions.Purpose.compiler(.c)] {
Expand Down Expand Up @@ -249,7 +252,7 @@ package final class BuildDescriptionManager: Sendable {
}

// Create the build description.
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)
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)
}

/// 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.
Expand Down
4 changes: 2 additions & 2 deletions Sources/SWBTestSupport/TaskExecutionTestSupport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ package struct TestManifest: Sendable {

extension BuildDescription {
/// Convenience testing method which omits the `capturedBuildInfo:` parameter.
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? {
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)
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? {
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)
}
}

Expand Down
29 changes: 29 additions & 0 deletions Sources/SWBUtil/ArtifactInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 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
//
//===----------------------------------------------------------------------===//

///. Describes high-level information about an artifact produced by a particular ConfiguredTarget, suitable for consumption by build tools like SwiftPM command plugins.
public struct ArtifactInfo: Equatable, Hashable, Sendable, SerializableCodable {
public enum Kind: Equatable, Hashable, Sendable, SerializableCodable {
case executable
case staticLibrary
case dynamicLibrary
case framework
}

public let kind: Kind
public let path: Path

public init(kind: Kind, path: Path) {
self.kind = kind
self.path = path
}
}
1 change: 1 addition & 0 deletions Sources/SWBUtil/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ add_library(SWBUtil
Architecture.swift
ArgumentSplitting.swift
Array.swift
ArtifactInfo.swift
AsyncCache.swift
AsyncFlatteningSequence.swift
AsyncIteratorProtocol.swift
Expand Down
5 changes: 5 additions & 0 deletions Sources/SwiftBuild/SWBBuildServiceSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ public final class SWBBuildServiceSession: Sendable {
try await createBuildOperation(request: request, delegate: delegate, onlyCreateBuildDescription: false, retainBuildDescription: false)
}

@_disfavoredOverload
public func createBuildOperation(request: SWBBuildRequest, delegate: any SWBPlanningOperationDelegate, retainBuildDescription: Bool) async throws -> SWBBuildOperation {
try await createBuildOperation(request: request, delegate: delegate, onlyCreateBuildDescription: false, retainBuildDescription: retainBuildDescription)
}

@_disfavoredOverload
public func createBuildOperationForBuildDescriptionOnly(request: SWBBuildRequest, delegate: any SWBPlanningOperationDelegate) async throws -> SWBBuildOperation {
try await createBuildOperation(request: request, delegate: delegate, onlyCreateBuildDescription: true, retainBuildDescription: false)
Expand Down
36 changes: 33 additions & 3 deletions Sources/SwiftBuild/SWBConfiguredTargetInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,34 @@
//===----------------------------------------------------------------------===//

import SWBProtocol
import SWBUtil

public struct SWBConfiguredTargetInfo {
public struct SWBArtifactInfo: Sendable {
public enum Kind: Sendable {
case executable
case staticLibrary
case dynamicLibrary
case framework
}
public var kind: Kind
public var path: String

init(_ info: ArtifactInfo) {
switch info.kind {
case .executable:
self.kind = .executable
case .staticLibrary:
self.kind = .staticLibrary
case .dynamicLibrary:
self.kind = .dynamicLibrary
case .framework:
self.kind = .framework
}
self.path = info.path.str
}
}

public struct SWBConfiguredTargetInfo: Sendable {
/// The GUID of this configured target
public let identifier: SWBConfiguredTargetIdentifier

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

public init(identifier: SWBConfiguredTargetIdentifier, name: String, dependencies: Set<SWBConfiguredTargetIdentifier>, toolchain: AbsolutePath?) {
public let artifactInfo: SWBArtifactInfo?

public init(identifier: SWBConfiguredTargetIdentifier, name: String, dependencies: Set<SWBConfiguredTargetIdentifier>, toolchain: AbsolutePath?, artifactInfo: SWBArtifactInfo?) {
self.identifier = identifier
self.name = name
self.dependencies = dependencies
self.toolchain = toolchain
self.artifactInfo = artifactInfo
}

init(_ configuredTargetInfo: BuildDescriptionConfiguredTargetsResponse.ConfiguredTargetInfo) {
self.init(
identifier: SWBConfiguredTargetIdentifier(configuredTargetIdentifier: configuredTargetInfo.identifier),
name: configuredTargetInfo.name,
dependencies: Set(configuredTargetInfo.dependencies.map { SWBConfiguredTargetIdentifier(configuredTargetIdentifier: $0) }),
toolchain: AbsolutePath(configuredTargetInfo.toolchain)
toolchain: AbsolutePath(configuredTargetInfo.toolchain),
artifactInfo: configuredTargetInfo.artifactInfo.map { SWBArtifactInfo($0) }
)
}
}
Loading
Loading