diff --git a/Package.swift b/Package.swift index 14110d94038..8ecc9074dc0 100644 --- a/Package.swift +++ b/Package.swift @@ -681,9 +681,7 @@ package.targets.append(contentsOf: [ .testTarget( name: "FunctionalPerformanceTests", dependencies: [ - "swift-build", - "swift-package", - "swift-test", + "swift-package-manager", "SPMTestSupport" ] ), @@ -695,9 +693,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_DISABLE_SDK_DEPENDENT_TESTS"] == .testTarget( name: "FunctionalTests", dependencies: [ - "swift-build", - "swift-package", - "swift-test", + "swift-package-manager", "PackageModel", "SPMTestSupport" ] @@ -713,10 +709,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_DISABLE_SDK_DEPENDENT_TESTS"] == .testTarget( name: "CommandsTests", dependencies: [ - "swift-build", - "swift-package", - "swift-test", - "swift-run", + "swift-package-manager", "Basics", "Build", "Commands", diff --git a/Sources/Commands/SwiftBuildCommand.swift b/Sources/Commands/SwiftBuildCommand.swift index 13c2d658621..a29eca9d0e1 100644 --- a/Sources/Commands/SwiftBuildCommand.swift +++ b/Sources/Commands/SwiftBuildCommand.swift @@ -95,7 +95,7 @@ struct BuildCommandOptions: ParsableArguments { } /// swift-build command namespace -public struct SwiftBuildCommand: SwiftCommand { +public struct SwiftBuildCommand: AsyncSwiftCommand { public static var configuration = CommandConfiguration( commandName: "build", _superCommandName: "swift", @@ -110,7 +110,7 @@ public struct SwiftBuildCommand: SwiftCommand { @OptionGroup() var options: BuildCommandOptions - public func run(_ swiftCommandState: SwiftCommandState) throws { + public func run(_ swiftCommandState: SwiftCommandState) async throws { if options.shouldPrintBinPath { return try print(swiftCommandState.productsBuildParameters.buildPath.description) } diff --git a/Sources/Commands/SwiftRunCommand.swift b/Sources/Commands/SwiftRunCommand.swift index bbf480ab23e..de7e9077090 100644 --- a/Sources/Commands/SwiftRunCommand.swift +++ b/Sources/Commands/SwiftRunCommand.swift @@ -90,7 +90,7 @@ struct RunCommandOptions: ParsableArguments { } /// swift-run command namespace -public struct SwiftRunCommand: SwiftCommand { +public struct SwiftRunCommand: AsyncSwiftCommand { public static var configuration = CommandConfiguration( commandName: "run", _superCommandName: "swift", @@ -109,7 +109,7 @@ public struct SwiftRunCommand: SwiftCommand { return .init(wantsREPLProduct: options.mode == .repl) } - public func run(_ swiftCommandState: SwiftCommandState) throws { + public func run(_ swiftCommandState: SwiftCommandState) async throws { if options.shouldBuildTests && options.shouldSkipBuild { swiftCommandState.observabilityScope.emit( .mutuallyExclusiveArgumentsError(arguments: ["--build-tests", "--skip-build"]) @@ -284,9 +284,20 @@ public struct SwiftRunCommand: SwiftCommand { /// A safe wrapper of TSCBasic.exec. private func execute(path: String, args: [String]) throws -> Never { #if !os(Windows) - // On platforms other than Windows, signal(SIGINT, SIG_IGN) is used for handling SIGINT by DispatchSourceSignal, - // but this process is about to be replaced by exec, so SIG_IGN must be returned to default. - signal(SIGINT, SIG_DFL) + // Dispatch will disable almost all asynchronous signals on its worker threads, and this is called from `async` + // context. To correctly `exec` a freshly built binary, we will need to: + // 1. reset the signal masks + for i in 1.. AbsolutePath { @@ -114,11 +114,12 @@ extension SwiftPM { #endif // FIXME: We use this private environment variable hack to be able to // create special conditions in swift-build for swiftpm tests. - environment["SWIFTPM_TESTS_MODULECACHE"] = xctestBinaryPath.parentDirectory.pathString - + environment["SWIFTPM_TESTS_MODULECACHE"] = self.xctestBinaryPath.parentDirectory.pathString + // Unset the internal env variable that allows skipping certain tests. environment["_SWIFTPM_SKIP_TESTS_LIST"] = nil - + environment["SWIFTPM_EXEC_NAME"] = self.executableName + for (key, value) in env ?? [:] { environment[key] = value } diff --git a/Sources/swift-build/CMakeLists.txt b/Sources/swift-build/CMakeLists.txt index 4488799b1f5..936298a6fad 100644 --- a/Sources/swift-build/CMakeLists.txt +++ b/Sources/swift-build/CMakeLists.txt @@ -7,9 +7,12 @@ # See http://swift.org/CONTRIBUTORS.txt for Swift project authors add_executable(swift-build - main.swift) + Entrypoint.swift) target_link_libraries(swift-build PRIVATE Commands) +target_compile_options(swift-build PRIVATE + -parse-as-library) + install(TARGETS swift-build DESTINATION bin) diff --git a/Sources/swift-run/main.swift b/Sources/swift-build/Entrypoint.swift similarity index 83% rename from Sources/swift-run/main.swift rename to Sources/swift-build/Entrypoint.swift index 0f624378052..0dcb53302ec 100644 --- a/Sources/swift-run/main.swift +++ b/Sources/swift-build/Entrypoint.swift @@ -12,4 +12,9 @@ import Commands -SwiftRunCommand.main() +@main +struct Entrypoint { + static func main() async { + await SwiftBuildCommand.main() + } +} diff --git a/Sources/swift-package-manager/SwiftPM.swift b/Sources/swift-package-manager/SwiftPM.swift index 750597f5ed3..3e3d7a106ee 100644 --- a/Sources/swift-package-manager/SwiftPM.swift +++ b/Sources/swift-package-manager/SwiftPM.swift @@ -17,23 +17,35 @@ import PackageCollectionsCommand import PackageRegistryCommand let firstArg = CommandLine.arguments[0] -let execName = (try? AbsolutePath(validating: firstArg).basenameWithoutExt) ?? +let baseNameWithoutExtension = (try? AbsolutePath(validating: firstArg).basenameWithoutExt) ?? (try? RelativePath(validating: firstArg).basenameWithoutExt) @main struct SwiftPM { static func main() async { + // Workaround a bug in Swift 5.9, where multiple executables with an `async` main entrypoint can't be linked + // into the same test bundle. We're then linking single `swift-package-manager` binary instead and passing + // executable name via `SWIFTPM_EXEC_NAME`. + if baseNameWithoutExtension == "swift-package-manager" { + await main(execName: EnvironmentVariables.process()["SWIFTPM_EXEC_NAME"]) + } else { + await main(execName: baseNameWithoutExtension) + } + } + + @discardableResult + private static func main(execName: String?) async -> Bool { switch execName { case "swift-package": await SwiftPackageCommand.main() case "swift-build": - SwiftBuildCommand.main() + await SwiftBuildCommand.main() case "swift-experimental-sdk": await SwiftSDKCommand.main() case "swift-test": SwiftTestCommand.main() case "swift-run": - SwiftRunCommand.main() + await SwiftRunCommand.main() case "swift-package-collection": await PackageCollectionsCommand.main() case "swift-package-registry": @@ -41,5 +53,7 @@ struct SwiftPM { default: fatalError("swift-package-manager launched with unexpected name: \(execName ?? "(unknown)")") } + + return true } } diff --git a/Sources/swift-run/CMakeLists.txt b/Sources/swift-run/CMakeLists.txt index 26b4ac9e321..9c609f84ea1 100644 --- a/Sources/swift-run/CMakeLists.txt +++ b/Sources/swift-run/CMakeLists.txt @@ -7,9 +7,12 @@ # See http://swift.org/CONTRIBUTORS.txt for Swift project authors add_executable(swift-run - main.swift) + Entrypoint.swift) target_link_libraries(swift-run PRIVATE Commands) +target_compile_options(swift-run PRIVATE + -parse-as-library) + install(TARGETS swift-run RUNTIME DESTINATION bin) diff --git a/Sources/swift-build/main.swift b/Sources/swift-run/Entrypoint.swift similarity index 83% rename from Sources/swift-build/main.swift rename to Sources/swift-run/Entrypoint.swift index 9600d0998cf..4976cccdb15 100644 --- a/Sources/swift-build/main.swift +++ b/Sources/swift-run/Entrypoint.swift @@ -12,4 +12,9 @@ import Commands -SwiftBuildCommand.main() +@main +struct Entrypoint { + static func main() async { + await SwiftRunCommand.main() + } +} diff --git a/Tests/CommandsTests/RunCommandTests.swift b/Tests/CommandsTests/RunCommandTests.swift index 7661902cbff..ec50b40b3ea 100644 --- a/Tests/CommandsTests/RunCommandTests.swift +++ b/Tests/CommandsTests/RunCommandTests.swift @@ -16,6 +16,7 @@ import SPMTestSupport import XCTest import class TSCBasic.Process +import enum TSCBasic.ProcessEnv final class RunCommandTests: CommandsTestCase { private func execute( @@ -121,8 +122,12 @@ final class RunCommandTests: CommandsTestCase { let sync = DispatchGroup() let outputHandler = OutputHandler(sync: sync) + + var environmentBlock = ProcessEnv.block + environmentBlock["SWIFTPM_EXEC_NAME"] = "swift-run" let process = Process( arguments: [SwiftPM.Run.xctestBinaryPath.pathString, "--package-path", fixturePath.pathString], + environmentBlock: environmentBlock, outputRedirection: .stream(stdout: outputHandler.handle(bytes:), stderr: outputHandler.handle(bytes:)) )