diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index f1dab6183..82c3ed922 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -660,7 +660,9 @@ public struct Driver { self.lto = Self.ltoKind(&parsedOptions, diagnosticsEngine: diagnosticsEngine) // Figure out the primary outputs from the driver. - (self.compilerOutputType, self.linkerOutputType) = Self.determinePrimaryOutputs(&parsedOptions, driverKind: driverKind, diagnosticsEngine: diagnosticEngine) + (self.compilerOutputType, self.linkerOutputType) = + Self.determinePrimaryOutputs(&parsedOptions, targetTriple: self.frontendTargetInfo.target.triple, + driverKind: driverKind, diagnosticsEngine: diagnosticEngine) // Multithreading. self.numThreads = Self.determineNumThreads(&parsedOptions, compilerMode: compilerMode, diagnosticsEngine: diagnosticEngine) @@ -1994,6 +1996,7 @@ extension Driver { /// Determine the primary compiler and linker output kinds. private static func determinePrimaryOutputs( _ parsedOptions: inout ParsedOptions, + targetTriple: Triple, driverKind: DriverKind, diagnosticsEngine: DiagnosticsEngine ) -> (FileType?, LinkOutputType?) { @@ -2005,7 +2008,7 @@ extension Driver { if let outputOption = parsedOptions.getLast(in: .modes) { switch outputOption.option { case .emitExecutable: - if parsedOptions.contains(.static) { + if parsedOptions.contains(.static) && !targetTriple.supportsStaticExecutables { diagnosticsEngine.emit(.error_static_emit_executable_disallowed) } linkerOutputType = .executable diff --git a/Sources/SwiftDriver/Utilities/Triple.swift b/Sources/SwiftDriver/Utilities/Triple.swift index 430685cde..805984f16 100644 --- a/Sources/SwiftDriver/Utilities/Triple.swift +++ b/Sources/SwiftDriver/Utilities/Triple.swift @@ -1705,3 +1705,14 @@ fileprivate extension Array { } } } + +// MARK: - Linker support + +extension Triple { + /// Returns `true` if a given triple supports producing fully statically linked executables by providing `-static` + /// flag to the linker. This implies statically linking platform's libc, and of those that Swift supports currently + /// only Musl allows that reliably. + var supportsStaticExecutables: Bool { + self.environment == .musl + } +} diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index 648088dba..148192d70 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -2100,10 +2100,42 @@ final class SwiftDriverTests: XCTestCase { XCTAssertFalse(cmd.contains(.flag("-dylib"))) XCTAssertFalse(cmd.contains(.flag("-shared"))) } + + do { + // executable linking linux static stdlib with musl + var driver = try Driver(args: commonArgs + [ + "-emit-executable", "-Osize", "-static-stdlib", "-static-executable", "-target", "x86_64-unknown-linux-musl" + ], env: env) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 4) + + let autolinkExtractJob = plannedJobs[2] + XCTAssertEqual(autolinkExtractJob.kind, .autolinkExtract) + + let autolinkCmd = autolinkExtractJob.commandLine + XCTAssertTrue(commandContainsTemporaryPath(autolinkCmd, "foo.o")) + XCTAssertTrue(commandContainsTemporaryPath(autolinkCmd, "bar.o")) + XCTAssertTrue(commandContainsTemporaryPath(autolinkCmd, "Test.autolink")) + + let linkJob = plannedJobs[3] + let cmd = linkJob.commandLine + XCTAssertTrue(cmd.contains(.flag("-o"))) + XCTAssertTrue(commandContainsTemporaryPath(cmd, "foo.o")) + XCTAssertTrue(commandContainsTemporaryPath(cmd, "bar.o")) + XCTAssertTrue(cmd.contains(.flag("--start-group"))) + XCTAssertTrue(cmd.contains(.flag("--end-group"))) + XCTAssertTrue(cmd.contains(.flag("-Os"))) + XCTAssertTrue(cmd.contains(.flag("-static"))) + XCTAssertEqual(linkJob.outputs[0].file, try VirtualPath(path: "Test")) + + XCTAssertFalse(cmd.contains(.flag("-dylib"))) + XCTAssertFalse(cmd.contains(.flag("-shared"))) + } #endif do { - // static WASM linking + // static Wasm linking var driver = try Driver(args: commonArgs + ["-emit-library", "-static", "-target", "wasm32-unknown-wasi"], env: env) let plannedJobs = try driver.planBuild() @@ -2135,7 +2167,7 @@ final class SwiftDriverTests: XCTestCase { try withTemporaryDirectory { path in try localFileSystem.writeFileContents( path.appending(components: "wasi", "static-executable-args.lnk")) { $0 <<< "garbage" } - // WASM executable linking + // Wasm executable linking var driver = try Driver(args: commonArgs + ["-emit-executable", "-Ounchecked", "-target", "wasm32-unknown-wasi", "-resource-dir", path.pathString,