Skip to content

Commit 0aa5486

Browse files
Add support for generating Swift SDKs for Ubuntu 24.04 Noble (#188)
I was working on adding Debian 12 support for #116, but realized that adding Ubuntu Noble is "low-hanging fruit", since it's very straightforward. The default is still Ubuntu 22.04 Jammy, but this adds the option of generating the Swift SDK for 24.04 Noble now. ``` swift run swift-sdk-generator make-linux-sdk --linux-distribution-version 24.04 ``` I have also changed the packages download to get `Packages.xz` instead of `Packages.gz` since it is a smaller file download. I wonder if it is okay to use `xz` here since I noticed that the directories on the Debian mirrors only have `Packages.xz` files available, unlike the Ubuntu mirrors which all have `Packages.gz` AND `Packages.xz` files available.
1 parent a9f8e2a commit 0aa5486

File tree

7 files changed

+101
-19
lines changed

7 files changed

+101
-19
lines changed

Brewfile

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
brew 'xz'

README.md

+11-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ swift experimental-sdk list
2727
The output will either state that no Swift SDKs are available, or produce a list of those you previously had
2828
installed, in case you've used the `swift experimental-sdk install` command before.
2929

30+
### macOS Requirements
31+
32+
The generator depends on the `xz` utility for more efficient downloading of package lists for Ubuntu. This is optional, but can be installed via the included `Brewfile`:
33+
34+
```bash
35+
brew bundle install
36+
```
37+
38+
If `xz` is not found, the generator will fallback on `gzip`.
39+
3040
## Supported platforms and minimum versions
3141

3242
macOS as a host platform and Linux as both host and target platforms are supported by the generator.
@@ -36,7 +46,7 @@ The generator also allows cross-compiling between any Linux distributions offici
3646
| -: | :- | :- |
3747
| macOS (arm64) | ✅ macOS 13.0+ ||
3848
| macOS (x86_64) | ✅ macOS 13.0+[^1] ||
39-
| Ubuntu | ✅ 20.04+ | ✅ 20.04 / 22.04 |
49+
| Ubuntu | ✅ 20.04+ | ✅ 20.04+ |
4050
| RHEL | ✅ Fedora 39[^2], UBI 9 | ✅ UBI 9 |
4151
| Amazon Linux 2 | ✅ Supported | ✅ Supported[^3] |
4252
| Debian 12 | ✅ Supported[^2] | ✅ Supported[^2][^3] |

Sources/GeneratorCLI/GeneratorCLI.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,9 @@ extension GeneratorCLI {
199199

200200
@Option(
201201
help: """
202-
Version of the Linux distribution used as a target platform. Available options for Ubuntu: `20.04`, \
203-
`22.04` (default when `--linux-distribution-name` is `ubuntu`). Available options for RHEL: `ubi9` (default when \
204-
`--linux-distribution-name` is `rhel`).
202+
Version of the Linux distribution used as a target platform.
203+
Available options for Ubuntu: `20.04`, `22.04` (default when `--linux-distribution-name` is `ubuntu`), `24.04`.
204+
Available options for RHEL: `ubi9` (default when `--linux-distribution-name` is `rhel`).
205205
"""
206206
)
207207
var linuxDistributionVersion: String?

Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Download.swift

+30-8
Original file line numberDiff line numberDiff line change
@@ -82,27 +82,39 @@ extension SwiftSDKGenerator {
8282
) async throws {
8383
logger.debug("Parsing Ubuntu packages list...")
8484

85+
// Find xz path
86+
let xzPath = try await which("xz")
87+
if xzPath == nil {
88+
logger.warning("""
89+
The `xz` utility was not found in `PATH`. \
90+
Consider installing it for more efficient downloading of package lists.
91+
""")
92+
}
93+
8594
async let mainPackages = try await client.parseUbuntuPackagesList(
8695
ubuntuRelease: versionsConfiguration.linuxDistribution.release,
8796
repository: "main",
8897
targetTriple: self.targetTriple,
89-
isVerbose: self.isVerbose
98+
isVerbose: self.isVerbose,
99+
xzPath: xzPath
90100
)
91101

92102
async let updatesPackages = try await client.parseUbuntuPackagesList(
93103
ubuntuRelease: versionsConfiguration.linuxDistribution.release,
94104
releaseSuffix: "-updates",
95105
repository: "main",
96106
targetTriple: self.targetTriple,
97-
isVerbose: self.isVerbose
107+
isVerbose: self.isVerbose,
108+
xzPath: xzPath
98109
)
99110

100111
async let universePackages = try await client.parseUbuntuPackagesList(
101112
ubuntuRelease: versionsConfiguration.linuxDistribution.release,
102113
releaseSuffix: "-updates",
103114
repository: "universe",
104115
targetTriple: self.targetTriple,
105-
isVerbose: self.isVerbose
116+
isVerbose: self.isVerbose,
117+
xzPath: xzPath
106118
)
107119

108120
let allPackages = try await mainPackages
@@ -175,21 +187,31 @@ extension SwiftSDKGenerator {
175187
extension HTTPClientProtocol {
176188
private func downloadUbuntuPackagesList(
177189
from url: String,
190+
unzipWith zipPath: String,
178191
isVerbose: Bool
179192
) async throws -> String? {
180-
guard let packages = try await get(url: url).body?.unzip(isVerbose: isVerbose) else {
193+
guard let packages = try await get(url: url).body?.unzip(zipPath: zipPath, isVerbose: isVerbose) else {
181194
throw FileOperationError.downloadFailed(url)
182195
}
183196

184197
return String(buffer: packages)
185198
}
186199

200+
func packagesFileName(isXzAvailable: Bool) -> String {
201+
if isXzAvailable {
202+
return "Packages.xz"
203+
}
204+
// Use .gz if xz is not available
205+
return "Packages.gz"
206+
}
207+
187208
func parseUbuntuPackagesList(
188209
ubuntuRelease: String,
189210
releaseSuffix: String = "",
190211
repository: String,
191212
targetTriple: Triple,
192-
isVerbose: Bool
213+
isVerbose: Bool,
214+
xzPath: String?
193215
) async throws -> [String: URL] {
194216
let mirrorURL: String
195217
if targetTriple.arch == .x86_64 {
@@ -200,13 +222,13 @@ extension HTTPClientProtocol {
200222

201223
let packagesListURL = """
202224
\(mirrorURL)/dists/\(ubuntuRelease)\(releaseSuffix)/\(repository)/binary-\(
203-
targetTriple.arch!
204-
.debianConventionName
205-
)/Packages.gz
225+
targetTriple.arch!.debianConventionName
226+
)/\(packagesFileName(isXzAvailable: xzPath != nil))
206227
"""
207228

208229
guard let packages = try await downloadUbuntuPackagesList(
209230
from: packagesListURL,
231+
unzipWith: xzPath ?? "/usr/bin/gzip", // fallback on gzip if xz not available
210232
isVerbose: isVerbose
211233
) else {
212234
throw GeneratorError.ubuntuPackagesDecompressionFailure

Sources/SwiftSDKGenerator/PlatformModels/LinuxDistribution.swift

+17-5
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
private let ubuntuReleases = [
14-
"22.04": "jammy",
15-
]
16-
1713
public enum LinuxDistribution: Hashable, Sendable {
1814
public enum Name: String {
1915
case rhel
@@ -27,13 +23,16 @@ public enum LinuxDistribution: Hashable, Sendable {
2723
public enum Ubuntu: String, Sendable {
2824
case focal
2925
case jammy
26+
case noble
3027

3128
init(version: String) throws {
3229
switch version {
3330
case "20.04":
3431
self = .focal
3532
case "22.04":
3633
self = .jammy
34+
case "24.04":
35+
self = .noble
3736
default:
3837
throw GeneratorError.unknownLinuxDistribution(name: LinuxDistribution.Name.ubuntu.rawValue, version: version)
3938
}
@@ -43,6 +42,7 @@ public enum LinuxDistribution: Hashable, Sendable {
4342
switch self {
4443
case .focal: return "20.04"
4544
case .jammy: return "22.04"
45+
case .noble: return "24.04"
4646
}
4747
}
4848

@@ -60,7 +60,6 @@ public enum LinuxDistribution: Hashable, Sendable {
6060
"linux-libc-dev",
6161
"zlib1g",
6262
"zlib1g-dev",
63-
"libc6",
6463
]
6564
case .jammy: return [
6665
"libc6",
@@ -75,6 +74,19 @@ public enum LinuxDistribution: Hashable, Sendable {
7574
"zlib1g",
7675
"zlib1g-dev",
7776
]
77+
case .noble: return [
78+
"libc6",
79+
"libc6-dev",
80+
"libgcc-s1",
81+
"libgcc-13-dev",
82+
"libicu74",
83+
"libicu-dev",
84+
"libstdc++-13-dev",
85+
"libstdc++6",
86+
"linux-libc-dev",
87+
"zlib1g",
88+
"zlib1g-dev",
89+
]
7890
}
7991
}
8092
}

Sources/SwiftSDKGenerator/SystemUtils/ByteBuffer+Utils.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ import Foundation
1515
import NIOCore
1616

1717
public extension ByteBuffer {
18-
func unzip(isVerbose: Bool) async throws -> ByteBuffer? {
18+
func unzip(zipPath: String, isVerbose: Bool) async throws -> ByteBuffer? {
1919
let result = try await ProcessExecutor.runCollectingOutput(
20-
executable: "/usr/bin/gzip", ["-cd"],
20+
executable: zipPath, ["-cd"],
2121
standardInput: [self].async,
2222
collectStandardOutput: true,
2323
collectStandardError: false,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2022-2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import AsyncProcess
14+
import Foundation
15+
16+
/// Look for an executable using the `which` utility.
17+
///
18+
/// - Parameter executableName: The name of the executable to search for.
19+
/// - Throws: Any errors thrown by the ProcessExecutor.
20+
/// - Returns: The path to the executable if found, otherwise nil.
21+
func which(_ executableName: String) async throws -> String? {
22+
let result = try await ProcessExecutor.runCollectingOutput(
23+
executable: "/usr/bin/which", [executableName], collectStandardOutput: true, collectStandardError: false,
24+
environment: ProcessInfo.processInfo.environment
25+
)
26+
27+
guard result.exitReason == .exit(0) else {
28+
return nil
29+
}
30+
31+
if let output = result.standardOutput {
32+
let path = String(buffer: output).trimmingCharacters(in: .whitespacesAndNewlines)
33+
return path.isEmpty ? nil : path
34+
}
35+
36+
return nil
37+
}

0 commit comments

Comments
 (0)