Skip to content

Add FreeBSD platform support#565

Open
networkextension wants to merge 38 commits into
swiftlang:mainfrom
networkextension:freebsd-platform
Open

Add FreeBSD platform support#565
networkextension wants to merge 38 commits into
swiftlang:mainfrom
networkextension:freebsd-platform

Conversation

@networkextension

@networkextension networkextension commented Jun 20, 2026

Copy link
Copy Markdown

Summary

Introduces a `FreeBSDPlatform` target so swiftly compiles, runs, and passes its full test suite on FreeBSD, alongside the existing Linux and macOS platforms.

Approach

  • New `FreeBSDPlatform` (`struct FreeBSD: Platform`), modeled on `LinuxPlatform` since FreeBSD's Swift toolchains are tarball-based like Linux. `detectPlatform` returns a single `PlatformDefinition.freebsd`; the install/extract/directory logic reuses the portable POSIX/Foundation paths.
  • `Swiftly` and `TestSwiftly` select it via `#elseif os(FreeBSD)`, and the POSIX `os(macOS) || os(Linux)` guards in `SwiftlyCore` (`ProcessInfo`, `Platform`, `Platform+Process`, `Terminal`) now include FreeBSD.
  • Adds `PlatformDefinition.freebsd`.
  • `FreeBSDPlatform` is gated with `.when(platforms: [.custom("freebsd")])` (thanks @kkebo), matching the `LinuxPlatform`/`MacOSPlatform` entries — `PackageDescription` has no `.freebsd` case, but `.custom("freebsd")` works, so it stays out of the graph on non-FreeBSD builds.
  • The release-packaging tool's `run()` throws on FreeBSD for now (the Linux release path is musl-SDK specific).
  • `verifySystemPrerequisitesForInstall`: uses `pkg info -e` and FreeBSD dependency names (`libuuid`, `python311`, `sqlite3`) from swift-installer-scripts.
  • `getShell`: falls back to `/bin/sh` (not `/bin/bash`, which is not the default shell on FreeBSD).

Testing

All 170 SwiftlyTests pass on two FreeBSD boards:

  • FreeBSD 15.1 / aarch64 (NXP DPAA2, 16-core, native Swift 6.3.2-RELEASE toolchain)
  • FreeBSD 14.4 / aarch64 (NXP DPAA2, native Swift 6.3.2-RELEASE toolchain)

CI runs via Cloudflare Tunnel to self-hosted boards (see workflow). HTTPClientTests (21 tests) require real network access to swift.org and also pass.

Tests use `Subprocess.run` instead of `Foundation.Process` on FreeBSD — `Process.run()` triggers a `_CFRunLoopCreate` assertion when called from a libdispatch worker thread on FreeBSD, causing SIGTRAP. `Subprocess` uses `posix_spawn` internally and is concurrency-safe.

Note: swift.org doesn't yet publish FreeBSD toolchains, so this is foundational platform support — it makes swiftly itself build/run on FreeBSD and will enable toolchain management once FreeBSD builds are published.

Introduces a `FreeBSDPlatform` target (`struct FreeBSD: Platform`) so swiftly
compiles and runs on FreeBSD, alongside the existing Linux and macOS platforms.

- New `FreeBSDPlatform` target, modeled on `LinuxPlatform` (FreeBSD's toolchain
  layout is tarball-based like Linux). `detectPlatform` returns a single
  `.freebsd` `PlatformDefinition`; install/extract/dirs reuse the portable
  POSIX/Foundation paths.
- Wired into `Swiftly`/`TestSwiftly` (`#elseif os(FreeBSD)`), and the POSIX
  `os(macOS) || os(Linux)` guards in `SwiftlyCore` (ProcessInfo, Platform,
  Platform+Process, Terminal) now include FreeBSD.
- Added `PlatformDefinition.freebsd`.
- `FreeBSDPlatform` is an unconditional package dependency that self-guards with
  `#if os(FreeBSD)`, because `PackageDescription` currently has no `.freebsd`
  platform (so `.when(platforms: [.freebsd])` is unavailable).
- The release-packaging tool's `run()` throws on FreeBSD for now (the Linux
  release path is musl-SDK specific).

Verified: builds and runs (`swiftly --help` / `--version`) natively on
FreeBSD 15.1/aarch64.
Two follow-ups to the FreeBSDPlatform support:

- FreeBSD.swift: verifySwiftlySystemPrerequisites checked the Linux CA
  bundle paths (/etc/ssl/certs/ca-certificates.crt,
  /etc/pki/tls/certs/ca-bundle.crt), which don't exist on FreeBSD, so it
  always reported "ca-certificates not installed". Check the FreeBSD trust
  store instead: /usr/local/etc/ssl/cert.pem (security/ca_root_nss),
  /etc/ssl/cert.pem (base), and /usr/local/share/certs/ca-root-nss.crt.

- SwiftlyTests.swift: the MockToolchainDownloader helpers
  (makeMockedSwiftly/makeMockedToolchain, the signatures cache, and the
  on-disk toolchain verifier) were guarded #if os(Linux)/#elseif os(macOS)
  only, so the test target failed to compile on FreeBSD. Add os(FreeBSD)
  to the Linux branches; the Linux mock builds a .tar.gz + GPG signature,
  which is exactly what FreeBSDPlatform consumes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kkebo

kkebo commented Jun 21, 2026

Copy link
Copy Markdown
Member

.when(platforms: [.freebsd]) can't be used

You can use .when(platforms: [.custom("freebsd")]) instead.

…rm dep

Per review feedback (@kkebo): instead of depending on FreeBSDPlatform
unconditionally (and relying on the target's internal `#if os(FreeBSD)`
self-guard), gate the dependency with `.when(platforms: [.custom("freebsd")])`,
matching the LinuxPlatform/MacOSPlatform entries. This keeps FreeBSDPlatform
out of the dependency graph entirely on non-FreeBSD builds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@networkextension

Copy link
Copy Markdown
Author

Thanks @kkebo — that's much cleaner. Applied in 4f25b50: the three FreeBSDPlatform dependencies now use .when(platforms: [.custom("freebsd")]), matching the LinuxPlatform/MacOSPlatform entries, so it's no longer pulled into the graph on non-FreeBSD builds. I'll update the PR description note accordingly.

yarshure and others added 2 commits June 21, 2026 03:32
SelfUpdate.execute hit `fatalError("Unsupported OS")` on FreeBSD, which
crashed the whole test process (SIGTRAP) and aborted the suite. Treat
FreeBSD like Linux (tarball-based): build a freebsd/swiftly-*.tar.gz
download URL for an explicitly-requested version, and match the Linux
release artifact in the auto-update path (the swift.org website API has
no FreeBSD platform yet; this can switch to a dedicated FreeBSD artifact
once one is published).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Without these additions, running the test suite on FreeBSD would fail:

1. `HTTPClient.platformDef` maps swift.org API platform name strings
   (e.g. "Ubuntu 22.04") to `PlatformDefinition`. Adding "FreeBSD"
   lets `Platform.matches(_:)` return true when filtering mock releases.

2. `HTTPClient.getSnapshotToolchains` had no `freebsd` case — it would
   throw "No snapshot toolchains available". FreeBSD goes through the
   same `.init(platform.name)` path as ubuntu2404/debian12/fedora.

3. `MockToolchainDownloader.getReleaseToolchains()` in the test suite
   maps the detected platform to a swift.org-style platform name string.
   Without a `PlatformDefinition.freebsd` case it would throw
   `SwiftlyTestError("Could not detect the current platform in test")`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WDxhuavrKQXkFay9JMFAhK
Comment thread Sources/FreeBSDPlatform/FreeBSD.swift
Comment thread Sources/FreeBSDPlatform/FreeBSD.swift
Comment thread Sources/FreeBSDPlatform/FreeBSD.swift Outdated
@jakepetroules

Copy link
Copy Markdown
Contributor

Awesome to see more FreeBSD progress!

yarshure and others added 2 commits June 24, 2026 01:29
Adds a `freebsd-aarch64-tests` job that runs on ubuntu-latest, installs
cloudflared, and SSHes into the FreeBSD aarch64 board via a named
Cloudflare Tunnel to build and run the swiftly test suite natively.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Heredoc + GitHub Actions expression expansion was unreliable.
Switch to env: vars + printf for robust config file creation.
Also fix ~ expansion in rsync -e arg by using $HOME.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@networkextension

Copy link
Copy Markdown
Author

@jakepetroules Thanks! Here is a full status update.

Test results on FreeBSD 15.1 aarch64 (Swift 6.3.2-RELEASE, NXP DPAA2 board)

HTTPClientTests — all pass with NIOSSL CA fix

The prior failure (CERTIFICATE_VERIFY_FAILED) was a missing FreeBSD branch in swift-nio-ssl's platformDefaultConfiguration() — zero CA trust roots were loaded. With a two-line fix, all 6 tests pass over real HTTPS to swift.org/download.swift.org:

✔ Test getSwiftOrgGPGKeys() passed after 0.700 seconds.
✔ Test getSwiftToolchain() passed after 109.816 seconds.
✔ Test getSwiftlyLinuxReleases(url:) with 6 test cases passed after 31.636 seconds.
✔ Test getSwiftlyMacOSReleases(url:) with 3 test cases passed after 6.700 seconds.
✔ Test getSwiftlyReleaseMetadataFromSwiftOrg() passed after 0.576 seconds.
✔ Test getToolchainMetadataFromSwiftOrg(_:) with 9 test cases passed after 34.348 seconds.
✔ Suite HTTPClientTests passed after 183.785 seconds.
✔ Test run with 6 tests in 1 suite passed after 183.786 seconds.

The NIOSSL fix (SSLContext.swift + LinuxCABundle.swift):

-#if os(Linux)
+#if os(Linux) || os(FreeBSD)

Plus FreeBSD CA paths: /usr/local/etc/ssl/cert.pem, /etc/ssl/cert.pem, /usr/local/share/certs/ca-root-nss.crt. Will open a PR to swift-nio-ssl.

UpdateTests — all pass

✔ Suite UpdateTests passed after 11.360 seconds.
✔ Test run with 13 tests in 2 suites passed after 20.007 seconds.

All other suites (PlatformTests, InstallTests, UninstallTests, LinkTests, ListTests, UseTests, etc.) confirmed passing in earlier runs on the same board.

Dependency patches to build and test on FreeBSD

swift test pulls in swift-nio and swift-nio-ssl, which each need FreeBSD fixes. All are upstream PR candidates.

swift-nio / _NIOFileSystem:

  • O_TMPFILE is Linux-only — exclude FreeBSD from FileDescriptor.OpenOptions.temporaryFile; keep currentWorkingDirectory (AT_FDCWD is on FreeBSD)
  • xattr (flistxattr, fgetxattr, fsetxattr, fremovexattr) — FreeBSD uses extattr_*; add #elseif os(FreeBSD) stubs returning -1
  • sendfile — FreeBSD reverses file/socket args and returns bytes via out-param; needs its own #if os(FreeBSD) block
  • RENAME_NOREPLACE, AT_EMPTY_PATH, CNIOLinux_renameat2 — add to CNIOLinux.h FreeBSD section (FreeBSD 15 has these natively)
  • Errno.noData (ENODATA) — not on FreeBSD; guard with #if !os(FreeBSD)
  • fts_info is int on FreeBSD (not u_short); DT_* constants need || os(FreeBSD) on Int32 branch

swift-nio / NIOCore + NIOPosix:

  • Use os(OpenBSD) || os(FreeBSD) blocks (not os(Linux) || os(FreeBSD)) — Glibc on FreeBSD doesn't expose the same symbols as on Linux
  • kqueue: EVFILT_EXCEPT stub; isEarlyEOFDeliveryWorkingOnThisOS = false
  • CMSG shims via CNIOOpenBSD_CMSG_*; BSD sendfile via CNIOOpenBSD.sendfile(); UIO_MAXIOV=1024; SHUT_* as literals

swift-nio-ssl:

  • Add #elseif os(FreeBSD) import block in NIOSSLClientHandler, IdentityVerification, SubjectAlternativeName
  • Route inet_ntop through CNIOOpenBSD_inet_ntop() on FreeBSD
  • CA trust root fix above

Related: Synchronization.Mutex deadlock

Synchronization.Mutex deadlock on FreeBSD (swiftlang/swift#90057) has a fix PR at swiftlang/swift#90143. That was the root cause of swift-testing's SIGTRAP on FreeBSD.

Happy to open separate upstream PRs to swift-nio and swift-nio-ssl for the dependency patches.

yarshure and others added 2 commits June 24, 2026 01:51
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
cloudflared access ssh requires a Cloudflare Access JWT service token
for automated sessions; tcp tunneling bypasses the Access layer and
only requires the SSH key we already have.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
yarshure and others added 11 commits June 24, 2026 02:47
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes redundantReturn, indent, trailingCommas, braces, hoistAwait/Try,
redundantOptionalBinding, unusedArguments flagged by swiftformat lint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previous commit used 0.61.1 which formats switch expressions differently.
Re-run with the exact version pinned in Package.swift.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace debug ssh uname -a with actual swift build + swift test
- Fix swiftformat: remove trailing comma from detectPlatform params,
  fix indent on multi-line SwiftlyError message string

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Use heredoc + /bin/sh to avoid tcsh login shell on board
- Collapse SwiftlyError message: onto one line (swiftformat indent rule)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CNIOBoringSSL/ssl_lib.cc calls gettimeofday() but doesn't include
<sys/time.h> on FreeBSD. Pass -Xcc -include to both build and test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
-Xcc -include sys/time.h breaks C++ TUs (sys/_types.h conflicts with
BoringSSL defines). Instead patch the checkout directly before building:
add #include "sys/time.h" to ssl_lib.cc so gettimeofday is declared.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
FreeBSD's pthread_mutex_t and pthread_mutexattr_t are OpaquePointer
(like OpenBSD), not structs. Treat FreeBSD the same as OpenBSD in
Locks.swift so the optional-pointer API and bitPattern init are used.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ReadWriteLock class had no OpenBSD branch, so FreeBSD fell through to the
non-optional UnsafeMutablePointer<pthread_rwlock_t> path. FreeBSD's
pthread_rwlock_t is OpaquePointer and the API expects the optional form.
Switch to idempotent Python patch covering both Lock and ReadWriteLock.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Python code at column-0 inside a YAML literal block terminates the block
early (YAML ends scalars when indentation drops below the block level).
Switch back to sed-only approach: two guarded idempotent sed commands cover
both the Lock class (os(OpenBSD) → also FreeBSD) and ReadWriteLock class
(pthread_rwlock_t → optional form for FreeBSD's opaque pointer).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Multiple packages (swift-log, swift-distributed-tracing, etc.) vendor a
copy of NIO's Locks.swift with the same pthread_rwlock_t type mismatch.
Apply fixes to all Locks.swift files under .build/checkouts at once to
avoid one-by-one failures.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
yarshure and others added 17 commits June 24, 2026 06:26
…reeBSD

On FreeBSD, canImport(Glibc) is true but Glibc.stdout/stderr are
non-optional (UnsafeMutablePointer<FILE>, not Optional). The Glibc.stdout!
and Glibc.stderr! force-unwraps that work on Linux fail on FreeBSD.
Strip the ! for those specific qualified references.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
swift-http-types has NIOLock.swift with same pthread opaque pointer issues.
Also fix LockPrimitive typealias: pthread_mutex_t -> pthread_mutex_t? so
UnsafeMutablePointer<LockPrimitive> matches what pthread_mutex_init expects
on FreeBSD.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs in the previous sed-based patch:
1. BSD sed does not reliably emit a newline for \n in the replacement string
2. #include "sys/time.h" (quoted/relative) finds BoringSSL's own shadowed
   sys/time.h rather than the system header; <sys/time.h> (angle brackets)
   bypasses BoringSSL's -I paths

Switch to awk for reliable line insertion before ssl_local.h, and use
<sys/time.h> to guarantee the system declaration of gettimeofday.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ranch

Files without an os(OpenBSD) branch fall through to the generic pthread path
with 'var attr = pthread_mutexattr_t()'. On FreeBSD, pthread_mutexattr_t is
OpaquePointer and has no default init — needs bitPattern: 0 (produces nil,
which pthread_mutexattr_init then initializes to a valid attr).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The file already has <sys/time.h> inside #else !OPENSSL_WINDOWS, but
gettimeofday remains undeclared in the BSSL namespace on FreeBSD.
Guard on a unique marker and insert the include unconditionally at line
1, before any #if blocks and before BSSL_NAMESPACE_BEGIN.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
FreeBSD's sys/time.h guards gettimeofday behind #if __XSI_VISIBLE, which
is 0 unless _XOPEN_SOURCE>=500. BoringSSL compilation never defines it,
so gettimeofday was invisible even with the include present. Adding
_XOPEN_SOURCE 600 before the include makes the declaration visible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MutexType/ConditionType are pthread_mutex_t/pthread_cond_t which map to
OpaquePointer on FreeBSD (opaque, not a struct). Wrap them in an
#if os(FreeBSD) || os(OpenBSD) block to use the optional form so
UnsafeMutablePointer<MutexType> matches what pthread_mutex_init expects.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
getentropy is in unistd.h behind #if __BSD_VISIBLE on FreeBSD, which is
0 when BoringSSL's compile flags restrict POSIX visibility. Forward-
declaring extern "C" int getentropy(void*, size_t) at line 1 bypasses
the feature macro issue entirely.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pthread_t is OpaquePointer on FreeBSD (no default initializer). Use
unsafeBitCast(0 as UInt) as a placeholder — _subprocess_pthread_create
takes _Nonnull ptr so the variable must stay non-optional; pthread_create
overwrites it before any read.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ibc)

pthread_t on FreeBSD is an opaque C pointer; _subprocess_pthread_create
takes pthread_t* which Swift imports as UnsafeMutablePointer<pthread_t?>.
Excluding FreeBSD from the canImport(Glibc) guard makes it use the #else
branch (pthread_t?) which matches macOS and the expected function type.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- verifySystemPrerequisitesForInstall: replace Linux package database
  with FreeBSD deps (libuuid, python311, sqlite3) from swift-installer-scripts
- isSystemPackageInstalled: use pkg info -e instead of apt-get/dnf/yum
- getShell: fall back to /bin/sh, not /bin/bash (bash not default on FreeBSD)
Foundation.Process.run() calls _CFRunLoopGet0() to register child-process
monitoring with the current thread's run loop. On FreeBSD, __CFRunLoopCreate
hits a TRAP assertion when invoked from a libdispatch DispatchWorker thread
(any Swift async context), causing the test binary to die with signal 5.

Replace all three async usages (version(), makeMockedSwiftlyRelease,
makeMockedToolchain) with Subprocess.run, which uses posix_spawn internally
and is concurrency-safe on all platforms. Also simplify the GPG sign step
to always use --pinentry-mode loopback (GPG 2.1+, as shipped by FreeBSD).

Verified: swift test --filter SwiftlyTests exits 0 on FreeBSD 15.1 aarch64.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add FreeBSD 14.4 aarch64 job (board .29, secret FREEBSD_CF_HOSTNAME_144)
- Patch NIOSSL to load CA certs on FreeBSD (fix CERTIFICATE_VERIFY_FAILED
  in HTTPClientTests): add FreeBSD paths to LinuxCABundle.rootCAFileSearchPaths
  and add #elseif os(FreeBSD) branch to SSLContext.platformDefaultConfiguration
- Stop orphaned swift-test/swift-build processes at CI job start
- 90-minute timeout on both FreeBSD jobs
- Use doas for privilege escalation on FreeBSD 14.4 board

Both boards verified green: all 170 SwiftlyTests pass on FreeBSD 15.1 aarch64
and FreeBSD 14.4 aarch64.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Subprocess+BSD.swift uses DispatchSource makeProcessSource (kqueue
EVFILT_PROC | NOTE_EXIT) to detect child process exit. On FreeBSD 15.1,
when the child exits between the WNOHANG check and kqueue registration,
the NOTE_EXIT event is missed and monitorProcessTermination waits forever.
This caused swift test to hang for 90 minutes and timeout.

Fix: patch Subprocess+BSD.swift in the CI checkout to replace the
DispatchSource approach with a 10ms-interval polling loop using
waitid(WNOHANG). Polling is race-free for process termination detection
and has negligible overhead for the short-lived processes in the tests.
Applied to both FreeBSD 15.1 and 14.4 jobs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@networkextension

Copy link
Copy Markdown
Author

@jakepetroules thanks for the review — addressed all three in da8a452:

  • FreeBSD runtime deps (libuuid, python311, sqlite3) per the makePackage list you linked, replacing the Linux packages.
  • Package detection/install via pkg info -e / pkg install (no more apt/dnf/yum).
  • Default shell falls back to /bin/sh.

Separately, the underlying Synchronization.Mutex FreeBSD deadlock (swiftlang/swift#90057) now has a fix in swiftlang/swift#90143, which I have cross-built and verified deadlock-free under contention (16 threads × 100k) on FreeBSD 14.4 and 15.1 / aarch64. Re-requesting review.

Lukasa added a commit to apple/swift-nio-ssl that referenced this pull request Jun 29, 2026
## Summary

`platformDefaultConfiguration()` in `SSLContext.swift` has branches for
Linux and Android but not FreeBSD. On FreeBSD the function is a no-op:
no CA trust roots are loaded, so every HTTPS client connection fails
immediately with `CERTIFICATE_VERIFY_FAILED`.

`LinuxCABundle.swift` already compiles for FreeBSD (`#if os(Linux) ||
os(FreeBSD)`) and defines `rootCAFilePath`/`rootCADirectoryPath`, but
the search path arrays contained only Linux distro paths — no FreeBSD
path was ever matched, so `rootCAFilePath` and `rootCADirectoryPath`
were always `nil`.

## Changes

**`SSLContext.swift`** — one line:
```diff
-#if os(Linux)
+#if os(Linux) || os(FreeBSD)
```

**`LinuxCABundle.swift`** — add FreeBSD paths to both search arrays:
```swift
// rootCAFileSearchPaths additions:
"/usr/local/etc/ssl/cert.pem",          // security/openssl port
"/etc/ssl/cert.pem",                     // base system (FreeBSD 14+)
"/usr/local/share/certs/ca-root-nss.crt", // security/ca_root_nss port

// rootCADirectorySearchPaths addition:
"/usr/local/share/certs",               // security/ca_root_nss port
```

## Testing

Verified on FreeBSD 15.1 aarch64 (NXP DPAA2, Swift 6.3.2-RELEASE). With
this fix, all HTTPS requests to `swift.org` and `download.swift.org`
succeed. Without it, every connection fails at the TLS handshake
regardless of which CA bundle is installed.

Tested as part of the swiftly FreeBSD port (swiftlang/swiftly#565),
where `HTTPClientTests` makes live HTTPS requests:

```
✔ Test getSwiftOrgGPGKeys() passed after 0.700 seconds.
✔ Test getSwiftToolchain() passed after 109.816 seconds.
✔ Test getSwiftlyLinuxReleases(url:) with 6 test cases passed after 31.636 seconds.
✔ Suite HTTPClientTests passed after 183.785 seconds.
```

🤖 Generated with [Claude Code](https://claude.ai/claude-code)

---------

Co-authored-by: lushan.ai <kequeue@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Cory Benfield <lukasa@apple.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants