Skip to content

Commit 067dd3a

Browse files
committed
Enable the static Linux SDK build
Helps keep the codebase free of unintentional Glibc-isms. There are also some interesting testing use cases enabled by building Swift Build using the static SDK.
1 parent afc6b35 commit 067dd3a

File tree

5 files changed

+41
-17
lines changed

5 files changed

+41
-17
lines changed

.github/workflows/pull_request.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ jobs:
1919
linux_swift_versions: '["nightly-main", "nightly-6.2"]'
2020
windows_swift_versions: '["nightly-main"]'
2121
windows_build_command: 'swift test --no-parallel'
22+
enable_linux_static_sdk_build: true
23+
linux_static_sdk_build_command: SWIFTBUILD_STATIC_LINK=1 LLBUILD_STATIC_LINK=1 swift build
2224
cmake-smoke-test:
2325
name: cmake-smoke-test
2426
uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main

Package.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ let appleOS = true
2020
let appleOS = false
2121
#endif
2222

23+
let isStaticBuild = Context.environment["SWIFTBUILD_STATIC_LINK"] != nil
2324
let useLocalDependencies = Context.environment["SWIFTCI_USE_LOCAL_DEPS"] != nil
2425
let useLLBuildFramework = Context.environment["SWIFTBUILD_LLBUILD_FWK"] != nil
2526

@@ -447,6 +448,12 @@ for target in package.targets {
447448
}
448449
}
449450

451+
if isStaticBuild {
452+
package.targets = package.targets.filter { target in
453+
target.type != .test && !target.name.hasSuffix("TestSupport")
454+
}
455+
}
456+
450457
// `SWIFTCI_USE_LOCAL_DEPS` configures if dependencies are locally available to build
451458
if useLocalDependencies {
452459
package.dependencies += [

Sources/SWBUtil/Library.swift

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,28 +63,24 @@ public enum Library: Sendable {
6363
return unsafeBitCast(ptr, to: T.self)
6464
}
6565

66-
public static func locate<T>(_ pointer: T.Type) -> Path {
66+
public static func locate<T>(_ pointer: T.Type) throws -> Path {
6767
#if os(Windows)
6868
var handle: HMODULE?
6969
guard GetModuleHandleExW(DWORD(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT), unsafeBitCast(pointer, to: LPCWSTR?.self), &handle) else {
70-
return Path("")
71-
}
72-
do {
73-
return try Path(SWB_GetModuleFileNameW(handle))
74-
} catch {
75-
return Path("")
70+
throw SymbolLookupError(underlyingError: Win32Error(GetLastError()))
7671
}
72+
return try Path(SWB_GetModuleFileNameW(handle))
7773
#else
78-
let outPointer: UnsafeMutablePointer<CInterop.PlatformChar>
7974
var info = Dl_info()
8075
#if os(Android)
8176
dladdr(unsafeBitCast(pointer, to: UnsafeMutableRawPointer.self), &info)
82-
outPointer = UnsafeMutablePointer(mutating: info.dli_fname!)
8377
#else
8478
dladdr(unsafeBitCast(pointer, to: UnsafeMutableRawPointer?.self), &info)
85-
outPointer = UnsafeMutablePointer(mutating: info.dli_fname)
8679
#endif
87-
return Path(platformString: outPointer)
80+
guard let dli_fname = info.dli_fname else {
81+
throw SymbolLookupError(underlyingError: nil)
82+
}
83+
return Path(platformString: UnsafeMutablePointer(mutating: dli_fname))
8884
#endif
8985
}
9086
}
@@ -102,6 +98,23 @@ public struct LibraryOpenError: Error, CustomStringConvertible, Sendable {
10298
}
10399
}
104100

101+
public struct SymbolLookupError: Error, CustomStringConvertible, Sendable {
102+
private let underlyingError: (any Error)?
103+
104+
public var description: String {
105+
let message = "Could not locate shared object for pointer"
106+
if let underlyingError {
107+
return "\(message): \(underlyingError)"
108+
}
109+
return message
110+
}
111+
112+
@usableFromInline
113+
internal init(underlyingError: (any Error)?) {
114+
self.underlyingError = underlyingError
115+
}
116+
}
117+
105118
// Library handles just store an opaque reference to the dlopen/LoadLibrary-returned pointer, and so are Sendable in practice based on how they are used.
106119
public struct LibraryHandle: @unchecked Sendable {
107120
#if os(Windows)

Sources/SwiftBuild/SWBBuildServiceConnection.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ typealias swb_build_service_connection_message_handler_t = @Sendable (UInt64, SW
6363

6464
/// Absolute path to the dyld-loaded dylib binary that contains this class.
6565
fileprivate class var swiftbuildDylibPath: Path {
66-
return Library.locate(SWBBuildServiceConnection.self)
66+
get throws {
67+
return try Library.locate(SWBBuildServiceConnection.self)
68+
}
6769
}
6870

6971
fileprivate enum State {
@@ -168,7 +170,7 @@ typealias swb_build_service_connection_message_handler_t = @Sendable (UInt64, SW
168170
// Compute the path to the clang ASan dylib to use when launching the ASan variant of SWBBuildService.
169171
// The linker adds a non-portable rpath to the directory containing the ASan dylib based on the path to the Xcode used to link the binary. We look in Bundle.main.bundlePath (SwiftBuild_asan) for .../Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/<vers>/lib/darwin so we can relaunch with the ASan library in the default toolchain of the Xcode we're part of.
170172
// There are some examples of this rpath breaking which we've had to fix, e.g.: rdar://57759442&75222176
171-
let asanDylib = SWBBuildServiceConnection.swiftbuildDylibPath.str.hasSuffix("_asan") ? SWBBuildServiceConnection.swiftbuildDylibPath.str : SWBBuildServiceConnection.swiftbuildDylibPath.str + "_asan"
173+
let asanDylib = try SWBBuildServiceConnection.swiftbuildDylibPath.str.hasSuffix("_asan") ? SWBBuildServiceConnection.swiftbuildDylibPath.str : SWBBuildServiceConnection.swiftbuildDylibPath.str + "_asan"
172174
if !FileManager.default.isExecutableFile(atPath: asanDylib) {
173175
// We always look for the _asan variant of the build service executable since only it will have the rpaths we need to subsequently look up. However, if it's missing we should then just fall back to the normal variant.
174176
break asan
@@ -605,7 +607,7 @@ extension SWBBuildServiceVariant {
605607
case .default:
606608
// Check if the binary containing this class ends with _asan, in which case, we interpret this as a signal that we're running in asan mode, and that we should
607609
// load the service bundle in asan mode as well.
608-
return SWBBuildServiceConnection.swiftbuildDylibPath.str.hasSuffix("_asan")
610+
return (try? SWBBuildServiceConnection.swiftbuildDylibPath.str.hasSuffix("_asan")) ?? false
609611
case .normal:
610612
return false
611613
case .asan:
@@ -705,7 +707,7 @@ fileprivate final class InProcessStaticConnection: ConnectionTransport {
705707
buildServicePlugInsDirectory = URL(fileURLWithPath: path.dirname.str, isDirectory: true)
706708
} else {
707709
// If the build service executable is unbundled, then try to find the build service entry point in this executable.
708-
let path = Library.locate(SWBBuildServiceConnection.self)
710+
let path = try Library.locate(SWBBuildServiceConnection.self)
709711
// If the build service executable is unbundled, assume that any plugins that may exist are next to our executable.
710712
buildServicePlugInsDirectory = URL(fileURLWithPath: path.dirname.str, isDirectory: true)
711713
}
@@ -802,7 +804,7 @@ fileprivate final class InProcessConnection: ConnectionTransport {
802804
buildServicePlugInsDirectory = URL(fileURLWithPath: path.dirname.str, isDirectory: true)
803805
} else {
804806
// If the build service executable is unbundled, then try to find the build service entry point in this executable.
805-
let path = Library.locate(SWBBuildServiceConnection.self)
807+
let path = try Library.locate(SWBBuildServiceConnection.self)
806808
handle = try Library.open(path)
807809

808810
// If the build service executable is unbundled, assume that any plugins that may exist are next to our executable.

Tests/SwiftBuildTests/ServiceTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ fileprivate struct ServiceTests {
240240

241241
@Test(.requireSDKs(.macOS), .skipSwiftPackage, .skipIfEnvironment(key: "DYLD_IMAGE_SUFFIX", value: "_asan"), .disabled(if: getEnvironmentVariable("CI")?.isEmpty == false))
242242
func ASanBuildService() async throws {
243-
let testBundleExecutableFilePath = Library.locate(Self.self)
243+
let testBundleExecutableFilePath = try Library.locate(Self.self)
244244

245245
// Whether the current XCTest executable is running in ASan mode. Most likely never true.
246246
let runningWithASanSupport = testBundleExecutableFilePath.str.hasSuffix("_asan")

0 commit comments

Comments
 (0)