From 4e157ddbce3666baa0ce347609b85741fe138c8a Mon Sep 17 00:00:00 2001 From: "Jesse L. Zamora" Date: Fri, 15 Aug 2025 01:06:31 +0000 Subject: [PATCH 1/4] Add missing libcurl4-openssl-dev dependency for Ubuntu and Debian - Created list of commonPackages for both distros to share dependencies between releases. --- .../PlatformModels/LinuxDistribution.swift | 71 ++++++++----------- 1 file changed, 31 insertions(+), 40 deletions(-) diff --git a/Sources/SwiftSDKGenerator/PlatformModels/LinuxDistribution.swift b/Sources/SwiftSDKGenerator/PlatformModels/LinuxDistribution.swift index 7f64509..9f9cd96 100644 --- a/Sources/SwiftSDKGenerator/PlatformModels/LinuxDistribution.swift +++ b/Sources/SwiftSDKGenerator/PlatformModels/LinuxDistribution.swift @@ -50,49 +50,41 @@ public enum LinuxDistribution: Hashable, Sendable { } } + private var commonPackages: [String] { + [ + "libc6", + "libc6-dev", + "libgcc-s1", + "libstdc++6", + "linux-libc-dev", + "zlib1g", + "zlib1g-dev", + "libcurl4-openssl-dev", + ] + } + public var requiredPackages: [String] { switch self { case .focal: - return [ - "libc6", - "libc6-dev", - "libgcc-s1", + return commonPackages + [ "libgcc-10-dev", "libicu66", "libicu-dev", "libstdc++-10-dev", - "libstdc++6", - "linux-libc-dev", - "zlib1g", - "zlib1g-dev", ] case .jammy: - return [ - "libc6", - "libc6-dev", - "libgcc-s1", + return commonPackages + [ "libgcc-12-dev", "libicu70", "libicu-dev", "libstdc++-12-dev", - "libstdc++6", - "linux-libc-dev", - "zlib1g", - "zlib1g-dev", ] case .noble: - return [ - "libc6", - "libc6-dev", - "libgcc-s1", + return commonPackages + [ "libgcc-13-dev", "libicu74", "libicu-dev", "libstdc++-13-dev", - "libstdc++6", - "linux-libc-dev", - "zlib1g", - "zlib1g-dev", ] } } @@ -121,35 +113,34 @@ public enum LinuxDistribution: Hashable, Sendable { } } + private var commonPackages: [String] { + [ + "libc6", + "libc6-dev", + "libgcc-s1", + "libstdc++6", + "linux-libc-dev", + "zlib1g", + "zlib1g-dev", + "libcurl4-openssl-dev", + ] + } + public var requiredPackages: [String] { switch self { case .bullseye: - return [ - "libc6", - "libc6-dev", - "libgcc-s1", + return commonPackages + [ "libgcc-10-dev", "libicu67", "libicu-dev", "libstdc++-10-dev", - "libstdc++6", - "linux-libc-dev", - "zlib1g", - "zlib1g-dev", ] case .bookworm: - return [ - "libc6", - "libc6-dev", - "libgcc-s1", + return commonPackages + [ "libgcc-12-dev", "libicu72", "libicu-dev", "libstdc++-12-dev", - "libstdc++6", - "linux-libc-dev", - "zlib1g", - "zlib1g-dev", ] } } From a62a3cd320bf57e809a9e6653fc3af90c6e4bf17 Mon Sep 17 00:00:00 2001 From: "Jesse L. Zamora" Date: Fri, 15 Aug 2025 01:13:14 +0000 Subject: [PATCH 2/4] Add EndToEnd testcase for curl dependency, improve logging around this --- Tests/SwiftSDKGeneratorTests/EndToEndTests.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Tests/SwiftSDKGeneratorTests/EndToEndTests.swift b/Tests/SwiftSDKGeneratorTests/EndToEndTests.swift index 3bfea60..fb0f6ef 100644 --- a/Tests/SwiftSDKGeneratorTests/EndToEndTests.swift +++ b/Tests/SwiftSDKGeneratorTests/EndToEndTests.swift @@ -103,11 +103,11 @@ func buildSDK(_ logger: Logger, scratchPath: String, withArguments runArguments: } private let testcases = [ - #""" + "helloWorld": #""" // Default program generated by swift package init print("Hello, world!") """#, - #""" + "libcNonshared": #""" // Check that libc_nonshared.a is linked properly import Foundation @@ -117,6 +117,10 @@ private let testcases = [ atexit(fin) """#, + "curlDependency": #""" + // Check that FoundationNetworking can be linked with libcurl4.a + import FoundationNetworking + """#, ] final class RepeatedBuildTests: XCTestCase { @@ -325,7 +329,6 @@ func buildTestcase(_ logger: Logger, testcase: String, bundleName: String, tempD func buildTestcases(config: SDKConfiguration) async throws { var logger = Logger(label: "EndToEndTests") - logger[metadataKey: "testcase"] = "testPackageInitExecutable" if ProcessInfo.processInfo.environment.keys.contains("JENKINS_URL") || ProcessInfo.processInfo.environment.keys.contains("GITHUB_ACTIONS") @@ -359,7 +362,8 @@ func buildTestcases(config: SDKConfiguration) async throws { try? await Shell.run("swift experimental-sdk remove \(bundleName)") } - for testcase in testcases { + for (name, testcase) in testcases { + logger[metadataKey: "testcase"] = .string(name) do { try await FileManager.default.withTemporaryDirectory(logger: logger) { tempDir in try await buildTestcase( From a59dcb8aafca443cf5bc02cb79885daff6eaf82c Mon Sep 17 00:00:00 2001 From: "Jesse L. Zamora" Date: Fri, 15 Aug 2025 01:38:13 +0000 Subject: [PATCH 3/4] Improve running EndToEnd testcases by running in parallel, improve logging some more --- .../EndToEndTests.swift | 63 ++++++++++--------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/Tests/SwiftSDKGeneratorTests/EndToEndTests.swift b/Tests/SwiftSDKGeneratorTests/EndToEndTests.swift index fb0f6ef..2b183b3 100644 --- a/Tests/SwiftSDKGeneratorTests/EndToEndTests.swift +++ b/Tests/SwiftSDKGeneratorTests/EndToEndTests.swift @@ -59,19 +59,21 @@ func buildSDK(_ logger: Logger, scratchPath: String, withArguments runArguments: async throws -> String { var logger = logger - logger[metadataKey: "runArguments"] = "\"\(runArguments)\"" - logger[metadataKey: "scratchPath"] = "\(scratchPath)" + logger[metadataKey: "scratchPath"] = .string(scratchPath) - logger.info("Building Swift SDK") + let packageDirectory = FilePath(#filePath) + .removingLastComponent() + .removingLastComponent() - var packageDirectory = FilePath(#filePath) - packageDirectory.removeLastComponent() - packageDirectory.removeLastComponent() + logger.info("Building SDK generator") + _ = try await Shell.readStdout( + "cd \(packageDirectory) && swift build --scratch-path \"\(scratchPath)\" --product swift-sdk-generator" + ) + logger.info("Building Swift SDK", metadata: ["runArguments": .string(runArguments)]) let generatorOutput = try await Shell.readStdout( "cd \(packageDirectory) && swift run --scratch-path \"\(scratchPath)\" swift-sdk-generator make-linux-sdk \(runArguments)" ) - logger.info("Finished building Swift SDK") let installCommand = try XCTUnwrap( generatorOutput.split(separator: "\n").first { @@ -84,18 +86,16 @@ func buildSDK(_ logger: Logger, scratchPath: String, withArguments runArguments: ).stem logger[metadataKey: "bundleName"] = "\(bundleName)" - logger.info("Checking installed Swift SDKs") + // Make sure this bundle hasn't been installed already. let installedSDKs = try await Shell.readStdout("swift experimental-sdk list").components( separatedBy: "\n" ) - - // Make sure this bundle hasn't been installed already. if installedSDKs.contains(bundleName) { - logger.info("Removing existing Swift SDK") + logger.info("Removing existing Swift SDK", metadata: ["bundleName": .string(bundleName)]) try await Shell.run("swift experimental-sdk remove \(bundleName)") } - logger.info("Installing new Swift SDK") + logger.info("Installing new Swift SDK", metadata: ["bundleName": .string(bundleName)]) let installOutput = try await Shell.readStdout(String(installCommand)) XCTAssertTrue(installOutput.contains("successfully installed")) @@ -262,7 +262,6 @@ func buildTestcase(_ logger: Logger, testcase: String, bundleName: String, tempD // This is a workaround for if Swift 6.0 is used as the host toolchain to run the generator. // We manually set the swift-tools-version to 5.9 to support building our test cases. - logger.info("Updating minimum swift-tools-version in test project...") let package_swift = testPackageURL.appendingPathComponent("Package.swift") let text = try String(contentsOf: package_swift, encoding: .utf8) var lines = text.components(separatedBy: .newlines) @@ -353,8 +352,7 @@ func buildTestcases(config: SDKConfiguration) async throws { withArguments: config.sdkGeneratorArguments ) } - - logger.info("Built Swift SDK") + logger[metadataKey: "bundleName"] = .string(bundleName) // Cleanup func cleanupSDK() async { @@ -362,20 +360,29 @@ func buildTestcases(config: SDKConfiguration) async throws { try? await Shell.run("swift experimental-sdk remove \(bundleName)") } - for (name, testcase) in testcases { - logger[metadataKey: "testcase"] = .string(name) - do { - try await FileManager.default.withTemporaryDirectory(logger: logger) { tempDir in - try await buildTestcase( - logger, - testcase: testcase, - bundleName: bundleName, - tempDir: tempDir - ) + // Let's run the test cases in parallel to speed things up! + logger.info("Running test cases...") + try await withThrowingTaskGroup(of: Void.self) { group in + for (name, testcase) in testcases { + var testcaseLogger = logger + testcaseLogger[metadataKey: "testcase"] = .string(name) + group.addTask { + try await FileManager.default.withTemporaryDirectory(logger: testcaseLogger) { tempDir in + try await buildTestcase( + testcaseLogger, + testcase: testcase, + bundleName: bundleName, + tempDir: tempDir + ) + } + } + + do { + try await group.next() + } catch { + await cleanupSDK() + throw error } - } catch { - await cleanupSDK() - throw error } } From 8fd406343b6b61750ca3213e28bfab8c9e4ad978 Mon Sep 17 00:00:00 2001 From: "Jesse L. Zamora" Date: Fri, 15 Aug 2025 14:29:39 +0000 Subject: [PATCH 4/4] Resolve 5.9/5.10 concurrency error by copying testcaseLogger --- Tests/SwiftSDKGeneratorTests/EndToEndTests.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Tests/SwiftSDKGeneratorTests/EndToEndTests.swift b/Tests/SwiftSDKGeneratorTests/EndToEndTests.swift index 2b183b3..b8af0f9 100644 --- a/Tests/SwiftSDKGeneratorTests/EndToEndTests.swift +++ b/Tests/SwiftSDKGeneratorTests/EndToEndTests.swift @@ -360,12 +360,17 @@ func buildTestcases(config: SDKConfiguration) async throws { try? await Shell.run("swift experimental-sdk remove \(bundleName)") } + func testcaseLogger(_ name: String) -> Logger { + var testcaseLogger = logger + testcaseLogger[metadataKey: "testcase"] = .string(name) + return testcaseLogger + } + // Let's run the test cases in parallel to speed things up! logger.info("Running test cases...") try await withThrowingTaskGroup(of: Void.self) { group in for (name, testcase) in testcases { - var testcaseLogger = logger - testcaseLogger[metadataKey: "testcase"] = .string(name) + let testcaseLogger = testcaseLogger(name) group.addTask { try await FileManager.default.withTemporaryDirectory(logger: testcaseLogger) { tempDir in try await buildTestcase(