Skip to content

Commit 1d602f2

Browse files
authored
Merge pull request #892 from kcieplak/topics/add-os-distribution-version-detection
Force linker to lld on amazon linux 2.
2 parents 7c7c622 + 2da727f commit 1d602f2

File tree

3 files changed

+616
-2
lines changed

3 files changed

+616
-2
lines changed

Sources/SWBGenericUnixPlatform/Plugin.swift

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,24 @@ struct GenericUnixSDKRegistryExtension: SDKRegistryExtension {
126126
defaultProperties = [:]
127127
}
128128

129-
if operatingSystem == .freebsd || operatingSystem != context.hostOperatingSystem {
130-
// FreeBSD is always LLVM-based, and if we're cross-compiling, use lld
129+
let shouldUseLLD = {
130+
switch operatingSystem {
131+
case .freebsd:
132+
// FreeBSD is always LLVM-based.
133+
return true
134+
case .linux:
135+
// Amazon Linux 2 has a gold linker bug see: https://sourceware.org/bugzilla/show_bug.cgi?id=23016.
136+
guard let distribution = operatingSystem.distribution else {
137+
return false
138+
}
139+
return distribution.kind == .amazon && distribution.version == "2"
140+
default:
141+
// Cross-compiling.
142+
return operatingSystem != context.hostOperatingSystem
143+
}
144+
}()
145+
146+
if shouldUseLLD {
131147
defaultProperties["ALTERNATE_LINKER"] = "lld"
132148
}
133149

Sources/SWBUtil/ProcessInfo.swift

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,56 @@ extension ProcessInfo {
123123
return .unknown
124124
#endif
125125
}
126+
127+
128+
}
129+
130+
public struct LinuxDistribution: Hashable, Sendable {
131+
public enum Kind: String, CaseIterable, Hashable, Sendable {
132+
case unknown
133+
case ubuntu
134+
case debian
135+
case amazon = "amzn"
136+
case centos
137+
case rhel
138+
case fedora
139+
case suse
140+
case alpine
141+
case arch
142+
143+
/// The display name for the distribution kind
144+
public var displayName: String {
145+
switch self {
146+
case .unknown: return "Unknown Linux"
147+
case .ubuntu: return "Ubuntu"
148+
case .debian: return "Debian"
149+
case .amazon: return "Amazon Linux"
150+
case .centos: return "CentOS"
151+
case .rhel: return "Red Hat Enterprise Linux"
152+
case .fedora: return "Fedora"
153+
case .suse: return "SUSE"
154+
case .alpine: return "Alpine Linux"
155+
case .arch: return "Arch Linux"
156+
}
157+
}
158+
}
159+
160+
public let kind: Kind
161+
public let version: String?
162+
163+
public init(kind: Kind, version: String? = nil) {
164+
self.kind = kind
165+
self.version = version
166+
}
167+
168+
/// The display name for the distribution including version if available
169+
public var displayName: String {
170+
if let version = version {
171+
return "\(kind.displayName) \(version)"
172+
} else {
173+
return kind.displayName
174+
}
175+
}
126176
}
127177

128178
public enum OperatingSystem: Hashable, Sendable {
@@ -157,6 +207,16 @@ public enum OperatingSystem: Hashable, Sendable {
157207
}
158208
}
159209

210+
/// The distribution if this is a Linux operating system
211+
public var distribution: LinuxDistribution? {
212+
switch self {
213+
case .linux:
214+
return detectHostLinuxDistribution()
215+
default:
216+
return nil
217+
}
218+
}
219+
160220
public var imageFormat: ImageFormat {
161221
switch self {
162222
case .macOS, .iOS, .tvOS, .watchOS, .visionOS:
@@ -167,6 +227,110 @@ public enum OperatingSystem: Hashable, Sendable {
167227
return .elf
168228
}
169229
}
230+
231+
private func detectHostLinuxDistribution() -> LinuxDistribution? {
232+
return detectHostLinuxDistribution(fs: localFS)
233+
}
234+
235+
/// Detects the Linux distribution by examining system files with an injected filesystem
236+
/// Start with the "generic" /etc/os-release then fallback
237+
/// to various distribution named files.
238+
public func detectHostLinuxDistribution(fs: any FSProxy) -> LinuxDistribution? {
239+
// Try /etc/os-release first (standard)
240+
let osReleasePath = Path("/etc/os-release")
241+
if fs.exists(osReleasePath) {
242+
if let osReleaseData = try? fs.read(osReleasePath),
243+
let osRelease = String(data: Data(osReleaseData.bytes), encoding: .utf8) {
244+
if let distribution = parseOSRelease(osRelease) {
245+
return distribution
246+
}
247+
}
248+
}
249+
250+
// Fallback to distribution-specific files
251+
let distributionFiles: [(String, LinuxDistribution.Kind)] = [
252+
("/etc/ubuntu-release", .ubuntu),
253+
("/etc/debian_version", .debian),
254+
("/etc/amazon-release", .amazon),
255+
("/etc/centos-release", .centos),
256+
("/etc/redhat-release", .rhel),
257+
("/etc/fedora-release", .fedora),
258+
("/etc/SuSE-release", .suse),
259+
("/etc/alpine-release", .alpine),
260+
("/etc/arch-release", .arch),
261+
]
262+
263+
for (file, kind) in distributionFiles {
264+
if fs.exists(Path(file)) {
265+
return LinuxDistribution(kind: kind)
266+
}
267+
}
268+
269+
return nil
270+
}
271+
272+
/// Parses /etc/os-release content to determine distribution and version
273+
/// Fallback to just getting the distribution from specific files.
274+
private func parseOSRelease(_ content: String) -> LinuxDistribution? {
275+
let lines = content.components(separatedBy: .newlines)
276+
var id: String?
277+
var idLike: String?
278+
var versionId: String?
279+
280+
// Parse out ID, ID_LIKE and VERSION_ID
281+
for line in lines {
282+
let trimmed = line.trimmingCharacters(in: .whitespaces)
283+
if trimmed.hasPrefix("ID=") {
284+
id = String(trimmed.dropFirst(3)).trimmingCharacters(in: CharacterSet(charactersIn: "\""))
285+
} else if trimmed.hasPrefix("ID_LIKE=") {
286+
idLike = String(trimmed.dropFirst(8)).trimmingCharacters(in: CharacterSet(charactersIn: "\""))
287+
} else if trimmed.hasPrefix("VERSION_ID=") {
288+
versionId = String(trimmed.dropFirst(11)).trimmingCharacters(in: CharacterSet(charactersIn: "\""))
289+
}
290+
}
291+
292+
// Check ID first
293+
if let id = id {
294+
let kind: LinuxDistribution.Kind?
295+
switch id.lowercased() {
296+
case "ubuntu": kind = .ubuntu
297+
case "debian": kind = .debian
298+
case "amzn": kind = .amazon
299+
case "centos": kind = .centos
300+
case "rhel": kind = .rhel
301+
case "fedora": kind = .fedora
302+
case "suse", "opensuse", "opensuse-leap", "opensuse-tumbleweed": kind = .suse
303+
case "alpine": kind = .alpine
304+
case "arch": kind = .arch
305+
default: kind = nil
306+
}
307+
308+
if let kind = kind {
309+
return LinuxDistribution(kind: kind, version: versionId)
310+
}
311+
}
312+
313+
// Check ID_LIKE as fallback
314+
if let idLike = idLike {
315+
let likes = idLike.components(separatedBy: .whitespaces)
316+
for like in likes {
317+
let kind: LinuxDistribution.Kind?
318+
switch like.lowercased() {
319+
case "ubuntu": kind = .ubuntu
320+
case "debian": kind = .debian
321+
case "rhel", "fedora": kind = .rhel
322+
case "suse": kind = .suse
323+
case "arch": kind = .arch
324+
default: kind = nil
325+
}
326+
327+
if let kind = kind {
328+
return LinuxDistribution(kind: kind, version: versionId)
329+
}
330+
}
331+
}
332+
return nil
333+
}
170334
}
171335

172336
public enum ImageFormat {
@@ -255,3 +419,4 @@ extension FixedWidthInteger {
255419
return self != 0 ? self : other
256420
}
257421
}
422+

0 commit comments

Comments
 (0)