diff --git a/Sources/SWBCore/SwiftSDK.swift b/Sources/SWBCore/SwiftSDK.swift index eb443381..51bf0003 100644 --- a/Sources/SWBCore/SwiftSDK.swift +++ b/Sources/SWBCore/SwiftSDK.swift @@ -85,11 +85,11 @@ public struct SwiftSDK: Sendable { } /// Find Swift SDKs installed by SwiftPM. - public static func findSDKs(targetTriples: [String], fs: any FSProxy, hostOperatingSystem: OperatingSystem) throws -> [SwiftSDK] { + public static func findSDKs(targetTriples: [String]?, fs: any FSProxy, hostOperatingSystem: OperatingSystem) throws -> [SwiftSDK] { return try findSDKs(swiftSDKsDirectory: defaultSwiftSDKsDirectory(hostOperatingSystem: hostOperatingSystem), targetTriples: targetTriples, fs: fs) } - private static func findSDKs(swiftSDKsDirectory: Path, targetTriples: [String], fs: any FSProxy) throws -> [SwiftSDK] { + private static func findSDKs(swiftSDKsDirectory: Path, targetTriples: [String]?, fs: any FSProxy) throws -> [SwiftSDK] { var sdks: [SwiftSDK] = [] // Find .artifactbundle in the SDK directory (e.g. ~/Library/org.swift.swiftpm/swift-sdks) for artifactBundle in try fs.listdir(swiftSDKsDirectory) { @@ -118,7 +118,7 @@ public struct SwiftSDK: Sendable { } /// Find Swift SDKs in an artifact bundle supporting one of the given targets. - private static func findSDKs(artifactBundle: Path, targetTriples: [String], fs: any FSProxy) throws -> [SwiftSDK] { + private static func findSDKs(artifactBundle: Path, targetTriples: [String]?, fs: any FSProxy) throws -> [SwiftSDK] { // Load info.json from the artifact bundle let infoPath = artifactBundle.join("info.json") guard try fs.isFile(infoPath) else { return [] } @@ -145,7 +145,9 @@ public struct SwiftSDK: Sendable { guard let sdk = try SwiftSDK(identifier: identifier, version: artifact.version, path: sdkPath, fs: fs) else { continue } // Filter out SDKs that don't support any of the target triples. - guard targetTriples.contains(where: { sdk.targetTriples[$0] != nil }) else { continue } + if let targetTriples { + guard targetTriples.contains(where: { sdk.targetTriples[$0] != nil }) else { continue } + } sdks.append(sdk) } } diff --git a/Sources/SWBGenericUnixPlatform/Plugin.swift b/Sources/SWBGenericUnixPlatform/Plugin.swift index 632a4cae..dc287793 100644 --- a/Sources/SWBGenericUnixPlatform/Plugin.swift +++ b/Sources/SWBGenericUnixPlatform/Plugin.swift @@ -49,12 +49,6 @@ struct SwiftTargetInfo: Decodable { let target: TargetInfo } -extension SwiftTargetInfo.TargetInfo { - var tripleVersion: String? { - triple != unversionedTriple && triple.system.hasPrefix(unversionedTriple.system) ? String(triple.system.dropFirst(unversionedTriple.system.count)).nilIfEmpty : nil - } -} - struct GenericUnixDeveloperDirectoryExtension: DeveloperDirectoryExtension { func fallbackDeveloperDirectory(hostOperatingSystem: OperatingSystem) async throws -> Core.DeveloperPath? { if hostOperatingSystem == .windows || hostOperatingSystem == .macOS { @@ -82,13 +76,12 @@ struct GenericUnixPlatformSpecsExtension: SpecificationsExtension { struct GenericUnixPlatformInfoExtension: PlatformInfoExtension { func additionalPlatforms(context: any PlatformInfoExtensionAdditionalPlatformsContext) throws -> [(path: Path, data: [String: PropertyListItem])] { - let operatingSystem = context.hostOperatingSystem - guard operatingSystem.createFallbackSystemToolchain else { - return [] - } - - return try [ - (.root, [ + return try OperatingSystem.createFallbackSystemToolchains.compactMap { operatingSystem in + // Only create platforms if the host OS allows a fallback toolchain, or we're cross compiling. + guard operatingSystem.createFallbackSystemToolchain || operatingSystem != context.hostOperatingSystem else { + return nil + } + return try (.root, [ "Type": .plString("Platform"), "Name": .plString(operatingSystem.xcodePlatformName), "Identifier": .plString(operatingSystem.xcodePlatformName), @@ -97,7 +90,7 @@ struct GenericUnixPlatformInfoExtension: PlatformInfoExtension { "FamilyIdentifier": .plString(operatingSystem.xcodePlatformName), "IsDeploymentPlatform": .plString("YES"), ]) - ] + } } } @@ -105,70 +98,148 @@ struct GenericUnixSDKRegistryExtension: SDKRegistryExtension { let plugin: GenericUnixPlugin func additionalSDKs(context: any SDKRegistryExtensionAdditionalSDKsContext) async throws -> [(path: Path, platform: SWBCore.Platform?, data: [String: PropertyListItem])] { - let operatingSystem = context.hostOperatingSystem - guard operatingSystem.createFallbackSystemToolchain, let platform = try context.platformRegistry.lookup(name: operatingSystem.xcodePlatformName), let swift = plugin.swiftExecutablePath(fs: context.fs) else { - return [] - } + return try await OperatingSystem.createFallbackSystemToolchains.asyncMap { operatingSystem in + // Only create SDKs if the host OS allows a fallback toolchain, or we're cross compiling. + guard operatingSystem.createFallbackSystemToolchain || operatingSystem != context.hostOperatingSystem else { + return nil + } - let defaultProperties: [String: PropertyListItem] - switch operatingSystem { - case .linux, .freebsd: - defaultProperties = [ - // Workaround to avoid `-dependency_info`. - "LD_DEPENDENCY_INFO_FILE": .plString(""), - - "GENERATE_TEXT_BASED_STUBS": "NO", - "GENERATE_INTERMEDIATE_TEXT_BASED_STUBS": "NO", - - "CHOWN": "/usr/bin/chown", - "AR": "llvm-ar", - ] - default: - defaultProperties = [:] - } + // Don't create any SDKs for the platform if the platform itself isn't registered. + guard let platform = try context.platformRegistry.lookup(name: operatingSystem.xcodePlatformName) else { + return nil + } - let tripleEnvironment: String - switch operatingSystem { - case .linux: - tripleEnvironment = "gnu" - default: - tripleEnvironment = "" - } + var defaultProperties: [String: PropertyListItem] + switch operatingSystem { + case .linux, .freebsd: + defaultProperties = [ + // Workaround to avoid `-dependency_info`. + "LD_DEPENDENCY_INFO_FILE": .plString(""), - let swiftTargetInfo = try await plugin.swiftTargetInfo(swiftExecutablePath: swift) + "GENERATE_TEXT_BASED_STUBS": "NO", + "GENERATE_INTERMEDIATE_TEXT_BASED_STUBS": "NO", - let deploymentTargetSettings: [String: PropertyListItem] - if operatingSystem == .freebsd { - guard let tripleVersion = swiftTargetInfo.target.tripleVersion else { - throw StubError.error("Unknown FreeBSD triple version") + "CHOWN": "/usr/bin/chown", + "AR": "llvm-ar", + ] + default: + defaultProperties = [:] } - deploymentTargetSettings = [ - "DeploymentTargetSettingName": .plString("FREEBSD_DEPLOYMENT_TARGET"), - "DefaultDeploymentTarget": .plString(tripleVersion), - "MinimumDeploymentTarget": .plString(tripleVersion), - "MaximumDeploymentTarget": .plString(tripleVersion), - ] - } else { - deploymentTargetSettings = [:] - } - return try [(.root, platform, [ - "Type": .plString("SDK"), - "Version": .plString(Version(ProcessInfo.processInfo.operatingSystemVersion).zeroTrimmed.description), - "CanonicalName": .plString(operatingSystem.xcodePlatformName), - "IsBaseSDK": .plBool(true), - "DefaultProperties": .plDict([ - "PLATFORM_NAME": .plString(operatingSystem.xcodePlatformName), - ].merging(defaultProperties, uniquingKeysWith: { _, new in new })), - "SupportedTargets": .plDict([ - operatingSystem.xcodePlatformName: .plDict([ - "Archs": .plArray([.plString(Architecture.hostStringValue ?? "unknown")]), - "LLVMTargetTripleEnvironment": .plString(tripleEnvironment), - "LLVMTargetTripleSys": .plString(operatingSystem.xcodePlatformName), - "LLVMTargetTripleVendor": .plString("unknown"), - ].merging(deploymentTargetSettings, uniquingKeysWith: { _, new in new })) - ]), - ])] + if operatingSystem == .freebsd || operatingSystem != context.hostOperatingSystem { + // FreeBSD is always LLVM-based, and if we're cross-compiling, use lld + defaultProperties["ALTERNATE_LINKER"] = "lld" + } + + let tripleEnvironment: String + switch operatingSystem { + case .linux: + tripleEnvironment = "gnu" + default: + tripleEnvironment = "" + } + + let swiftSDK: SwiftSDK? + let sysroot: Path + let architectures: [String] + let tripleVersion: String? + let customProperties: [String: PropertyListItem] + if operatingSystem == context.hostOperatingSystem { + swiftSDK = nil + sysroot = .root + architectures = [Architecture.hostStringValue ?? "unknown"] + tripleVersion = nil + customProperties = [:] + } else { + do { + let swiftSDKs = try SwiftSDK.findSDKs( + targetTriples: nil, + fs: context.fs, + hostOperatingSystem: context.hostOperatingSystem + ).filter { sdk in + try sdk.targetTriples.keys.map { + try LLVMTriple($0) + }.contains { + switch operatingSystem { + case .linux: + $0.system == "linux" && $0.environment?.hasPrefix("gnu") == true + case .freebsd: + $0.system == "freebsd" + case .openbsd: + $0.system == "openbsd" + default: + throw StubError.error("Unhandled operating system: \(operatingSystem)") + } + } + } + // FIXME: Do something better than just skipping the platform if more than one SDK matches + swiftSDK = swiftSDKs.only + guard let swiftSDK else { + return nil + } + sysroot = swiftSDK.path + architectures = try swiftSDK.targetTriples.keys.map { try LLVMTriple($0).arch }.sorted() + tripleVersion = try Set(swiftSDK.targetTriples.keys.compactMap { try LLVMTriple($0).systemVersion }).only?.description + customProperties = try Dictionary(uniqueKeysWithValues: swiftSDK.targetTriples.map { targetTriple in + try ("__SYSROOT_\(LLVMTriple(targetTriple.key).arch)", .plString(swiftSDK.path.join(targetTriple.value.sdkRootPath).str)) + }).merging([ + "SYSROOT": "$(__SYSROOT_$(CURRENT_ARCH))", + + // ld.lld: error: -r and --export-dynamic (-rdynamic) may not be used together + "LD_EXPORT_GLOBAL_SYMBOLS": "YES", + ], uniquingKeysWith: { _, new in new }) + } catch { + // FIXME: Handle errors? + return nil + } + } + + let deploymentTargetSettings: [String: PropertyListItem] + if operatingSystem == .freebsd { + let realTripleVersion: String + if context.hostOperatingSystem == operatingSystem { + guard let swift = plugin.swiftExecutablePath(fs: context.fs) else { + throw StubError.error("Cannot locate swift executable path for determining the FreeBSD triple version") + } + let swiftTargetInfo = try await plugin.swiftTargetInfo(swiftExecutablePath: swift) + guard let foundTripleVersion = try swiftTargetInfo.target.triple.version?.description else { + throw StubError.error("Unknown FreeBSD triple version") + } + realTripleVersion = foundTripleVersion + } else if let tripleVersion { + realTripleVersion = tripleVersion + } else { + return nil // couldn't compute triple version for FreeBSD + } + deploymentTargetSettings = [ + "DeploymentTargetSettingName": .plString("FREEBSD_DEPLOYMENT_TARGET"), + "DefaultDeploymentTarget": .plString(realTripleVersion), + "MinimumDeploymentTarget": .plString(realTripleVersion), + "MaximumDeploymentTarget": .plString(realTripleVersion), + ] + } else { + deploymentTargetSettings = [:] + } + + return try (sysroot, platform, [ + "Type": .plString("SDK"), + "Version": .plString(Version(ProcessInfo.processInfo.operatingSystemVersion).zeroTrimmed.description), + "CanonicalName": .plString(operatingSystem.xcodePlatformName), + "IsBaseSDK": .plBool(true), + "DefaultProperties": .plDict([ + "PLATFORM_NAME": .plString(operatingSystem.xcodePlatformName), + ].merging(defaultProperties, uniquingKeysWith: { _, new in new })), + "CustomProperties": .plDict(customProperties), + "SupportedTargets": .plDict([ + operatingSystem.xcodePlatformName: .plDict([ + "Archs": .plArray(architectures.map { .plString($0) }), + "LLVMTargetTripleEnvironment": .plString(tripleEnvironment), + "LLVMTargetTripleSys": .plString(operatingSystem.xcodePlatformName), + "LLVMTargetTripleVendor": .plString("unknown"), + ].merging(deploymentTargetSettings, uniquingKeysWith: { _, new in new })) + ]), + ]) + }.compactMap { $0 } } } @@ -218,8 +289,12 @@ struct GenericUnixToolchainRegistryExtension: ToolchainRegistryExtension { } extension OperatingSystem { + static var createFallbackSystemToolchains: [OperatingSystem] { + [.linux, .freebsd, .openbsd] + } + /// Whether the Core is allowed to create a fallback toolchain, SDK, and platform for this operating system in cases where no others have been provided. - var createFallbackSystemToolchain: Bool { - return self == .linux || self == .freebsd || self == .openbsd + fileprivate var createFallbackSystemToolchain: Bool { + return Self.createFallbackSystemToolchains.contains(self) } } diff --git a/Sources/SWBGenericUnixPlatform/Specs/UnixCompile.xcspec b/Sources/SWBGenericUnixPlatform/Specs/UnixCompile.xcspec index eb35188c..dac3119c 100644 --- a/Sources/SWBGenericUnixPlatform/Specs/UnixCompile.xcspec +++ b/Sources/SWBGenericUnixPlatform/Specs/UnixCompile.xcspec @@ -38,6 +38,12 @@ { Name = SDKROOT; Type = Path; + CommandLineArgs = (); + }, + { + Name = SYSROOT; + DefaultValue = "$(SDKROOT)"; + Type = Path; CommandLineFlag = "--sysroot"; }, { @@ -91,6 +97,12 @@ { Name = SDKROOT; Type = Path; + CommandLineArgs = (); + }, + { + Name = SYSROOT; + DefaultValue = "$(SDKROOT)"; + Type = Path; CommandLineFlag = "--sysroot"; }, ); @@ -106,6 +118,12 @@ { Name = SDKROOT; Type = Path; + CommandLineArgs = (); + }, + { + Name = SYSROOT; + DefaultValue = "$(SDKROOT)"; + Type = Path; CommandLineFlag = "--sysroot"; }, ); diff --git a/Sources/SWBGenericUnixPlatform/Specs/UnixLd.xcspec b/Sources/SWBGenericUnixPlatform/Specs/UnixLd.xcspec index 97316ed8..fe7fbd33 100644 --- a/Sources/SWBGenericUnixPlatform/Specs/UnixLd.xcspec +++ b/Sources/SWBGenericUnixPlatform/Specs/UnixLd.xcspec @@ -53,7 +53,7 @@ { Name = CLANG_SDKROOT_LINKER_INPUT; Type = Path; - DefaultValue = "$(SDKROOT)"; + DefaultValue = "$(SYSROOT:default=$(SDKROOT))"; Condition = "$(LINKER_DRIVER) == clang"; CommandLineFlag = "--sysroot"; IsInputDependency = Yes; @@ -61,7 +61,7 @@ { Name = SWIFTC_SDKROOT_LINKER_INPUT; Type = Path; - DefaultValue = "$(SDKROOT)"; + DefaultValue = "$(SYSROOT:default=$(SDKROOT))"; Condition = "$(LINKER_DRIVER) == swiftc"; CommandLineFlag = "-sysroot"; IsInputDependency = Yes; diff --git a/Sources/SWBUtil/LLVMTriple.swift b/Sources/SWBUtil/LLVMTriple.swift index 2ec27fa5..9dfd434e 100644 --- a/Sources/SWBUtil/LLVMTriple.swift +++ b/Sources/SWBUtil/LLVMTriple.swift @@ -14,23 +14,59 @@ public struct LLVMTriple: Decodable, Equatable, Sendable, CustomStringConvertibl public var arch: String public var vendor: String public var system: String - public var environment: String? + public var systemVersion: Version? + + public var environment: String? { + get { _environment?.environment } + set { + if let newValue { + var env = _environment ?? Environment(environment: newValue, environmentVersion: nil) + env.environment = newValue + _environment = env + } else { + _environment = nil + } + } + } + + public var environmentVersion: Version? { + get { _environment?.environmentVersion } + set { + switch (_environment, newValue) { + case (nil, nil): + return + case (nil, let newValue): + fatalError("Can't set environmentVersion when environment is not set") + case (var env?, let newValue): + env.environmentVersion = newValue + _environment = env + } + } + } + + private struct Environment: Equatable, Sendable { + var environment: String + var environmentVersion: Version? + } + private var _environment: Environment? public var description: String { if let environment { - return "\(arch)-\(vendor)-\(system)-\(environment)" + return "\(arch)-\(vendor)-\(system)\(systemVersion?.description ?? "")-\(environment)\(environmentVersion?.description ?? "")" } - return "\(arch)-\(vendor)-\(system)" + return "\(arch)-\(vendor)-\(system)\(systemVersion?.description ?? "")" } public init(_ string: String) throws { - guard let match = try #/(?[^-]+)-(?[^-]+)-(?[^-]+)(-(?[^-]+))?/#.wholeMatch(in: string) else { + guard let match = try #/(?[^-]+)-(?[^-]+)-(?[a-zA-Z_]+)(?[0-9]+(?:.[0-9]+){0,})?(-(?[a-zA-Z_]+)(?[0-9]+(?:.[0-9]+){0,})?)?/#.wholeMatch(in: string) else { throw LLVMTripleError.invalidTripleStringFormat(string) } self.arch = String(match.output.arch) self.vendor = String(match.output.vendor) self.system = String(match.output.system) + self.systemVersion = try match.output.systemVersion.map { try Version(String($0)) } self.environment = match.output.environment.map { String($0) } + self.environmentVersion = try match.output.environmentVersion.map { try Version(String($0)) } } public init(from decoder: any Swift.Decoder) throws { @@ -38,13 +74,33 @@ public struct LLVMTriple: Decodable, Equatable, Sendable, CustomStringConvertibl } } +extension LLVMTriple { + public var version: Version? { + get throws { + switch (systemVersion, environmentVersion) { + case (let systemVersion?, nil): + return systemVersion + case (nil, let environmentVersion?): + return environmentVersion + case (nil, nil): + return nil + case (.some(_), .some(_)): + throw LLVMTripleError.multipleVersions + } + } + } +} + enum LLVMTripleError: Error, CustomStringConvertible { case invalidTripleStringFormat(String) + case multipleVersions var description: String { switch self { case let .invalidTripleStringFormat(tripleString): "Invalid triple string format: \(tripleString)" + case .multipleVersions: + "Triple has versions in both the system and environment fields" } } } diff --git a/Tests/SWBGenericUnixPlatformTests/SWBGenericUnixPlatformTests.swift b/Tests/SWBGenericUnixPlatformTests/SWBGenericUnixPlatformTests.swift new file mode 100644 index 00000000..87e97a6e --- /dev/null +++ b/Tests/SWBGenericUnixPlatformTests/SWBGenericUnixPlatformTests.swift @@ -0,0 +1,173 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import class Foundation.ProcessInfo +import struct Foundation.URL +import struct Foundation.UUID + +import Testing + +import func SWBBuildService.commandLineDisplayString +import SWBBuildSystem +import SWBCore +import struct SWBProtocol.RunDestinationInfo +import struct SWBProtocol.TargetDescription +import struct SWBProtocol.TargetDependencyRelationship +import SWBTestSupport +import SWBTaskExecution +@_spi(Testing) import SWBUtil +import SWBTestSupport + +fileprivate func crossCompileTargets() throws -> [OperatingSystem] { + // Skip the test when running on the same host as host testing is already covered by _most_ tests + let hostOS = try ProcessInfo.processInfo.hostOperatingSystem() + return [.linux, .freebsd, .openbsd].filter { $0 != hostOS } +} + +@Suite +fileprivate struct GenerixUnixBuildOperationTests: CoreBasedTests { + /// Tests cross-compilation to Linux, FreeBSD, and OpenBSD. Skipped with Xcode toolchains because lld is required for cross-compilation. + @Test(.skipHostOS(.windows), .skipXcodeToolchain, arguments: try crossCompileTargets()) + func crossCompileCommandLineTool(operatingSystem: OperatingSystem) async throws { + let core = try await getCore() + + // Skip the test when we don't have the necessary SDK, as this test specifically tests cross compilation via Swift SDKs. + if try core.sdkRegistry.lookup(operatingSystem.xcodePlatformName) == nil && core.sdkRegistry.allSDKs.count(where: { try $0.aliases.contains(operatingSystem.xcodePlatformName) }) == 0 { + // FIXME: Adopt Swift Testing API to "cancel" the test case when preconditions aren't met + withKnownIssue { + Issue.record("Skipping \(operatingSystem) because there is no Swift SDK installed for this platform") + } + return + } + + try await withTemporaryDirectory { (tmpDir: Path) in + let testProject = try TestProject( + "TestProject", + sourceRoot: tmpDir, + groupTree: TestGroup( + "SomeFiles", + children: [ + TestFile("main.c"), + TestFile("dynamic.c"), + TestFile("static.c"), + ]), + buildConfigurations: [ + TestBuildConfiguration("Debug", buildSettings: [ + "CODE_SIGNING_ALLOWED": "NO", + "DEFINES_MODULE": "YES", + "PRODUCT_NAME": "$(TARGET_NAME)", + "SDKROOT": "\(operatingSystem.xcodePlatformName)", + "SUPPORTED_PLATFORMS": "\(operatingSystem.xcodePlatformName)", + ]) + ], + targets: [ + TestStandardTarget( + "tool", + type: .commandLineTool, + buildConfigurations: [ + TestBuildConfiguration("Debug", buildSettings: [:]) + ], + buildPhases: [ + TestSourcesBuildPhase(["main.c"]), + TestFrameworksBuildPhase([ + TestBuildFile(.target("dynamiclib")), + TestBuildFile(.target("staticlib")), + ]) + ], + dependencies: [ + "dynamiclib", + "staticlib", + ] + ), + TestStandardTarget( + "dynamiclib", + type: .dynamicLibrary, + buildConfigurations: [ + TestBuildConfiguration("Debug", buildSettings: [ + "DYLIB_INSTALL_NAME_BASE": "$ORIGIN", + + // FIXME: Find a way to make these default + "EXECUTABLE_PREFIX": "lib", + ]) + ], + buildPhases: [ + TestSourcesBuildPhase(["dynamic.c"]), + ], + productReferenceName: "libdynamiclib.so" + ), + TestStandardTarget( + "staticlib", + type: .staticLibrary, + buildConfigurations: [ + TestBuildConfiguration("Debug", buildSettings: [ + // FIXME: Find a way to make these default + "EXECUTABLE_PREFIX": "lib", + ]) + ], + buildPhases: [ + TestSourcesBuildPhase(["static.c"]), + ] + ), + ]) + let tester = try await BuildOperationTester(core, testProject, simulated: false) + + let projectDir = tester.workspace.projects[0].sourceRoot + + try await tester.fs.writeFileContents(projectDir.join("main.c")) { stream in + stream <<< "int main() { }\n" + } + + try await tester.fs.writeFileContents(projectDir.join("dynamic.c")) { stream in + stream <<< "void dynamicLib() { }" + } + + try await tester.fs.writeFileContents(projectDir.join("static.c")) { stream in + stream <<< "void staticLib() { }" + } + + let destination: RunDestinationInfo + switch operatingSystem { + case .linux: + destination = .linux + case .freebsd: + destination = .freebsd + case .openbsd: + destination = .openbsd + default: + throw StubError.error("Unexpected platform \(operatingSystem)") + } + try await tester.checkBuild(runDestination: destination) { results in + results.checkNoErrors() + + let executionResult = try await Process.getOutput(url: URL(filePath: "/usr/bin/file"), arguments: [projectDir.join("build").join("Debug\(destination.builtProductsDirSuffix)").join(core.hostOperatingSystem.imageFormat.executableName(basename: "tool")).str], environment: destination.hostRuntimeEnvironment(core)) + #expect(executionResult.exitStatus == .exit(0)) + let s = String(decoding: executionResult.stdout, as: UTF8.self) + #expect(s.contains("ELF 64-bit"), Comment(rawValue: s)) + if core.hostOperatingSystem != .openbsd { + switch operatingSystem { + case .linux: + #expect(s.contains("Linux"), Comment(rawValue: s)) + case .freebsd: + #expect(s.contains("FreeBSD"), Comment(rawValue: s)) + case .openbsd: + #expect(s.contains("OpenBSD"), Comment(rawValue: s)) + default: + throw StubError.error("Unexpected platform \(operatingSystem)") + } + } else { + // OpenBSD's `file` doesn't show platform details + } + #expect(String(decoding: executionResult.stderr, as: UTF8.self) == "") + } + } + } +} diff --git a/Tests/SWBGenericUnixPlatformTests/SWBLinuxPlatformTests.swift b/Tests/SWBGenericUnixPlatformTests/SWBLinuxPlatformTests.swift deleted file mode 100644 index 380508dc..00000000 --- a/Tests/SWBGenericUnixPlatformTests/SWBLinuxPlatformTests.swift +++ /dev/null @@ -1,12 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift open source project -// -// Copyright (c) 2025 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See http://swift.org/LICENSE.txt for license information -// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -