From 10595046bf837dbd85342dbce36cc2ac0e1efde4 Mon Sep 17 00:00:00 2001 From: Jake Petroules Date: Fri, 14 Feb 2025 15:30:25 +0900 Subject: [PATCH] Add FreeBSD support This allows building Swift Build for FreeBSD hosts, as well as building for a FreeBSD target from a FreeBSD host. Also adds some speculative support for targeting OpenBSD on OpenBSD hosts, since SwiftPM has minimal support. --- Package.swift | 8 +++--- Sources/SWBCore/Settings/Settings.swift | 4 +++ Sources/SWBGenericUnixPlatform/Plugin.swift | 12 ++++++--- .../Specs/FreeBSDLibtool.xcspec | 27 +++++++++++++++++++ .../RunDestinationTestSupport.swift | 20 ++++++++++++++ .../SWBTestSupport/SkippedTestSupport.swift | 9 +++++++ Sources/SWBUtil/Architecture.swift | 8 +++++- Sources/SWBUtil/FSProxy.swift | 8 ++++++ Sources/SWBUtil/Lock.swift | 2 +- Sources/SWBUtil/ProcessInfo.swift | 8 +++++- Tests/SWBUtilTests/FSProxyTests.swift | 4 +-- Tests/SWBUtilTests/MiscTests.swift | 2 +- 12 files changed, 98 insertions(+), 14 deletions(-) create mode 100644 Sources/SWBGenericUnixPlatform/Specs/FreeBSDLibtool.xcspec diff --git a/Package.swift b/Package.swift index d8ac4ec7..3820201e 100644 --- a/Package.swift +++ b/Package.swift @@ -105,7 +105,7 @@ let package = Package( "SWBBuildSystem", "SWBServiceCore", "SWBTaskExecution", - .product(name: "SystemPackage", package: "swift-system", condition: .when(platforms: [.linux, .android, .windows])), + .product(name: "SystemPackage", package: "swift-system", condition: .when(platforms: [.linux, .openbsd, .android, .windows, .custom("freebsd")])), ], exclude: ["CMakeLists.txt"], swiftSettings: swiftSettings(languageMode: .v5)), @@ -195,8 +195,8 @@ let package = Package( "SWBCSupport", "SWBLibc", .product(name: "ArgumentParser", package: "swift-argument-parser"), - .product(name: "Crypto", package: "swift-crypto", condition: .when(platforms: [.linux, .openbsd, .android])), - .product(name: "SystemPackage", package: "swift-system", condition: .when(platforms: [.linux, .android, .windows])), + .product(name: "Crypto", package: "swift-crypto", condition: .when(platforms: [.linux, .openbsd, .android, .custom("freebsd")])), + .product(name: "SystemPackage", package: "swift-system", condition: .when(platforms: [.linux, .openbsd, .android, .windows, .custom("freebsd")])), ], exclude: ["CMakeLists.txt"], swiftSettings: swiftSettings(languageMode: .v5)), @@ -439,7 +439,7 @@ if useLocalDependencies { package.dependencies += [ .package(url: "https://github.com/apple/swift-crypto.git", "2.0.0"..<"4.0.0"), .package(url: "https://github.com/swiftlang/swift-driver.git", branch: "main"), - .package(url: "https://github.com/apple/swift-system.git", .upToNextMajor(from: "1.4.1")), + .package(url: "https://github.com/apple/swift-system.git", .upToNextMajor(from: "1.4.2")), .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.3"), ] if !useLLBuildFramework { diff --git a/Sources/SWBCore/Settings/Settings.swift b/Sources/SWBCore/Settings/Settings.swift index d0492cba..5963ee5d 100644 --- a/Sources/SWBCore/Settings/Settings.swift +++ b/Sources/SWBCore/Settings/Settings.swift @@ -5323,6 +5323,10 @@ extension OperatingSystem { return "windows" case .linux: return "linux" + case .freebsd: + return "freebsd" + case .openbsd: + return "openbsd" case .android: return "android" case .unknown: diff --git a/Sources/SWBGenericUnixPlatform/Plugin.swift b/Sources/SWBGenericUnixPlatform/Plugin.swift index e4679961..0a719951 100644 --- a/Sources/SWBGenericUnixPlatform/Plugin.swift +++ b/Sources/SWBGenericUnixPlatform/Plugin.swift @@ -39,7 +39,11 @@ struct GenericUnixPlatformSpecsExtension: SpecificationsExtension { } func specificationDomains() -> [String: [String]] { - ["linux": ["generic-unix"]] + [ + "linux": ["generic-unix"], + "freebsd": ["generic-unix"], + "openbsd": ["generic-unix"], + ] } } @@ -73,9 +77,9 @@ struct GenericUnixSDKRegistryExtension: SDKRegistryExtension { let defaultProperties: [String: PropertyListItem] switch operatingSystem { - case .linux: + case .linux, .freebsd: defaultProperties = [ - // Workaround to avoid `-dependency_info` on Linux. + // Workaround to avoid `-dependency_info`. "LD_DEPENDENCY_INFO_FILE": .plString(""), "GENERATE_TEXT_BASED_STUBS": "NO", @@ -167,6 +171,6 @@ struct GenericUnixToolchainRegistryExtension: ToolchainRegistryExtension { extension OperatingSystem { /// 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 + return self == .linux || self == .freebsd || self == .openbsd } } diff --git a/Sources/SWBGenericUnixPlatform/Specs/FreeBSDLibtool.xcspec b/Sources/SWBGenericUnixPlatform/Specs/FreeBSDLibtool.xcspec new file mode 100644 index 00000000..8d247c89 --- /dev/null +++ b/Sources/SWBGenericUnixPlatform/Specs/FreeBSDLibtool.xcspec @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +( + { + Domain = freebsd; + Identifier = com.apple.pbx.linkers.libtool; + BasedOn = generic-unix:com.apple.pbx.linkers.libtool; + Type = Linker; + Options = ( + { + Name = "LIBTOOL_USE_RESPONSE_FILE"; + Type = Boolean; + DefaultValue = NO; + }, + ); + }, +) diff --git a/Sources/SWBTestSupport/RunDestinationTestSupport.swift b/Sources/SWBTestSupport/RunDestinationTestSupport.swift index 9f6ae3bf..247397b7 100644 --- a/Sources/SWBTestSupport/RunDestinationTestSupport.swift +++ b/Sources/SWBTestSupport/RunDestinationTestSupport.swift @@ -98,6 +98,10 @@ extension _RunDestinationInfo { windows case .linux: linux + case .freebsd: + freebsd + case .openbsd: + openbsd case .android: android case .unknown: @@ -259,6 +263,22 @@ extension _RunDestinationInfo { return .init(platform: "linux", sdk: "linux", sdkVariant: "linux", targetArchitecture: arch, supportedArchitectures: ["x86_64", "aarch64"], disableOnlyActiveArch: false) } + /// A run destination targeting FreeBSD generic device, using the public SDK. + package static var freebsd: Self { + guard let arch = Architecture.hostStringValue else { + preconditionFailure("Unknown architecture \(Architecture.host.stringValue ?? "")") + } + return .init(platform: "freebsd", sdk: "freebsd", sdkVariant: "freebsd", targetArchitecture: arch, supportedArchitectures: ["x86_64", "aarch64"], disableOnlyActiveArch: false) + } + + /// A run destination targeting OpenBSD generic device, using the public SDK. + package static var openbsd: Self { + guard let arch = Architecture.hostStringValue else { + preconditionFailure("Unknown architecture \(Architecture.host.stringValue ?? "")") + } + return .init(platform: "openbsd", sdk: "openbsd", sdkVariant: "openbsd", targetArchitecture: arch, supportedArchitectures: ["x86_64", "aarch64"], disableOnlyActiveArch: false) + } + /// A run destination targeting Android generic device, using the public SDK. package static var android: Self { return .init(platform: "android", sdk: "android", sdkVariant: "android", targetArchitecture: "undefined_arch", supportedArchitectures: ["armv7", "aarch64", "riscv64", "i686", "x86_64"], disableOnlyActiveArch: true) diff --git a/Sources/SWBTestSupport/SkippedTestSupport.swift b/Sources/SWBTestSupport/SkippedTestSupport.swift index c6fc84de..af4c5de2 100644 --- a/Sources/SWBTestSupport/SkippedTestSupport.swift +++ b/Sources/SWBTestSupport/SkippedTestSupport.swift @@ -48,6 +48,10 @@ extension KnownSDK { return windows case .success(.linux): return linux + case .success(.freebsd): + return freebsd + case .success(.openbsd): + return openbsd case .success(.android): return android case .success(.unknown), .failure: @@ -68,6 +72,8 @@ extension KnownSDK { extension KnownSDK { package static let windows: Self = "windows" package static let linux: Self = "linux" + package static let freebsd: Self = "freebsd" + package static let openbsd: Self = "openbsd" package static let android: Self = "android" package static let qnx: Self = "qnx" package static let wasi: Self = "wasi" @@ -143,6 +149,9 @@ extension Trait where Self == Testing.ConditionTrait { case .linux: // Amazon Linux 2 has glibc 2.26, and glibc 2.29 is needed for posix_spawn_file_actions_addchdir_np support FileManager.default.contents(atPath: "/etc/system-release").map { String(decoding: $0, as: UTF8.self) == "Amazon Linux release 2 (Karoo)\n" } ?? false + case .openbsd: + // OpenBSD does not yet have posix_spawn_file_actions_addchdir support at all + true default: false } diff --git a/Sources/SWBUtil/Architecture.swift b/Sources/SWBUtil/Architecture.swift index 340491bd..1831cba1 100644 --- a/Sources/SWBUtil/Architecture.swift +++ b/Sources/SWBUtil/Architecture.swift @@ -98,7 +98,13 @@ public struct Architecture: Sendable { if uname(&buf) == 0 { return withUnsafeBytes(of: &buf.machine) { buf in let data = Data(buf) - return String(decoding: data[0...(data.lastIndex(where: { $0 != 0 }) ?? 0)], as: UTF8.self) + let value = String(decoding: data[0...(data.lastIndex(where: { $0 != 0 }) ?? 0)], as: UTF8.self) + switch value { + case "amd64": + return "x86_64" + default: + return value + } } } return nil diff --git a/Sources/SWBUtil/FSProxy.swift b/Sources/SWBUtil/FSProxy.swift index a8959df3..6c4e9abd 100644 --- a/Sources/SWBUtil/FSProxy.swift +++ b/Sources/SWBUtil/FSProxy.swift @@ -765,6 +765,9 @@ class LocalFS: FSProxy, @unchecked Sendable { #if os(Windows) // Implement ADS on Windows? See also https://github.com/swiftlang/swift-foundation/issues/1166 return [] + #elseif os(FreeBSD) + // FreeBSD blocked on https://github.com/swiftlang/swift/pull/77836 + return [] #elseif os(OpenBSD) // OpenBSD no longer supports extended attributes return [] @@ -805,6 +808,8 @@ class LocalFS: FSProxy, @unchecked Sendable { func setExtendedAttribute(_ path: Path, key: String, value: ByteString) throws { #if os(Windows) // Implement ADS on Windows? See also https://github.com/swiftlang/swift-foundation/issues/1166 + #elseif os(FreeBSD) + // FreeBSD blocked on https://github.com/swiftlang/swift/pull/77836 #elseif os(OpenBSD) // OpenBSD no longer supports extended attributes #else @@ -825,6 +830,9 @@ class LocalFS: FSProxy, @unchecked Sendable { #if os(Windows) // Implement ADS on Windows? See also https://github.com/swiftlang/swift-foundation/issues/1166 return nil + #elseif os(FreeBSD) + // FreeBSD blocked on https://github.com/swiftlang/swift/pull/77836 + return nil #elseif os(OpenBSD) // OpenBSD no longer supports extended attributes return nil diff --git a/Sources/SWBUtil/Lock.swift b/Sources/SWBUtil/Lock.swift index 2135ce67..3f45f71e 100644 --- a/Sources/SWBUtil/Lock.swift +++ b/Sources/SWBUtil/Lock.swift @@ -26,7 +26,7 @@ public final class Lock: @unchecked Sendable { #if os(Windows) @usableFromInline let mutex: UnsafeMutablePointer = UnsafeMutablePointer.allocate(capacity: 1) - #elseif os(OpenBSD) + #elseif os(FreeBSD) || os(OpenBSD) @usableFromInline let mutex: UnsafeMutablePointer = UnsafeMutablePointer.allocate(capacity: 1) #else diff --git a/Sources/SWBUtil/ProcessInfo.swift b/Sources/SWBUtil/ProcessInfo.swift index 7202bfdd..5ee550ef 100644 --- a/Sources/SWBUtil/ProcessInfo.swift +++ b/Sources/SWBUtil/ProcessInfo.swift @@ -97,6 +97,10 @@ extension ProcessInfo { return .windows #elseif os(Linux) return .linux + #elseif os(FreeBSD) + return .freebsd + #elseif os(OpenBSD) + return .openbsd #else if try FileManager.default.isReadableFile(atPath: systemVersionPlistURL.filePath.str) { switch try systemVersion().productName { @@ -127,6 +131,8 @@ public enum OperatingSystem: Hashable, Sendable { case visionOS(simulator: Bool) case windows case linux + case freebsd + case openbsd case android case unknown @@ -155,7 +161,7 @@ public enum OperatingSystem: Hashable, Sendable { return .macho case .windows: return .pe - case .linux, .android, .unknown: + case .linux, .freebsd, .openbsd, .android, .unknown: return .elf } } diff --git a/Tests/SWBUtilTests/FSProxyTests.swift b/Tests/SWBUtilTests/FSProxyTests.swift index 557a33d1..1f56717d 100644 --- a/Tests/SWBUtilTests/FSProxyTests.swift +++ b/Tests/SWBUtilTests/FSProxyTests.swift @@ -488,7 +488,7 @@ import SWBTestSupport case .android, .linux: // This will _usually_ be correct on Linux-derived OSes (see above), but not always. #expect(current_gid == ownership.group) - case .macOS, .iOS, .tvOS, .watchOS, .visionOS: + case .macOS, .iOS, .tvOS, .watchOS, .visionOS, .freebsd, .openbsd: #expect(parentDirOwnership.group == ownership.group) case .windows: // POSIX permissions don't exist, so everything is hardcoded to zero. @@ -566,7 +566,7 @@ import SWBTestSupport } } - @Test(.skipHostOS(.windows)) + @Test(.skipHostOS(.windows), .skipHostOS(.freebsd, "Blocked on https://github.com/swiftlang/swift/pull/77836")) func extendedAttributesSupport() throws { try withTemporaryDirectory { (tmpDir: Path) in // Many filesystems on other platforms (e.g. various non-ext4 temporary filesystems on Linux) don't support xattrs and will return ENOTSUP. diff --git a/Tests/SWBUtilTests/MiscTests.swift b/Tests/SWBUtilTests/MiscTests.swift index 36240ad9..f93094a4 100644 --- a/Tests/SWBUtilTests/MiscTests.swift +++ b/Tests/SWBUtilTests/MiscTests.swift @@ -25,7 +25,7 @@ import SWBUtil #expect(SWBUtil.userCacheDir().str.hasPrefix("/var/folders")) case .android: #expect(SWBUtil.userCacheDir().str.hasPrefix("/data/local/tmp")) - case .linux, .unknown: + case .linux, .freebsd, .openbsd, .unknown: #expect(SWBUtil.userCacheDir().str.hasPrefix("/tmp")) } }