Skip to content

Commit 80ce079

Browse files
committed
Add FreeBSD support
This allows building Swift Build for FreeBSD hosts, as well as building for a FreeBSD target from a FreeBSD host.
1 parent 9647382 commit 80ce079

19 files changed

+102
-34
lines changed

Diff for: Package.swift

+15-7
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ let package = Package(
102102
"SWBBuildSystem",
103103
"SWBServiceCore",
104104
"SWBTaskExecution",
105-
.product(name: "SystemPackage", package: "swift-system", condition: .when(platforms: [.linux, .android, .windows])),
105+
.product(name: "SystemPackage", package: "swift-system", condition: .when(platforms: [.linux, .openbsd, .android, .windows, .custom("freebsd")])),
106106
],
107107
swiftSettings: swiftSettings(languageMode: .v5)),
108108
.target(
@@ -183,8 +183,8 @@ let package = Package(
183183
"SWBCSupport",
184184
"SWBLibc",
185185
.product(name: "ArgumentParser", package: "swift-argument-parser"),
186-
.product(name: "Crypto", package: "swift-crypto", condition: .when(platforms: [.linux, .android])),
187-
.product(name: "SystemPackage", package: "swift-system", condition: .when(platforms: [.linux, .android, .windows])),
186+
.product(name: "Crypto", package: "swift-crypto", condition: .when(platforms: [.linux, .openbsd, .android, .custom("freebsd")])),
187+
.product(name: "SystemPackage", package: "swift-system", condition: .when(platforms: [.linux, .openbsd, .android, .windows, .custom("freebsd")])),
188188
],
189189
swiftSettings: swiftSettings(languageMode: .v5)),
190190
.target(
@@ -419,12 +419,20 @@ if useLocalDependencies {
419419
package.dependencies += [.package(path: "../llbuild"),]
420420
}
421421
} else {
422+
#if os(FreeBSD)
423+
package.dependencies += [
424+
// https://github.com/apple/swift-system/commit/4fa2a719c5d225fc763f21f2d341c0c8d825d65e is not yet in a tag
425+
.package(url: "https://github.com/apple/swift-system.git", branch: "main"),
426+
]
427+
#else
422428
package.dependencies += [
423-
// https://github.com/apple/swift-crypto/issues/262
424-
// 3.7.1 introduced a regression which fails to link on aarch64-windows; revert to <4.0.0 for the upper bound when this is fixed
425-
.package(url: "https://github.com/apple/swift-crypto.git", "2.0.0"..<"3.7.1"),
426-
.package(url: "https://github.com/apple/swift-driver.git", branch: "main"),
427429
.package(url: "https://github.com/apple/swift-system.git", .upToNextMajor(from: "1.4.0")),
430+
]
431+
#endif
432+
433+
package.dependencies += [
434+
.package(url: "https://github.com/apple/swift-crypto.git", "2.0.0"..<"4.0.0"),
435+
.package(url: "https://github.com/apple/swift-driver.git", branch: "main"),
428436
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.3"),
429437
]
430438
if !useLLBuildFramework {

Diff for: Sources/SWBCore/Core.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,6 @@ struct CoreRegistryDelegate : PlatformRegistryDelegate, SDKRegistryDelegate, Spe
671671
extension OperatingSystem {
672672
/// 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.
673673
internal var createFallbackSystemToolchain: Bool {
674-
return self == .linux
674+
return self == .linux || self == .freebsd
675675
}
676676
}

Diff for: Sources/SWBCore/SDKRegistry.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -747,8 +747,10 @@ public final class SDKRegistry: SDKRegistryLookup, CustomStringConvertible, Send
747747
private func fallbackSystemSDKSettings(operatingSystem: OperatingSystem) throws -> [String: PropertyListItem] {
748748
let defaultProperties: [String: PropertyListItem]
749749
switch operatingSystem {
750-
case .linux:
750+
case .linux, .freebsd:
751751
defaultProperties = [
752+
"LIBTOOL_USE_RESPONSE_FILE": .plString("NO"), // TODO: Need to make this specific to FreeBSD
753+
752754
// Workaround to avoid `-add_ast_path` on Linux, apparently this needs to perform some "swift modulewrap" step instead.
753755
"GCC_GENERATE_DEBUGGING_SYMBOLS": .plString("NO"),
754756

Diff for: Sources/SWBCore/Settings/BuiltinMacros.swift

+2
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,7 @@ public final class BuiltinMacros {
808808
public static let LIBRARY_SEARCH_PATHS = BuiltinMacros.declarePathListMacro("LIBRARY_SEARCH_PATHS")
809809
public static let LIBTOOL = BuiltinMacros.declarePathMacro("LIBTOOL")
810810
public static let LIBTOOL_DEPENDENCY_INFO_FILE = BuiltinMacros.declarePathMacro("LIBTOOL_DEPENDENCY_INFO_FILE")
811+
public static let LIBTOOL_USE_RESPONSE_FILE = BuiltinMacros.declareBooleanMacro("LIBTOOL_USE_RESPONSE_FILE")
811812
public static let LINKER = BuiltinMacros.declareStringMacro("LINKER")
812813
public static let ALTERNATE_LINKER = BuiltinMacros.declareStringMacro("ALTERNATE_LINKER")
813814
public static let LINK_OBJC_RUNTIME = BuiltinMacros.declareBooleanMacro("LINK_OBJC_RUNTIME")
@@ -1859,6 +1860,7 @@ public final class BuiltinMacros {
18591860
LIBRARY_SEARCH_PATHS,
18601861
LIBTOOL,
18611862
LIBTOOL_DEPENDENCY_INFO_FILE,
1863+
LIBTOOL_USE_RESPONSE_FILE,
18621864
LINKER,
18631865
LINK_OBJC_RUNTIME,
18641866
LINK_WITH_STANDARD_LIBRARIES,

Diff for: Sources/SWBCore/Settings/Settings.swift

+2
Original file line numberDiff line numberDiff line change
@@ -5269,6 +5269,8 @@ extension OperatingSystem {
52695269
return "windows"
52705270
case .linux:
52715271
return "linux"
5272+
case .freebsd:
5273+
return "freebsd"
52725274
case .android:
52735275
return "android"
52745276
case .unknown:

Diff for: Sources/SWBCore/Specs/Tools/LinkerTools.swift

+12-7
Original file line numberDiff line numberDiff line change
@@ -1371,17 +1371,22 @@ public final class LibtoolLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @u
13711371

13721372
override public func constructLinkerTasks(_ cbc: CommandBuildContext, _ delegate: any TaskGenerationDelegate, libraries: [LibrarySpecifier], usedTools: [CommandLineToolSpec: Set<FileTypeSpec>]) async {
13731373
var inputPaths = cbc.inputs.map({ $0.absolutePath })
1374+
var specialArgs = [String]()
13741375

1375-
// Define the linker file list.
1376-
let fileListPath = cbc.scope.evaluate(BuiltinMacros.__INPUT_FILE_LIST_PATH__)
1377-
if !fileListPath.isEmpty {
1378-
let contents = cbc.inputs.map({ return $0.absolutePath.strWithPosixSlashes + "\n" }).joined(separator: "")
1379-
cbc.producer.writeFileSpec.constructFileTasks(CommandBuildContext(producer: cbc.producer, scope: cbc.scope, inputs: [], output: fileListPath), delegate, contents: ByteString(encodingAsUTF8: contents), permissions: nil, preparesForIndexing: false, additionalTaskOrderingOptions: [.immediate, .ignorePhaseOrdering])
1380-
inputPaths.append(fileListPath)
1376+
if cbc.scope.evaluate(BuiltinMacros.LIBTOOL_USE_RESPONSE_FILE) {
1377+
// Define the linker file list.
1378+
let fileListPath = cbc.scope.evaluate(BuiltinMacros.__INPUT_FILE_LIST_PATH__)
1379+
if !fileListPath.isEmpty {
1380+
let contents = cbc.inputs.map({ return $0.absolutePath.strWithPosixSlashes + "\n" }).joined(separator: "")
1381+
cbc.producer.writeFileSpec.constructFileTasks(CommandBuildContext(producer: cbc.producer, scope: cbc.scope, inputs: [], output: fileListPath), delegate, contents: ByteString(encodingAsUTF8: contents), permissions: nil, preparesForIndexing: false, additionalTaskOrderingOptions: [.immediate, .ignorePhaseOrdering])
1382+
inputPaths.append(fileListPath)
1383+
}
1384+
} else {
1385+
specialArgs.append(contentsOf: cbc.inputs.map { $0.absolutePath.str })
1386+
inputPaths.append(contentsOf: cbc.inputs.map { $0.absolutePath })
13811387
}
13821388

13831389
// Add arguments for the contents of the Link Binaries build phase.
1384-
var specialArgs = [String]()
13851390
specialArgs.append(contentsOf: libraries.flatMap { specifier -> [String] in
13861391
let basename = specifier.path.basename
13871392

Diff for: Sources/SWBCore/ToolchainRegistry.swift

+17-4
Original file line numberDiff line numberDiff line change
@@ -440,10 +440,23 @@ public final class ToolchainRegistry: @unchecked Sendable {
440440
return
441441
}
442442

443-
if let swift = StackedSearchPath(environment: ProcessInfo.processInfo.cleanEnvironment, fs: fs).lookup(Path("swift")), fs.exists(swift) && swift.normalize().str.hasSuffix("/usr/bin/swift") {
444-
let path = swift.dirname.dirname.dirname
445-
let llvmDirectories = try fs.listdir(Path("/usr/lib")).filter { $0.hasPrefix("llvm-") }.sorted().reversed()
446-
try register(Toolchain(Self.defaultToolchainIdentifier, "Default", Version(), [], path, [], llvmDirectories.map { "/usr/lib/\($0)/lib" } + ["/usr/lib64"], [:], [:], [:], executableSearchPaths: [path.join("usr").join("bin"), path.join("usr").join("local").join("bin"), path.join("usr").join("libexec")], fs: fs))
443+
if let swift = StackedSearchPath(environment: ProcessInfo.processInfo.cleanEnvironment, fs: fs).lookup(Path("swift")), fs.exists(swift) {
444+
let hasUsrBin = swift.normalize().str.hasSuffix("/usr/bin/swift")
445+
let hasUsrLocalBin = swift.normalize().str.hasSuffix("/usr/local/bin/swift")
446+
let path: Path
447+
switch (hasUsrBin, hasUsrLocalBin) {
448+
case (true, false):
449+
path = swift.dirname.dirname.dirname
450+
case (false, true):
451+
path = swift.dirname.dirname.dirname.dirname
452+
case (false, false):
453+
return
454+
case (true, true):
455+
preconditionFailure()
456+
}
457+
let llvmDirectories = try Array(fs.listdir(Path("/usr/lib")).filter { $0.hasPrefix("llvm-") }.sorted().reversed())
458+
let llvmDirectoriesLocal = try Array(fs.listdir(Path("/usr/local")).filter { $0.hasPrefix("llvm") }.sorted().reversed())
459+
try register(Toolchain(Self.defaultToolchainIdentifier, "Default", Version(), [], path, [], llvmDirectories.map { "/usr/lib/\($0)/lib" } + llvmDirectoriesLocal.map { "/usr/local/\($0)/lib" } + ["/usr/lib64"], [:], [:], [:], executableSearchPaths: [path.join("usr").join("bin"), path.join("usr").join("local").join("bin"), path.join("usr").join("libexec")], fs: fs))
447460
}
448461
}
449462

Diff for: Sources/SWBGenericUnixPlatform/Plugin.swift

+4-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ struct GenericUnixPlatformSpecsExtension: SpecificationsExtension {
2424
}
2525

2626
func specificationDomains() -> [String: [String]] {
27-
["linux": ["generic-unix"]]
27+
[
28+
"linux": ["generic-unix"],
29+
"freebsd": ["generic-unix"],
30+
]
2831
}
2932
}

Diff for: Sources/SWBGenericUnixPlatform/UnixLibtool.xcspec

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
Name = __INPUT_FILE_LIST_PATH__;
5252
Type = Path;
5353
// this is set up for us as a read-only property
54+
Condition = "$(LIBTOOL_USE_RESPONSE_FILE) != NO";
5455
DefaultValue = "$(LINK_FILE_LIST_$(variant)_$(arch))";
5556
CommandLineArgs = (
5657
"@$(value)",

Diff for: Sources/SWBTestSupport/RunDestinationTestSupport.swift

+10
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ extension _RunDestinationInfo {
9898
windows
9999
case .linux:
100100
linux
101+
case .freebsd:
102+
freebsd
101103
case .android:
102104
android
103105
case .unknown:
@@ -259,6 +261,14 @@ extension _RunDestinationInfo {
259261
return .init(platform: "linux", sdk: "linux", sdkVariant: "linux", targetArchitecture: arch, supportedArchitectures: ["x86_64", "aarch64"], disableOnlyActiveArch: false)
260262
}
261263

264+
/// A run destination targeting FreeBSD generic device, using the public SDK.
265+
package static var freebsd: Self {
266+
guard let arch = Architecture.hostStringValue else {
267+
preconditionFailure("Unknown architecture \(Architecture.host.stringValue ?? "<nil>")")
268+
}
269+
return .init(platform: "freebsd", sdk: "freebsd", sdkVariant: "freebsd", targetArchitecture: arch, supportedArchitectures: ["x86_64", "aarch64"], disableOnlyActiveArch: false)
270+
}
271+
262272
/// A run destination targeting Android generic device, using the public SDK.
263273
package static var android: Self {
264274
return .init(platform: "android", sdk: "android", sdkVariant: "android", targetArchitecture: "undefined_arch", supportedArchitectures: ["armv7", "aarch64", "riscv64", "i686", "x86_64"], disableOnlyActiveArch: true)

Diff for: Sources/SWBTestSupport/SkippedTestSupport.swift

+3
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ extension KnownSDK {
4848
return windows
4949
case .success(.linux):
5050
return linux
51+
case .success(.freebsd):
52+
return freebsd
5153
case .success(.android):
5254
return android
5355
case .success(.unknown), .failure:
@@ -68,6 +70,7 @@ extension KnownSDK {
6870
extension KnownSDK {
6971
package static let windows: Self = "windows"
7072
package static let linux: Self = "linux"
73+
package static let freebsd: Self = "freebsd"
7174
package static let android: Self = "android"
7275
package static let qnx: Self = "qnx"
7376
package static let wasi: Self = "wasi"

Diff for: Sources/SWBUniversalPlatform/Specs/Libtool.xcspec

+6
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
// Input file lists
6262
{ Name = __INPUT_FILE_LIST_PATH__;
6363
Type = Path;
64+
Condition = "$(LIBTOOL_USE_RESPONSE_FILE) != NO";
6465
DefaultValue = "$(LINK_FILE_LIST_$(variant)_$(arch))"; // this is set up for us as a read-only property
6566
CommandLineFlag = "-filelist";
6667
IsInputDependency = Yes;
@@ -104,6 +105,11 @@
104105
DefaultValue = "$(OBJECT_FILE_DIR_$(CURRENT_VARIANT))/$(CURRENT_ARCH)/$(PRODUCT_NAME)_libtool_dependency_info.dat";
105106
},
106107

108+
{
109+
Name = "LIBTOOL_USE_RESPONSE_FILE";
110+
Type = Boolean;
111+
DefaultValue = YES;
112+
},
107113
);
108114
}
109115
)

Diff for: Sources/SWBUtil/Architecture.swift

+7-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,13 @@ public struct Architecture: Sendable {
9898
if uname(&buf) == 0 {
9999
return withUnsafeBytes(of: &buf.machine) { buf in
100100
let data = Data(buf)
101-
return String(decoding: data[0...(data.lastIndex(where: { $0 != 0 }) ?? 0)], as: UTF8.self)
101+
let value = String(decoding: data[0...(data.lastIndex(where: { $0 != 0 }) ?? 0)], as: UTF8.self)
102+
switch value {
103+
case "amd64":
104+
return "x86_64"
105+
default:
106+
return value
107+
}
102108
}
103109
}
104110
return nil

Diff for: Sources/SWBUtil/FSProxy.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -758,7 +758,7 @@ class LocalFS: FSProxy, @unchecked Sendable {
758758
}
759759

760760
func listExtendedAttributes(_ path: Path) throws -> [String] {
761-
#if os(Windows)
761+
#if os(Windows) || os(FreeBSD) // FreeBSD blocked on https://github.com/swiftlang/swift/pull/77836
762762
// no xattrs on Windows
763763
return []
764764
#else
@@ -796,7 +796,7 @@ class LocalFS: FSProxy, @unchecked Sendable {
796796
}
797797

798798
func setExtendedAttribute(_ path: Path, key: String, value: ByteString) throws {
799-
#if os(Windows)
799+
#if os(Windows) || os(FreeBSD) // FreeBSD blocked on https://github.com/swiftlang/swift/pull/77836
800800
// no xattrs on Windows
801801
#else
802802
try value.bytes.withUnsafeBufferPointer { buf throws -> Void in
@@ -813,7 +813,7 @@ class LocalFS: FSProxy, @unchecked Sendable {
813813
}
814814

815815
func getExtendedAttribute(_ path: Path, key: String) throws -> ByteString? {
816-
#if os(Windows)
816+
#if os(Windows) || os(FreeBSD) // FreeBSD blocked on https://github.com/swiftlang/swift/pull/77836
817817
return nil // no xattrs on Windows
818818
#else
819819
var bufferSize = 4096

Diff for: Sources/SWBUtil/Lock.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public final class Lock: @unchecked Sendable {
2828
let mutex: UnsafeMutablePointer<SRWLOCK> = UnsafeMutablePointer.allocate(capacity: 1)
2929
#else
3030
@usableFromInline
31-
let mutex: UnsafeMutablePointer<pthread_mutex_t> = UnsafeMutablePointer.allocate(capacity: 1)
31+
let mutex: UnsafeMutablePointer<pthread_mutex_t?> = UnsafeMutablePointer.allocate(capacity: 1)
3232
#endif
3333

3434
public init() {

Diff for: Sources/SWBUtil/ProcessInfo.swift

+4-1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ extension ProcessInfo {
9797
return .windows
9898
#elseif os(Linux)
9999
return .linux
100+
#elseif os(FreeBSD)
101+
return .freebsd
100102
#else
101103
if try FileManager.default.isReadableFile(atPath: systemVersionPlistURL.filePath.str) {
102104
switch try systemVersion().productName {
@@ -125,6 +127,7 @@ public enum OperatingSystem: Hashable, Sendable {
125127
case visionOS(simulator: Bool)
126128
case windows
127129
case linux
130+
case freebsd
128131
case android
129132
case unknown
130133

@@ -153,7 +156,7 @@ public enum OperatingSystem: Hashable, Sendable {
153156
return .macho
154157
case .windows:
155158
return .pe
156-
case .linux, .android, .unknown:
159+
case .linux, .freebsd, .android, .unknown:
157160
return .elf
158161
}
159162
}

Diff for: Tests/SWBBuildSystemTests/BuildOperationTests.swift

+8-4
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ fileprivate struct BuildOperationTests: CoreBasedTests {
8282
type: .dynamicLibrary,
8383
buildConfigurations: [
8484
TestBuildConfiguration("Debug", buildSettings: [
85-
"DYLIB_INSTALL_NAME_BASE": "@rpath",
86-
"DYLIB_INSTALL_NAME_BASE[sdk=linux*]": "$ORIGIN",
85+
"DYLIB_INSTALL_NAME_BASE": "$ORIGIN",
86+
"DYLIB_INSTALL_NAME_BASE[sdk=macosx*]": "@rpath",
8787

8888
// FIXME: Find a way to make these default
8989
"EXECUTABLE_PREFIX": "lib",
@@ -145,8 +145,12 @@ fileprivate struct BuildOperationTests: CoreBasedTests {
145145

146146
let toolchain = try #require(try await getCore().toolchainRegistry.defaultToolchain)
147147
let environment: [String: String]
148-
if destination.platform == "linux" {
149-
environment = ["LD_LIBRARY_PATH": toolchain.path.join("usr/lib/swift/linux").str]
148+
if ["linux", "freebsd"].contains(destination.platform) {
149+
// FIXME: Should be for "any ELF platform", not hardcoded to Linux and FreeBSD
150+
environment = ["LD_LIBRARY_PATH": [
151+
toolchain.path.join("usr/lib/swift/\(destination.platform)").str,
152+
toolchain.path.join("usr/local/lib/swift/\(destination.platform)").str,
153+
].joined(separator: String(Path.pathEnvironmentSeparator))]
150154
} else {
151155
environment = [:]
152156
}

Diff for: Tests/SWBUtilTests/FSProxyTests.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,7 @@ import SWBTestSupport
565565
// String.utf8CString *includes* the trailing null byte
566566
#if canImport(Darwin)
567567
#expect(setxattr(testDataPath.str, "attr.string", "true", "true".utf8CString.count, 0, 0) == 0)
568-
#elseif !os(Windows)
568+
#elseif !os(Windows) && !os(FreeBSD) // FreeBSD blocked on https://github.com/swiftlang/swift/pull/77836
569569
#expect(setxattr(testDataPath.str, "attr.string", "true", "true".utf8CString.count, 0) == 0)
570570
#endif
571571
#expect(try localFS.getExtendedAttribute(testDataPath, key: "attr.string") == "true\0")
@@ -574,7 +574,7 @@ import SWBTestSupport
574574
// String.utf8CString *includes* the trailing null byte
575575
#if canImport(Darwin)
576576
#expect(setxattr(testDataPath.str, "attr.string", "tr\0ue", "tr\0ue".utf8CString.count, 0, 0) == 0)
577-
#elseif !os(Windows)
577+
#elseif !os(Windows) && !os(FreeBSD) // FreeBSD blocked on https://github.com/swiftlang/swift/pull/77836
578578
#expect(setxattr(testDataPath.str, "attr.string", "tr\0ue", "tr\0ue".utf8CString.count, 0) == 0)
579579
#endif
580580
#expect(try localFS.getExtendedAttribute(testDataPath, key: "attr.string") == "tr\0ue\0")

Diff for: Tests/SWBUtilTests/MiscTests.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import SWBUtil
2525
#expect(SWBUtil.userCacheDir().str.hasPrefix("/var/folders"))
2626
case .android:
2727
#expect(SWBUtil.userCacheDir().str.hasPrefix("/data/local/tmp"))
28-
case .linux, .unknown:
28+
case .linux, .freebsd, .unknown:
2929
#expect(SWBUtil.userCacheDir().str.hasPrefix("/tmp"))
3030
}
3131
}

0 commit comments

Comments
 (0)