Skip to content

Commit 4ad91c9

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 83bcfc4 commit 4ad91c9

21 files changed

+134
-46
lines changed

Diff for: Package.swift

+39-11
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@ let appleOS = true
2020
let appleOS = false
2121
#endif
2222

23+
// Workaround until FreeBSD platform condition lands
24+
#if os(FreeBSD)
25+
let nonApplePlatformsCondition: TargetDependencyCondition? = nil
26+
let nonAppleUnixPlatformsCondition: TargetDependencyCondition? = nil
27+
let noCustomExecutionTraitCondition: BuildSettingCondition? = nil
28+
#else
29+
let nonApplePlatformsCondition: TargetDependencyCondition? = .when(platforms: [.linux, .openbsd, .android, .windows])
30+
let nonAppleUnixPlatformsCondition: TargetDependencyCondition? = .when(platforms: [.linux, .openbsd, .android])
31+
let noCustomExecutionTraitCondition: BuildSettingCondition? = .when(platforms: [.macOS, .macCatalyst, .iOS, .tvOS, .watchOS, .visionOS, .windows])
32+
#endif
33+
2334
let useLocalDependencies = Context.environment["SWIFTCI_USE_LOCAL_DEPS"] != nil
2435
let useLLBuildFramework = Context.environment["SWIFTBUILD_LLBUILD_FWK"] != nil
2536
let readlinePackage = !appleOS ? "readline" : nil
@@ -82,7 +93,7 @@ let package = Package(
8293
"SWBBuildSystem",
8394
"SWBServiceCore",
8495
"SWBTaskExecution",
85-
.product(name: "SystemPackage", package: "swift-system", condition: .when(platforms: [.linux, .android, .windows])),
96+
.product(name: "SystemPackage", package: "swift-system", condition: nonApplePlatformsCondition),
8697
],
8798
swiftSettings: swiftSettings),
8899
.target(
@@ -164,8 +175,8 @@ let package = Package(
164175
"SWBLibc",
165176
.target(name: "readline", condition: .when(platforms: [.linux])),
166177
.product(name: "ArgumentParser", package: "swift-argument-parser"),
167-
.product(name: "Crypto", package: "swift-crypto", condition: .when(platforms: [.linux, .android])),
168-
.product(name: "SystemPackage", package: "swift-system", condition: .when(platforms: [.linux, .android, .windows])),
178+
.product(name: "Crypto", package: "swift-crypto", condition: nonAppleUnixPlatformsCondition),
179+
.product(name: "SystemPackage", package: "swift-system", condition: nonApplePlatformsCondition),
169180
],
170181
swiftSettings: swiftSettings),
171182
.target(
@@ -210,20 +221,20 @@ let package = Package(
210221
// Test support
211222
.target(
212223
name: "SwiftBuildTestSupport",
213-
dependencies: ["SwiftBuild", "SWBTestSupport", "SWBUtil"],
224+
dependencies: ["SwiftBuild", "SWBTestSupport", "SWBUtil", .product(name: "Testing", package: "swift-testing")],
214225
swiftSettings: swiftSettings,
215226
linkerSettings: [
216227
.linkedFramework("Testing", .when(platforms: [.macOS]))
217228
]),
218229
.target(
219230
name: "SWBTestSupport",
220-
dependencies: ["SwiftBuild", "SWBBuildSystem", "SWBCore", "SWBTaskConstruction", "SWBTaskExecution", "SWBUtil", "SWBLLBuild", "SWBMacro"],
231+
dependencies: ["SwiftBuild", "SWBBuildSystem", "SWBCore", "SWBTaskConstruction", "SWBTaskExecution", "SWBUtil", "SWBLLBuild", "SWBMacro", .product(name: "Testing", package: "swift-testing")],
221232
swiftSettings: swiftSettings + [
222233
// Temporary until swift-testing introduces replacement for this SPI
223-
.define("DONT_HAVE_CUSTOM_EXECUTION_TRAIT", .when(platforms: [.macOS, .macCatalyst, .iOS, .tvOS, .watchOS, .visionOS, .windows]))
234+
.define("DONT_HAVE_CUSTOM_EXECUTION_TRAIT", noCustomExecutionTraitCondition)
224235
],
225236
linkerSettings: [
226-
.linkedFramework("Testing", .when(platforms: [.macOS]))
237+
.linkedFramework("Testing", .when(platforms: [.macOS])),
227238
]),
228239

229240
// Tests
@@ -260,11 +271,11 @@ let package = Package(
260271
swiftSettings: swiftSettings),
261272
.testTarget(
262273
name: "SWBProjectModelTests",
263-
dependencies: ["SWBProjectModel"],
274+
dependencies: ["SWBProjectModel", .product(name: "Testing", package: "swift-testing")],
264275
swiftSettings: swiftSettings),
265276
.testTarget(
266277
name: "SWBProtocolTests",
267-
dependencies: ["SWBProtocol", "SWBUtil"],
278+
dependencies: ["SWBProtocol", "SWBUtil", .product(name: "Testing", package: "swift-testing")],
268279
swiftSettings: swiftSettings),
269280
.testTarget(
270281
name: "SWBUtilTests",
@@ -280,7 +291,7 @@ let package = Package(
280291
swiftSettings: swiftSettings),
281292
.testTarget(
282293
name: "SWBServiceCoreTests",
283-
dependencies: ["SWBServiceCore"],
294+
dependencies: ["SWBServiceCore", .product(name: "Testing", package: "swift-testing")],
284295
swiftSettings: swiftSettings),
285296
.testTarget(
286297
name: "SWBCoreTests",
@@ -401,15 +412,32 @@ if useLocalDependencies {
401412
package.dependencies += [.package(path: "../llbuild"),]
402413
}
403414
} else {
415+
#if os(FreeBSD)
416+
package.dependencies += [
417+
// https://github.com/apple/swift-crypto/commit/bce1725222a0547c061fb71eab460aff1aa1e28b is not yet in a tag
418+
.package(url: "https://github.com/apple/swift-crypto.git", branch: "main"),
419+
420+
// https://github.com/apple/swift-system/commit/4fa2a719c5d225fc763f21f2d341c0c8d825d65e is not yet in a tag
421+
.package(url: "https://github.com/apple/swift-system.git", branch: "main"),
422+
]
423+
#else
404424
package.dependencies += [
405425
// https://github.com/apple/swift-crypto/issues/262
406426
// 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
407427
.package(url: "https://github.com/apple/swift-crypto.git", "2.0.0"..<"3.7.1"),
408-
.package(url: "https://github.com/apple/swift-driver.git", branch: "main"),
428+
409429
.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-driver.git", branch: "main"),
410435
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.3"),
411436
]
412437
if !useLLBuildFramework {
413438
package.dependencies += [.package(url: "https://github.com/apple/swift-llbuild.git", branch: "main"),]
414439
}
415440
}
441+
442+
443+
package.dependencies += [.package(url: "https://github.com/swiftlang/swift-testing", branch: "main")]

Diff for: Sources/SWBCore/Core.swift

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

Diff for: Sources/SWBCore/SDKRegistry.swift

+4-2
Original file line numberDiff line numberDiff line change
@@ -747,8 +747,10 @@ public final class SDKRegistry: SDKRegistryLookup, CustomStringConvertible {
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

@@ -800,7 +802,7 @@ public final class SDKRegistry: SDKRegistryLookup, CustomStringConvertible {
800802
operatingSystem.xcodePlatformName: .plDict([
801803
"Archs": .plArray([.plString(Architecture.hostStringValue ?? "unknown")]),
802804
"LLVMTargetTripleEnvironment": .plString(tripleEnvironment),
803-
"LLVMTargetTripleSys": .plString(operatingSystem.xcodePlatformName),
805+
"LLVMTargetTripleSys": .plString(operatingSystem.xcodePlatformName + "14.1"), // TODO: Need to get the FreeBSD version and plumb it through somehow
804806
"LLVMTargetTripleVendor": .plString("unknown"),
805807
])
806808
]),

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

+2
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,7 @@ public final class BuiltinMacros {
806806
public static let LIBRARY_SEARCH_PATHS = BuiltinMacros.declarePathListMacro("LIBRARY_SEARCH_PATHS")
807807
public static let LIBTOOL = BuiltinMacros.declarePathMacro("LIBTOOL")
808808
public static let LIBTOOL_DEPENDENCY_INFO_FILE = BuiltinMacros.declarePathMacro("LIBTOOL_DEPENDENCY_INFO_FILE")
809+
public static let LIBTOOL_USE_RESPONSE_FILE = BuiltinMacros.declareBooleanMacro("LIBTOOL_USE_RESPONSE_FILE")
809810
public static let LINKER = BuiltinMacros.declareStringMacro("LINKER")
810811
public static let ALTERNATE_LINKER = BuiltinMacros.declareStringMacro("ALTERNATE_LINKER")
811812
public static let LINK_OBJC_RUNTIME = BuiltinMacros.declareBooleanMacro("LINK_OBJC_RUNTIME")
@@ -1852,6 +1853,7 @@ public final class BuiltinMacros {
18521853
LIBRARY_SEARCH_PATHS,
18531854
LIBTOOL,
18541855
LIBTOOL_DEPENDENCY_INFO_FILE,
1856+
LIBTOOL_USE_RESPONSE_FILE,
18551857
LINKER,
18561858
LINK_OBJC_RUNTIME,
18571859
LINK_WITH_STANDARD_LIBRARIES,

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

+2
Original file line numberDiff line numberDiff line change
@@ -5262,6 +5262,8 @@ extension OperatingSystem {
52625262
return "windows"
52635263
case .linux:
52645264
return "linux"
5265+
case .freebsd:
5266+
return "freebsd"
52655267
case .android:
52665268
return "android"
52675269
case .unknown:

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

+12-7
Original file line numberDiff line numberDiff line change
@@ -1419,17 +1419,22 @@ public final class LibtoolLinkerSpec : GenericLinkerSpec, SpecIdentifierType {
14191419

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

1423-
// Define the linker file list.
1424-
let fileListPath = cbc.scope.evaluate(BuiltinMacros.__INPUT_FILE_LIST_PATH__)
1425-
if !fileListPath.isEmpty {
1426-
let contents = cbc.inputs.map({ return $0.absolutePath.strWithPosixSlashes + "\n" }).joined(separator: "")
1427-
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])
1428-
inputPaths.append(fileListPath)
1424+
if cbc.scope.evaluate(BuiltinMacros.LIBTOOL_USE_RESPONSE_FILE) {
1425+
// Define the linker file list.
1426+
let fileListPath = cbc.scope.evaluate(BuiltinMacros.__INPUT_FILE_LIST_PATH__)
1427+
if !fileListPath.isEmpty {
1428+
let contents = cbc.inputs.map({ return $0.absolutePath.strWithPosixSlashes + "\n" }).joined(separator: "")
1429+
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])
1430+
inputPaths.append(fileListPath)
1431+
}
1432+
} else {
1433+
specialArgs.append(contentsOf: cbc.inputs.map { $0.absolutePath.str })
1434+
inputPaths.append(contentsOf: cbc.inputs.map { $0.absolutePath })
14291435
}
14301436

14311437
// Add arguments for the contents of the Link Binaries build phase.
1432-
var specialArgs = [String]()
14331438
specialArgs.append(contentsOf: libraries.flatMap { specifier -> [String] in
14341439
let basename = specifier.path.basename
14351440

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 + llvmDirectoriesLocal).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))
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
}

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
#endif

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

0 commit comments

Comments
 (0)