diff --git a/Sources/SwiftSDKGenerator/Extensions/String+hasAnyPrefix.swift b/Sources/SwiftSDKGenerator/Extensions/String+hasAnyPrefix.swift new file mode 100644 index 0000000..139e371 --- /dev/null +++ b/Sources/SwiftSDKGenerator/Extensions/String+hasAnyPrefix.swift @@ -0,0 +1,10 @@ +extension String { + func hasAnyPrefix(from array: [String]) -> Bool { + for item in array { + if self.hasPrefix(item) { + return true + } + } + return false + } +} diff --git a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Metadata.swift b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Metadata.swift index b9985dc..8388617 100644 --- a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Metadata.swift +++ b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Metadata.swift @@ -112,4 +112,36 @@ extension SwiftSDKGenerator { ) ) } + + struct SDKSettings: Codable { + var DisplayName: String + var Version: String + var VersionMap: [String: String] = [:] + var CanonicalName: String + } + + /// Generates an `SDKSettings.json` file that looks like this: + /// + /// ```json + /// { + /// "CanonicalName" : "-swift-linux-[gnu|gnueabihf]", + /// "DisplayName" : "Swift SDK for ()", + /// "Version" : "0.0.1", + /// "VersionMap" : { + /// + /// } + /// } + /// ``` + func generateSDKSettingsFile(sdkDirPath: FilePath, distribution: LinuxDistribution) throws { + logger.info("Generating SDKSettings.json file to silence cross-compilation warnings...") + + let sdkSettings = SDKSettings( + DisplayName: "Swift SDK for \(distribution) (\(targetTriple.archName))", + Version: bundleVersion, + CanonicalName: targetTriple.triple.replacingOccurrences(of: "unknown", with: "swift") + ) + + let sdkSettingsFilePath = sdkDirPath.appending("SDKSettings.json") + try writeFile(at: sdkSettingsFilePath, encoder.encode(sdkSettings)) + } } diff --git a/Sources/SwiftSDKGenerator/SwiftSDKRecipes/LinuxRecipe.swift b/Sources/SwiftSDKGenerator/SwiftSDKRecipes/LinuxRecipe.swift index 6d24169..9af0afc 100644 --- a/Sources/SwiftSDKGenerator/SwiftSDKRecipes/LinuxRecipe.swift +++ b/Sources/SwiftSDKGenerator/SwiftSDKRecipes/LinuxRecipe.swift @@ -195,8 +195,7 @@ public struct LinuxRecipe: SwiftSDKRecipe { if self.hostSwiftSource == .preinstalled { // Swift 5.9 and 5.10 require `supportedTriples` to be set in info.json. // FIXME: This can be removed once the SDK generator does not support 5.9/5.10 any more. - if self.versionsConfiguration.swiftVersion.hasPrefix("5.9") - || self.versionsConfiguration.swiftVersion.hasPrefix("5.10") { + if self.versionsConfiguration.swiftVersion.hasAnyPrefix(from: ["5.9", "5.10"]) { return [ Triple("x86_64-unknown-linux-gnu"), Triple("aarch64-unknown-linux-gnu"), @@ -292,13 +291,18 @@ public struct LinuxRecipe: SwiftSDKRecipe { try await generator.fixAbsoluteSymlinks(sdkDirPath: sdkDirPath) + // Swift 6.1 and later do not throw warnings about the SDKSettings.json file missing, + // so they don't need this file. + if self.versionsConfiguration.swiftVersion.hasAnyPrefix(from: ["5.9", "5.10", "6.0"]) { + try await generator.generateSDKSettingsFile(sdkDirPath: sdkDirPath, distribution: linuxDistribution) + } + if self.hostSwiftSource != .preinstalled { if self.mainHostTriple.os != .linux && !self.versionsConfiguration.swiftVersion.hasPrefix("6.0") { try await generator.prepareLLDLinker(engine, llvmArtifact: downloadableArtifacts.hostLLVM) } - if self.versionsConfiguration.swiftVersion.hasPrefix("5.9") || - self.versionsConfiguration.swiftVersion.hasPrefix("5.10") { + if self.versionsConfiguration.swiftVersion.hasAnyPrefix(from: ["5.9", "5.10"]) { try await generator.symlinkClangHeaders() } diff --git a/Tests/SwiftSDKGeneratorTests/Generator/SwiftSDKGenerator+MetadataTests.swift b/Tests/SwiftSDKGeneratorTests/Generator/SwiftSDKGenerator+MetadataTests.swift new file mode 100644 index 0000000..9baf7de --- /dev/null +++ b/Tests/SwiftSDKGeneratorTests/Generator/SwiftSDKGenerator+MetadataTests.swift @@ -0,0 +1,72 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2022-2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Logging +import SystemPackage +import XCTest + +@testable import SwiftSDKGenerator + +final class SwiftSDKGeneratorMetadataTests: XCTestCase { + let logger = Logger(label: "SwiftSDKGeneratorMetadataTests") + + func testGenerateSDKSettingsFile() async throws { + let testCases = [ + ( + bundleVersion: "0.0.1", + targetTriple: Triple("x86_64-unknown-linux-gnu"), + expectedCanonicalName: "x86_64-swift-linux-gnu" + ), + ( + bundleVersion: "0.0.2", + targetTriple: Triple("aarch64-unknown-linux-gnu"), + expectedCanonicalName: "aarch64-swift-linux-gnu" + ), + ( + bundleVersion: "0.0.3", + targetTriple: Triple("armv7-unknown-linux-gnueabihf"), + expectedCanonicalName: "armv7-swift-linux-gnueabihf" + ) + ] + + for testCase in testCases { + let sdk = try await SwiftSDKGenerator( + bundleVersion: testCase.bundleVersion, + targetTriple: testCase.targetTriple, + artifactID: "6.0.3-RELEASE_ubuntu_jammy_\(testCase.targetTriple.archName)", + isIncremental: false, + isVerbose: false, + logger: logger + ) + let linuxDistribution = try LinuxDistribution(name: .ubuntu, version: "22.04") + + let sdkDirPath = FilePath(".") + try await sdk.generateSDKSettingsFile(sdkDirPath: sdkDirPath, distribution: linuxDistribution) + + // Make sure the file exists + let sdkSettingsFile = sdkDirPath.appending("SDKSettings.json") + let fileExists = await sdk.doesFileExist(at: sdkSettingsFile) + XCTAssertTrue(fileExists) + + // Read back file, make sure it contains the expected data + let data = String(data: try await sdk.readFile(at: sdkSettingsFile), encoding: .utf8) + XCTAssertNotNil(data) + XCTAssertTrue(data!.contains(testCase.bundleVersion)) + XCTAssertTrue(data!.contains("(\(testCase.targetTriple.archName))")) + XCTAssertTrue(data!.contains(linuxDistribution.description)) + XCTAssertTrue(data!.contains(testCase.expectedCanonicalName)) + + // Cleanup + try await sdk.removeFile(at: sdkSettingsFile) + } + } +}