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
27 changes: 27 additions & 0 deletions Fixtures/PartiallyUnusedDependency/Dep/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// swift-tools-version: 6.0

import PackageDescription

let package = Package(
name: "Dep",
products: [
.library(
name: "MyDynamicLibrary",
type: .dynamic,
targets: ["MyDynamicLibrary"]
),
.executable(
name: "MySupportExecutable",
targets: ["MySupportExecutable"]
)
],
targets: [
.target(
name: "MyDynamicLibrary"
),
.executableTarget(
name: "MySupportExecutable",
dependencies: ["MyDynamicLibrary"]
)
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public func sayHello() {
print("hello!")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import MyDynamicLibrary

@main struct Entry {
static func main() {
print("running support tool")
sayHello()
}
}
29 changes: 29 additions & 0 deletions Fixtures/PartiallyUnusedDependency/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// swift-tools-version: 6.0

import PackageDescription

let package = Package(
name: "PartiallyUnusedDependency",
products: [
.executable(
name: "MyExecutable",
targets: ["MyExecutable"]
),
],
dependencies: [
.package(path: "Dep")
],
targets: [
.executableTarget(
name: "MyExecutable",
dependencies: [.product(name: "MyDynamicLibrary", package: "Dep")]
),
.plugin(
name: "dump-artifacts-plugin",
capability: .command(
intent: .custom(verb: "dump-artifacts-plugin", description: "Dump Artifacts"),
permissions: []
)
)
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import PackagePlugin

@main
struct DumpArtifactsPlugin: CommandPlugin {
func performCommand(
context: PluginContext,
arguments: [String]
) throws {
do {
var parameters = PackageManager.BuildParameters()
parameters.configuration = .debug
parameters.logging = .concise
let result = try packageManager.build(.all(includingTests: false), parameters: parameters)
print("succeeded: \(result.succeeded)")
for artifact in result.builtArtifacts {
print("artifact-path: \(artifact.path.string)")
print("artifact-kind: \(artifact.kind)")
}
}
catch {
print("error from the plugin host: \\(error)")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import MyDynamicLibrary

@main struct Entry {
static func main() {
print("Hello, world!")
sayHello()
}
}
26 changes: 26 additions & 0 deletions Sources/Build/BuildOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -461,11 +461,37 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
let buildResultBuildPlan = buildOutputs.contains(.buildPlan) ? try buildPlan : nil
let buildResultReplArgs = buildOutputs.contains(.replArguments) ? try buildPlan.createREPLArguments() : nil

let artifacts: [(String, PluginInvocationBuildResult.BuiltArtifact)]?
if buildOutputs.contains(.builtArtifacts) {
let builtProducts = try buildPlan.buildProducts
artifacts = try builtProducts.compactMap {
switch $0.product.type {
case .library(let kind):
let artifactKind: PluginInvocationBuildResult.BuiltArtifact.Kind
switch kind {
case .dynamic: artifactKind = .dynamicLibrary
case .static, .automatic: artifactKind = .staticLibrary
}
return try ($0.product.name, .init(
path: $0.binaryPath.pathString,
kind: artifactKind)
)
case .executable:
return try ($0.product.name, .init(path: $0.binaryPath.pathString, kind: .executable))
default:
return nil
}
}
} else {
artifacts = nil
}

result = BuildResult(
serializedDiagnosticPathsByTargetName: result.serializedDiagnosticPathsByTargetName,
symbolGraph: result.symbolGraph,
buildPlan: buildResultBuildPlan,
replArguments: buildResultReplArgs,
builtArtifacts: artifacts
)
var serializedDiagnosticPaths: [String: [AbsolutePath]] = [:]
do {
Expand Down
50 changes: 15 additions & 35 deletions Sources/Commands/Utilities/PluginDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -178,40 +178,21 @@ final class PluginDelegate: PluginInvocationDelegate {
)

// Run the build. This doesn't return until the build is complete.
let success = await buildSystem.buildIgnoringError(subset: buildSubset)
let result = await buildSystem.buildIgnoringError(subset: buildSubset, buildOutputs: [.builtArtifacts])
let success = result != nil

let packageGraph = try await buildSystem.getPackageGraph()

var builtArtifacts: [PluginInvocationBuildResult.BuiltArtifact] = []

for rootPkg in packageGraph.rootPackages {
let builtProducts = rootPkg.products.filter {
switch subset {
case .all(let includingTests):
return includingTests ? true : $0.type != .test
case .product(let name):
return $0.name == name
case .target(let name):
return $0.name == name
}
}

let artifacts: [PluginInvocationBuildResult.BuiltArtifact] = try builtProducts.compactMap {
switch $0.type {
case .library(let kind):
return .init(
path: try buildParameters.binaryPath(for: $0).pathString,
kind: (kind == .dynamic) ? .dynamicLibrary : .staticLibrary
)
case .executable:
return .init(path: try buildParameters.binaryPath(for: $0).pathString, kind: .executable)
default:
return nil
}
var builtArtifacts: [PluginInvocationBuildResult.BuiltArtifact] = (result?.builtArtifacts ?? []).filter { (name, _) in
switch subset {
case .all(let includingTests):
return true
case .product(let productName):
return name == productName
case .target(let targetName):
return name == targetName
}

builtArtifacts.append(contentsOf: artifacts)
}
}.map(\.1)

return PluginInvocationBuildResult(
succeeded: success,
Expand Down Expand Up @@ -495,12 +476,11 @@ final class PluginDelegate: PluginInvocationDelegate {
}

extension BuildSystem {
fileprivate func buildIgnoringError(subset: BuildSubset) async -> Bool {
fileprivate func buildIgnoringError(subset: BuildSubset, buildOutputs: [BuildOutput]) async -> BuildResult? {
do {
try await self.build(subset: subset, buildOutputs: [])
return true
return try await self.build(subset: subset, buildOutputs: buildOutputs)
} catch {
return false
return nil
}
}
}
Expand Down Expand Up @@ -533,4 +513,4 @@ fileprivate extension BuildOutput.SymbolGraphAccessLevel {
.open
}
}
}
}
32 changes: 5 additions & 27 deletions Sources/SPMBuildCore/BuildSystem/BuildSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,32 +39,6 @@ public enum BuildSubset {
/// build systems can produce all possible build outputs. Check the build
/// result for indication that the output was produced.
public enum BuildOutput: Equatable {
public static func == (lhs: BuildOutput, rhs: BuildOutput) -> Bool {
switch lhs {
case .symbolGraph(let leftOptions):
switch rhs {
case .symbolGraph(let rightOptions):
return leftOptions == rightOptions
default:
return false
}
case .buildPlan:
switch rhs {
case .buildPlan:
return true
default:
return false
}
case .replArguments:
switch rhs {
case .replArguments:
return true
default:
return false
}
}
}

public enum SymbolGraphAccessLevel: String {
case `private`, `fileprivate`, `internal`, `package`, `public`, `open`
}
Expand Down Expand Up @@ -93,6 +67,7 @@ public enum BuildOutput: Equatable {
case symbolGraph(SymbolGraphOptions)
case buildPlan
case replArguments
case builtArtifacts
}

/// A protocol that represents a build system used by SwiftPM for all build operations. This allows factoring out the
Expand Down Expand Up @@ -144,19 +119,22 @@ public struct BuildResult {
serializedDiagnosticPathsByTargetName: Result<[String: [AbsolutePath]], Error>,
symbolGraph: SymbolGraphResult? = nil,
buildPlan: BuildPlan? = nil,
replArguments: CLIArguments?
replArguments: CLIArguments?,
builtArtifacts: [(String, PluginInvocationBuildResult.BuiltArtifact)]? = nil
) {
self.serializedDiagnosticPathsByTargetName = serializedDiagnosticPathsByTargetName
self.symbolGraph = symbolGraph
self.buildPlan = buildPlan
self.replArguments = replArguments
self.builtArtifacts = builtArtifacts
}

public let replArguments: CLIArguments?
public let symbolGraph: SymbolGraphResult?
public let buildPlan: BuildPlan?

public var serializedDiagnosticPathsByTargetName: Result<[String: [AbsolutePath]], Error>
public var builtArtifacts: [(String, PluginInvocationBuildResult.BuiltArtifact)]?
}

public protocol ProductBuildDescription {
Expand Down
Loading