Skip to content

Commit 7c9dadb

Browse files
committed
Use SwiftlyCore in build swiftly release script
1 parent a5dca08 commit 7c9dadb

File tree

2 files changed

+36
-132
lines changed

2 files changed

+36
-132
lines changed

Sources/SwiftlyWebsiteAPI/openapi.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,9 @@ components:
284284
items:
285285
$ref: '#/components/schemas/Architecture'
286286
description: List of supported architectures.
287+
checksum:
288+
type: string
289+
description: SHA-256 Checksum of the static SDK, if this platform is the static SDK.
287290
required:
288291
- name
289292
- platform

Tools/build-swiftly-release/BuildSwiftlyRelease.swift

Lines changed: 33 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,6 @@ extension Runnable {
2929
}
3030
}
3131

32-
public struct SwiftPlatform: Codable {
33-
public var name: String?
34-
public var checksum: String?
35-
}
36-
37-
public struct SwiftRelease: Codable {
38-
public var name: String?
39-
public var platforms: [SwiftPlatform]?
40-
}
41-
4232
// These functions are cloned and adapted from SwiftlyCore until we can do better bootstrapping
4333
public struct Error: LocalizedError, CustomStringConvertible {
4434
public let message: String
@@ -51,93 +41,6 @@ public struct Error: LocalizedError, CustomStringConvertible {
5141
public var description: String { self.message }
5242
}
5343

54-
public func runProgramEnv(_ args: String..., quiet: Bool = false, env: [String: String]?) throws {
55-
if !quiet { print("\(args.joined(separator: " "))") }
56-
57-
let process = Process()
58-
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
59-
process.arguments = args
60-
61-
if let env = env {
62-
process.environment = env
63-
}
64-
65-
if quiet {
66-
process.standardOutput = nil
67-
process.standardError = nil
68-
}
69-
70-
try process.run()
71-
// Attach this process to our process group so that Ctrl-C and other signals work
72-
let pgid = tcgetpgrp(STDOUT_FILENO)
73-
if pgid != -1 {
74-
tcsetpgrp(STDOUT_FILENO, process.processIdentifier)
75-
}
76-
process.waitUntilExit()
77-
78-
guard process.terminationStatus == 0 else {
79-
throw Error(message: "\(args.first!) exited with non-zero status: \(process.terminationStatus)")
80-
}
81-
}
82-
83-
public func runProgram(_ args: String..., quiet: Bool = false) throws {
84-
if !quiet { print("\(args.joined(separator: " "))") }
85-
86-
let process = Process()
87-
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
88-
process.arguments = args
89-
90-
if quiet {
91-
process.standardOutput = nil
92-
process.standardError = nil
93-
}
94-
95-
try process.run()
96-
// Attach this process to our process group so that Ctrl-C and other signals work
97-
let pgid = tcgetpgrp(STDOUT_FILENO)
98-
if pgid != -1 {
99-
tcsetpgrp(STDOUT_FILENO, process.processIdentifier)
100-
}
101-
process.waitUntilExit()
102-
103-
guard process.terminationStatus == 0 else {
104-
throw Error(message: "\(args.first!) exited with non-zero status: \(process.terminationStatus)")
105-
}
106-
}
107-
108-
public func runProgramOutput(_ program: String, _ args: String...) async throws -> String? {
109-
print("\(program) \(args.joined(separator: " "))")
110-
111-
let process = Process()
112-
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
113-
process.arguments = [program] + args
114-
115-
let outPipe = Pipe()
116-
process.standardInput = FileHandle.nullDevice
117-
process.standardOutput = outPipe
118-
119-
try process.run()
120-
// Attach this process to our process group so that Ctrl-C and other signals work
121-
let pgid = tcgetpgrp(STDOUT_FILENO)
122-
if pgid != -1 {
123-
tcsetpgrp(STDOUT_FILENO, process.processIdentifier)
124-
}
125-
let outData = try outPipe.fileHandleForReading.readToEnd()
126-
127-
process.waitUntilExit()
128-
129-
guard process.terminationStatus == 0 else {
130-
print("\(args.first!) exited with non-zero status: \(process.terminationStatus)")
131-
throw Error(message: "\(args.first!) exited with non-zero status: \(process.terminationStatus)")
132-
}
133-
134-
if let outData {
135-
return String(data: outData, encoding: .utf8)
136-
} else {
137-
return nil
138-
}
139-
}
140-
14144
@main
14245
struct BuildSwiftlyRelease: AsyncParsableCommand {
14346
static let configuration = CommandConfiguration(
@@ -178,11 +81,11 @@ struct BuildSwiftlyRelease: AsyncParsableCommand {
17881
}
17982

18083
func assertTool(_ name: String, message: String) async throws -> String {
181-
guard let _ = try? await runProgramOutput(currentPlatform.getShell(), "-c", "which which") else {
84+
guard let _ = try? await currentPlatform.runProgramOutput(currentPlatform.getShell(), "-c", "which which") else {
18285
throw Error(message: "The which command could not be found. Please install it with your package manager.")
18386
}
18487

185-
guard let location = try? await runProgramOutput(currentPlatform.getShell(), "-c", "which \(name)") else {
88+
guard let location = try? await currentPlatform.runProgramOutput(currentPlatform.getShell(), "-c", "which \(name)") else {
18689
throw Error(message: message)
18790
}
18891

@@ -229,7 +132,7 @@ struct BuildSwiftlyRelease: AsyncParsableCommand {
229132
let swift = try await self.assertTool("swift", message: "Please install swift \(requiredSwiftVersion) and make sure that it is added to your path.")
230133

231134
// We also need a swift toolchain with the correct version
232-
guard let swiftVersion = try await runProgramOutput(swift, "--version"), swiftVersion.contains("Swift version \(requiredSwiftVersion)") else {
135+
guard let swiftVersion = try await currentPlatform.runProgramOutput(swift, "--version"), swiftVersion.contains("Swift version \(requiredSwiftVersion)") else {
233136
throw Error(message: "Swiftly releases require a Swift \(requiredSwiftVersion) toolchain available on the path")
234137
}
235138

@@ -275,7 +178,7 @@ struct BuildSwiftlyRelease: AsyncParsableCommand {
275178
try await self.checkGitRepoStatus(git)
276179

277180
// Start with a fresh SwiftPM package
278-
try runProgram(swift, "package", "reset")
181+
try currentPlatform.runProgram(swift, "package", "reset")
279182

280183
// Build a specific version of libarchive with a check on the tarball's SHA256
281184
let libArchiveVersion = "3.7.9"
@@ -289,26 +192,31 @@ struct BuildSwiftlyRelease: AsyncParsableCommand {
289192
try? FileManager.default.createDirectory(atPath: pkgConfigPath, withIntermediateDirectories: true)
290193

291194
try? FileManager.default.removeItem(atPath: libArchivePath)
292-
try runProgram(curl, "-L", "-o", "\(buildCheckoutsDir + "/libarchive-\(libArchiveVersion).tar.gz")", "--remote-name", "--location", "https://github.com/libarchive/libarchive/releases/download/v\(libArchiveVersion)/libarchive-\(libArchiveVersion).tar.gz")
293-
let libArchiveTarShaActual = try await runProgramOutput(sha256sum, "\(buildCheckoutsDir)/libarchive-\(libArchiveVersion).tar.gz")
195+
try currentPlatform.runProgram(curl, "-L", "-o", "\(buildCheckoutsDir + "/libarchive-\(libArchiveVersion).tar.gz")", "--remote-name", "--location", "https://github.com/libarchive/libarchive/releases/download/v\(libArchiveVersion)/libarchive-\(libArchiveVersion).tar.gz")
196+
let libArchiveTarShaActual = try await currentPlatform.runProgramOutput(sha256sum, "\(buildCheckoutsDir)/libarchive-\(libArchiveVersion).tar.gz")
294197
guard let libArchiveTarShaActual, libArchiveTarShaActual.starts(with: libArchiveTarSha) else {
295198
let shaActual = libArchiveTarShaActual ?? "none"
296199
throw Error(message: "The libarchive tar.gz file sha256sum is \(shaActual), but expected \(libArchiveTarSha)")
297200
}
298-
try runProgram(tar, "--directory=\(buildCheckoutsDir)", "-xzf", "\(buildCheckoutsDir)/libarchive-\(libArchiveVersion).tar.gz")
201+
try currentPlatform.runProgram(tar, "--directory=\(buildCheckoutsDir)", "-xzf", "\(buildCheckoutsDir)/libarchive-\(libArchiveVersion).tar.gz")
299202

300203
let cwd = FileManager.default.currentDirectoryPath
301204
FileManager.default.changeCurrentDirectoryPath(libArchivePath)
302205

303206
let swiftVerRegex: Regex<(Substring, Substring)> = try! Regex("Swift version (\\d+\\.\\d+\\.?\\d*) ")
304207

305-
let swiftVerOutput = (try await runProgramOutput(swift, "--version")) ?? ""
208+
let swiftVerOutput = (try await currentPlatform.runProgramOutput(swift, "--version")) ?? ""
306209
guard let swiftVerMatch = try swiftVerRegex.firstMatch(in: swiftVerOutput) else {
307210
throw Error(message: "Unable to detect swift version")
308211
}
309212

310213
let swiftVersion = swiftVerMatch.output.1
311214

215+
let httpExecutor = HTTPRequestExecutorImpl()
216+
guard let swiftRelease = (try await httpExecutor.getReleaseToolchains()).first(where: { $0.name == swiftVersion }) else {
217+
throw Error(message: "Unable to find swift release using swift.org API: \(swiftVersion)")
218+
}
219+
312220
let sdkName = "swift-\(swiftVersion)-RELEASE_static-linux-0.0.1"
313221

314222
#if arch(arm64)
@@ -317,24 +225,17 @@ struct BuildSwiftlyRelease: AsyncParsableCommand {
317225
let arch = "x86_64"
318226
#endif
319227

320-
let swiftReleasesJson = (try await runProgramOutput(curl, "https://www.swift.org/api/v1/install/releases.json")) ?? "[]"
321-
let swiftReleases = try JSONDecoder().decode([SwiftRelease].self, from: swiftReleasesJson.data(using: .utf8)!)
322-
323-
guard let swiftRelease = swiftReleases.first(where: { ($0.name ?? "") == swiftVersion }) else {
324-
throw Error(message: "Unable to find swift release using swift.org API: \(swiftVersion)")
325-
}
326-
327-
guard let sdkPlatform = (swiftRelease.platforms ?? [SwiftPlatform]()).first(where: { ($0.name ?? "") == "Static SDK" }) else {
228+
guard let sdkPlatform = swiftRelease.platforms.first(where: { $0.name == "Static SDK" }) else {
328229
throw Error(message: "Swift release \(swiftVersion) has no Static SDK offering")
329230
}
330231

331-
try runProgram(swift, "sdk", "install", "https://download.swift.org/swift-\(swiftVersion)-release/static-sdk/swift-\(swiftVersion)-RELEASE/swift-\(swiftVersion)-RELEASE_static-linux-0.0.1.artifactbundle.tar.gz", "--checksum", sdkPlatform.checksum ?? "deadbeef")
232+
try currentPlatform.runProgram(swift, "sdk", "install", "https://download.swift.org/swift-\(swiftVersion)-release/static-sdk/swift-\(swiftVersion)-RELEASE/swift-\(swiftVersion)-RELEASE_static-linux-0.0.1.artifactbundle.tar.gz", "--checksum", sdkPlatform.checksum ?? "deadbeef")
332233

333234
var customEnv = ProcessInfo.processInfo.environment
334235
customEnv["CC"] = "\(cwd)/Tools/build-swiftly-release/musl-clang"
335236
customEnv["MUSL_PREFIX"] = "\(FileManager.default.homeDirectoryForCurrentUser.path)/.swiftpm/swift-sdks/\(sdkName).artifactbundle/\(sdkName)/swift-linux-musl/musl-1.2.5.sdk/\(arch)/usr"
336237

337-
try runProgramEnv(
238+
try currentPlatform.runProgram(
338239
"./configure",
339240
"--prefix=\(pkgConfigPath)",
340241
"--enable-shared=no",
@@ -356,18 +257,18 @@ struct BuildSwiftlyRelease: AsyncParsableCommand {
356257
env: customEnv
357258
)
358259

359-
try runProgramEnv(make, env: customEnv)
260+
try currentPlatform.runProgram(make, env: customEnv)
360261

361-
try runProgram(make, "install")
262+
try currentPlatform.runProgram(make, "install")
362263

363264
FileManager.default.changeCurrentDirectoryPath(cwd)
364265

365-
try runProgram(swift, "build", "--swift-sdk", "\(arch)-swift-linux-musl", "--product=swiftly", "--pkg-config-path=\(pkgConfigPath)/lib/pkgconfig", "--static-swift-stdlib", "--configuration=release")
266+
try currentPlatform.runProgram(swift, "build", "--swift-sdk", "\(arch)-swift-linux-musl", "--product=swiftly", "--pkg-config-path=\(pkgConfigPath)/lib/pkgconfig", "--static-swift-stdlib", "--configuration=release")
366267

367268
let releaseDir = cwd + "/.build/release"
368269

369270
// Strip the symbols from the binary to decrease its size
370-
try runProgram(strip, releaseDir + "/swiftly")
271+
try currentPlatform.runProgram(strip, releaseDir + "/swiftly")
371272

372273
try await self.collectLicenses(releaseDir)
373274

@@ -377,7 +278,7 @@ struct BuildSwiftlyRelease: AsyncParsableCommand {
377278
let releaseArchive = "\(releaseDir)/swiftly-\(version)-x86_64.tar.gz"
378279
#endif
379280

380-
try runProgram(tar, "--directory=\(releaseDir)", "-czf", releaseArchive, "swiftly", "LICENSE.txt")
281+
try currentPlatform.runProgram(tar, "--directory=\(releaseDir)", "-czf", releaseArchive, "swiftly", "LICENSE.txt")
381282

382283
print(releaseArchive)
383284

@@ -390,13 +291,13 @@ struct BuildSwiftlyRelease: AsyncParsableCommand {
390291
let testArchive = "\(debugDir)/test-swiftly-linux-x86_64.tar.gz"
391292
#endif
392293

393-
try runProgram(swift, "build", "--swift-sdk", "\(arch)-swift-linux-musl", "--product=test-swiftly", "--pkg-config-path=\(pkgConfigPath)/lib/pkgconfig", "--static-swift-stdlib", "--configuration=debug")
394-
try runProgram(tar, "--directory=\(debugDir)", "-czf", testArchive, "test-swiftly")
294+
try currentPlatform.runProgram(swift, "build", "--swift-sdk", "\(arch)-swift-linux-musl", "--product=test-swiftly", "--pkg-config-path=\(pkgConfigPath)/lib/pkgconfig", "--static-swift-stdlib", "--configuration=debug")
295+
try currentPlatform.runProgram(tar, "--directory=\(debugDir)", "-czf", testArchive, "test-swiftly")
395296

396297
print(testArchive)
397298
}
398299

399-
try runProgram(swift, "sdk", "remove", sdkName)
300+
try currentPlatform.runProgram(swift, "sdk", "remove", sdkName)
400301
}
401302

402303
func buildMacOSRelease(cert: String?, identifier: String) async throws {
@@ -411,11 +312,11 @@ struct BuildSwiftlyRelease: AsyncParsableCommand {
411312

412313
let tar = try await self.assertTool("tar", message: "In order to produce archives there needs to be the `tar` tool that is installed on macOS.")
413314

414-
try runProgram(swift, "package", "clean")
315+
try currentPlatform.runProgram(swift, "package", "clean")
415316

416317
for arch in ["x86_64", "arm64"] {
417-
try runProgram(swift, "build", "--product=swiftly", "--configuration=release", "--arch=\(arch)")
418-
try runProgram(strip, ".build/\(arch)-apple-macosx/release/swiftly")
318+
try currentPlatform.runProgram(swift, "build", "--product=swiftly", "--configuration=release", "--arch=\(arch)")
319+
try currentPlatform.runProgram(strip, ".build/\(arch)-apple-macosx/release/swiftly")
419320
}
420321

421322
let swiftlyBinDir = fs.cwd / ".build/release/.swiftly/bin"
@@ -461,16 +362,16 @@ struct BuildSwiftlyRelease: AsyncParsableCommand {
461362
let pkgFileReconfigured = releaseDir.appendingPathComponent("swiftly-\(self.version)-reconfigured.pkg")
462363
let distFile = releaseDir.appendingPathComponent("distribution.plist")
463364

464-
try runProgram("productbuild", "--synthesize", "--package", pkgFile.path, distFile.path)
365+
try currentPlatform.runProgram("productbuild", "--synthesize", "--package", pkgFile.path, distFile.path)
465366

466367
var distFileContents = try String(contentsOf: distFile, encoding: .utf8)
467368
distFileContents = distFileContents.replacingOccurrences(of: "<choices-outline>", with: "<title>swiftly</title><domains enable_anywhere=\"false\" enable_currentUserHome=\"true\" enable_localSystem=\"false\"/><choices-outline>")
468369
try distFileContents.write(to: distFile, atomically: true, encoding: .utf8)
469370

470371
if let cert = cert {
471-
try runProgram("productbuild", "--distribution", distFile.path, "--package-path", pkgFile.deletingLastPathComponent().path, "--sign", cert, pkgFileReconfigured.path)
372+
try currentPlatform.runProgram("productbuild", "--distribution", distFile.path, "--package-path", pkgFile.deletingLastPathComponent().path, "--sign", cert, pkgFileReconfigured.path)
472373
} else {
473-
try runProgram("productbuild", "--distribution", distFile.path, "--package-path", pkgFile.deletingLastPathComponent().path, pkgFileReconfigured.path)
374+
try currentPlatform.runProgram("productbuild", "--distribution", distFile.path, "--package-path", pkgFile.deletingLastPathComponent().path, pkgFileReconfigured.path)
474375
}
475376
try FileManager.default.removeItem(at: pkgFile)
476377
try FileManager.default.copyItem(atPath: pkgFileReconfigured.path, toPath: pkgFile.path)
@@ -479,8 +380,8 @@ struct BuildSwiftlyRelease: AsyncParsableCommand {
479380

480381
if self.test {
481382
for arch in ["x86_64", "arm64"] {
482-
try runProgram(swift, "build", "--product=test-swiftly", "--configuration=debug", "--arch=\(arch)")
483-
try runProgram(strip, ".build/\(arch)-apple-macosx/release/swiftly")
383+
try currentPlatform.runProgram(swift, "build", "--product=test-swiftly", "--configuration=debug", "--arch=\(arch)")
384+
try currentPlatform.runProgram(strip, ".build/\(arch)-apple-macosx/release/swiftly")
484385
}
485386

486387
let testArchive = releaseDir.appendingPathComponent("test-swiftly-macos.tar.gz")
@@ -491,7 +392,7 @@ struct BuildSwiftlyRelease: AsyncParsableCommand {
491392
.create(output: swiftlyBinDir / "swiftly")
492393
.runEcho(currentPlatform)
493394

494-
try runProgram(tar, "--directory=.build/x86_64-apple-macosx/debug", "-czf", testArchive.path, "test-swiftly")
395+
try currentPlatform.runProgram(tar, "--directory=.build/x86_64-apple-macosx/debug", "-czf", testArchive.path, "test-swiftly")
495396

496397
print(testArchive.path)
497398
}

0 commit comments

Comments
 (0)