Skip to content

Commit e118398

Browse files
fix linker flags for dynamic libraries when executing command plugin (#9091)
Add appropriate `-tool` suffix to linker flags in dynamic libraries when executing `swift package command_plugin`. ### Motivation: Resolves the following issue: #9062 ### Modifications: Add appropriate `-tool` suffix to linker flags in dynamic libraries when executing `swift package command_plugin`. ### Result: Executing `swift package command_plugin` with dynamic dependencies works.
1 parent 96d689a commit e118398

File tree

2 files changed

+172
-1
lines changed

2 files changed

+172
-1
lines changed

Sources/Build/BuildDescription/ProductBuildDescription.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ public final class ProductBuildDescription: SPMBuildCore.ProductBuildDescription
167167
args += ["-L", self.buildParameters.buildPath.pathString]
168168
args += try ["-o", binaryPath.pathString]
169169
args += ["-module-name", self.product.name.spm_mangledToC99ExtendedIdentifier()]
170-
args += self.dylibs.map { "-l" + $0.product.name }
170+
args += self.dylibs.map { "-l" + $0.product.name + $0.buildParameters.suffix }
171171

172172
// Add arguments needed for code coverage if it is enabled.
173173
if self.buildParameters.testingParameters.enableCodeCoverage {

Tests/CommandsTests/PackageCommandTests.swift

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7134,5 +7134,176 @@ struct PackageCommandTests {
71347134
expectNoDiagnostics(observability.diagnostics)
71357135
}
71367136
}
7137+
7138+
@Test(arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms))
7139+
func commandPluginDynamicDependencies(
7140+
buildData: BuildData
7141+
) async throws {
7142+
try await withKnownIssue {
7143+
try await testWithTemporaryDirectory { tmpPath in
7144+
// Create a sample package with a command plugin that has a dynamic dependency.
7145+
let packageDir = tmpPath.appending(components: "MyPackage")
7146+
try localFileSystem.writeFileContents(
7147+
packageDir.appending(components: "Package.swift"),
7148+
string:
7149+
"""
7150+
// swift-tools-version: 6.0
7151+
// The swift-tools-version declares the minimum version of Swift required to build this package.
7152+
7153+
import PackageDescription
7154+
7155+
let package = Package(
7156+
name: "command-plugin-dynamic-linking",
7157+
products: [
7158+
// Products can be used to vend plugins, making them visible to other packages.
7159+
.plugin(
7160+
name: "command-plugin-dynamic-linking",
7161+
targets: ["command-plugin-dynamic-linking"]),
7162+
],
7163+
dependencies: [
7164+
.package(path: "LocalPackages/DynamicLib")
7165+
],
7166+
targets: [
7167+
// Targets are the basic building blocks of a package, defining a module or a test suite.
7168+
// Targets can depend on other targets in this package and products from dependencies.
7169+
.executableTarget(
7170+
name: "Core",
7171+
dependencies: [
7172+
.product(name: "DynamicLib", package: "DynamicLib")
7173+
]
7174+
),
7175+
.plugin(
7176+
name: "command-plugin-dynamic-linking",
7177+
capability: .command(intent: .custom(
7178+
verb: "command_plugin_dynamic_linking",
7179+
description: "prints hello world"
7180+
)),
7181+
dependencies: [
7182+
"Core"
7183+
]
7184+
)
7185+
]
7186+
)
7187+
"""
7188+
)
7189+
try localFileSystem.writeFileContents(
7190+
packageDir.appending(components: "Sources", "Core", "Core.swift"),
7191+
string:
7192+
"""
7193+
import DynamicLib
7194+
7195+
@main
7196+
struct Core {
7197+
static func main() {
7198+
let result = dynamicLibFunc()
7199+
print(result)
7200+
}
7201+
}
7202+
"""
7203+
)
7204+
try localFileSystem.writeFileContents(
7205+
packageDir.appending(components: "Plugins", "command-plugin-dynamic-linking.swift"),
7206+
string:
7207+
"""
7208+
import PackagePlugin
7209+
import Foundation
7210+
7211+
enum CommandError: Error, CustomStringConvertible {
7212+
var description: String {
7213+
String(describing: self)
7214+
}
7215+
7216+
case pluginError(String)
7217+
}
7218+
7219+
@main
7220+
struct command_plugin_dynamic_linking: CommandPlugin {
7221+
// Entry point for command plugins applied to Swift Packages.
7222+
func performCommand(context: PluginContext, arguments: [String]) async throws {
7223+
let tool = try context.tool(named: "Core")
7224+
7225+
let process = try Process.run(tool.url, arguments: arguments)
7226+
process.waitUntilExit()
7227+
7228+
if process.terminationReason != .exit || process.terminationStatus != 0 {
7229+
throw CommandError.pluginError("\\(tool.name) failed")
7230+
} else {
7231+
print("Works fine!")
7232+
}
7233+
}
7234+
}
7235+
7236+
#if canImport(XcodeProjectPlugin)
7237+
import XcodeProjectPlugin
7238+
7239+
extension command_plugin_dynamic_linking: XcodeCommandPlugin {
7240+
// Entry point for command plugins applied to Xcode projects.
7241+
func performCommand(context: XcodePluginContext, arguments: [String]) throws {
7242+
print("Hello, World!")
7243+
}
7244+
}
7245+
7246+
#endif
7247+
"""
7248+
)
7249+
7250+
try localFileSystem.writeFileContents(
7251+
packageDir.appending(components: "LocalPackages", "DynamicLib", "Package.swift"),
7252+
string:
7253+
"""
7254+
// swift-tools-version: 6.0
7255+
// The swift-tools-version declares the minimum version of Swift required to build this package.
7256+
7257+
import PackageDescription
7258+
7259+
let package = Package(
7260+
name: "DynamicLib",
7261+
products: [
7262+
// Products define the executables and libraries a package produces, making them visible to other packages.
7263+
.library(
7264+
name: "DynamicLib",
7265+
type: .dynamic,
7266+
targets: ["DynamicLib"]),
7267+
],
7268+
targets: [
7269+
// Targets are the basic building blocks of a package, defining a module or a test suite.
7270+
// Targets can depend on other targets in this package and products from dependencies.
7271+
.target(
7272+
name: "DynamicLib"),
7273+
.testTarget(
7274+
name: "DynamicLibTests",
7275+
dependencies: ["DynamicLib"]
7276+
),
7277+
]
7278+
)
7279+
"""
7280+
)
7281+
7282+
try localFileSystem.writeFileContents(
7283+
packageDir.appending(components: "LocalPackages", "DynamicLib", "Sources", "DynamicLib.swift"),
7284+
string:
7285+
"""
7286+
// The Swift Programming Language
7287+
// https://docs.swift.org/swift-book
7288+
7289+
public func dynamicLibFunc() -> String {
7290+
return "Hello from DynamicLib!"
7291+
}
7292+
"""
7293+
)
7294+
7295+
let (stdout, _) = try await execute(
7296+
["plugin", "command_plugin_dynamic_linking"],
7297+
packagePath: packageDir,
7298+
configuration: buildData.config,
7299+
buildSystem: buildData.buildSystem,
7300+
)
7301+
7302+
#expect(stdout.contains("Works fine!"))
7303+
}
7304+
} when: {
7305+
(ProcessInfo.hostOperatingSystem == .windows && buildData.buildSystem == .swiftbuild) || (ProcessInfo.hostOperatingSystem == .linux && buildData.buildSystem == .swiftbuild)
7306+
}
7307+
}
71377308
}
71387309
}

0 commit comments

Comments
 (0)