Add FreeBSD platform support #999
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Pull Request | |
| permissions: | |
| contents: read | |
| on: | |
| pull_request: | |
| types: [opened, reopened, synchronize] | |
| push: | |
| branches: [main, 'release/*'] | |
| workflow_dispatch: | |
| jobs: | |
| soundness: | |
| name: Soundness | |
| uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main | |
| with: | |
| license_header_check_enabled: false | |
| license_header_check_project_name: "Swift.org" | |
| api_breakage_check_enabled: false | |
| docs_check_enabled: false | |
| format_check_enabled: false | |
| shell_check_enabled: false | |
| unacceptable_language_check_enabled: true | |
| freebsd-aarch64-tests: | |
| name: Test / FreeBSD aarch64 | |
| runs-on: ubuntu-latest | |
| if: ${{ github.repository == 'networkextension/swiftly' || github.repository == 'swiftlang/swiftly' }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Install cloudflared | |
| run: | | |
| curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null | |
| echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main' | sudo tee /etc/apt/sources.list.d/cloudflared.list | |
| sudo apt-get update && sudo apt-get install -y cloudflared | |
| - name: Set up SSH key and known hosts | |
| env: | |
| FREEBSD_SSH_KEY: ${{ secrets.FREEBSD_SSH_KEY }} | |
| FREEBSD_CF_HOSTNAME: ${{ secrets.FREEBSD_CF_HOSTNAME }} | |
| run: | | |
| mkdir -p "$HOME/.ssh" | |
| printf '%s\n' "$FREEBSD_SSH_KEY" > "$HOME/.ssh/freebsd_ci_key" | |
| chmod 600 "$HOME/.ssh/freebsd_ci_key" | |
| printf '%s ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFqpy2Vs6oX1K5khxWW7INHC8a6meSaSvNDVXpCSv1Gj\n' \ | |
| "$FREEBSD_CF_HOSTNAME" >> "$HOME/.ssh/known_hosts" | |
| chmod 600 "$HOME/.ssh/known_hosts" | |
| printf 'Host freebsd-ci\n HostName %s\n User swift\n IdentityFile %s/.ssh/freebsd_ci_key\n ProxyCommand cloudflared access tcp --hostname %%h\n' \ | |
| "$FREEBSD_CF_HOSTNAME" "$HOME" > "$HOME/.ssh/config" | |
| chmod 600 "$HOME/.ssh/config" | |
| - name: Sync source to board | |
| run: | | |
| ssh -F "$HOME/.ssh/config" freebsd-ci 'mkdir -p /home/swift/ci/swiftly' | |
| rsync -avz --delete \ | |
| --exclude='.build' --exclude='.git' \ | |
| -e "ssh -F $HOME/.ssh/config" \ | |
| . freebsd-ci:/home/swift/ci/swiftly/ | |
| - name: Build and Test on FreeBSD aarch64 | |
| run: | | |
| ssh -F "$HOME/.ssh/config" freebsd-ci /bin/sh << 'ENDSSH' | |
| set -e | |
| # Stop any swift processes left over from a previous cancelled/timed-out run. | |
| # This prevents concurrent build/test from corrupting the shared .build directory. | |
| pkill -u swift -x swift-test 2>/dev/null || true | |
| pkill -u swift -x swift-build 2>/dev/null || true | |
| pkill -u swift -f "swift-package resolve" 2>/dev/null || true | |
| pkill -u swift -f "swiftlyPackageTests.xctest" 2>/dev/null || true | |
| . "$HOME/swift632-env.sh" | |
| cd "$HOME/ci/swiftly" | |
| # Reset swift-nio checkout before resolve so SwiftPM can update it cleanly. | |
| # Without this, files added by a previous networkextension branch switch | |
| # (e.g. .github/workflows/freebsd-aarch64.yml) cause "local changes would | |
| # be overwritten" when the branch updates between runs. | |
| _NIODIR_PRE=$(find .build/checkouts -maxdepth 1 -name "swift-nio" -type d 2>/dev/null | head -1) | |
| if [ -n "$_NIODIR_PRE" ]; then | |
| chmod -R u+w "$_NIODIR_PRE" 2>/dev/null || true | |
| git config --global --add safe.directory "$(realpath "$_NIODIR_PRE")" 2>/dev/null || true | |
| git -C "$_NIODIR_PRE" reset --hard 2>/dev/null || true | |
| git -C "$_NIODIR_PRE" clean -ffd 2>/dev/null || true | |
| fi | |
| swift package resolve 2>&1 | |
| # SwiftPM on FreeBSD creates all checkout files as mode 444 (read-only). | |
| # Make them writable before any git operations or source patching below. | |
| chmod -R u+w .build/checkouts 2>/dev/null || true | |
| # swift-nio: switch checkout to FreeBSD-patched fork branch | |
| # networkextension/swift-nio freebsd-compat-2.93.0 is based on the 2.93.0 tag | |
| # and carries all FreeBSD compatibility fixes as proper source edits. | |
| NIODIR=$(find .build/checkouts -maxdepth 1 -name "swift-nio" -type d 2>/dev/null | head -1) | |
| if [ -n "$NIODIR" ]; then | |
| chmod -R u+w "$NIODIR/.git" 2>/dev/null || true | |
| git config --global --add safe.directory "$(realpath "$NIODIR")" 2>/dev/null || true | |
| git -C "$NIODIR" reset --hard 2>/dev/null || true | |
| git -C "$NIODIR" clean -ffd 2>/dev/null || true | |
| git -C "$NIODIR" remote add networkextension https://github.com/networkextension/swift-nio.git 2>/dev/null || true | |
| git -C "$NIODIR" fetch networkextension freebsd-compat-2.93.0 | |
| git -C "$NIODIR" checkout networkextension/freebsd-compat-2.93.0 | |
| echo "Switched swift-nio to networkextension/freebsd-compat-2.93.0" | |
| fi | |
| # CNIOBoringSSL: FreeBSD sys/time.h guards gettimeofday behind __XSI_VISIBLE, | |
| # which is 0 unless _XOPEN_SOURCE>=500. BoringSSL doesn't define it. | |
| # Inject _XOPEN_SOURCE 600 + sys/time.h unconditionally at line 1. | |
| # Also strip any prior v1 patch (2-line) before applying the new 5-line one. | |
| SSL_LIB=$(find .build/checkouts -path "*/CNIOBoringSSL*/ssl/ssl_lib.cc" 2>/dev/null | head -1) | |
| if [ -n "$SSL_LIB" ]; then | |
| grep -qF 'FreeBSD-gettimeofday-xsi' "$SSL_LIB" || { | |
| grep -q 'FreeBSD-gettimeofday' "$SSL_LIB" && sed -i '' '1,2d' "$SSL_LIB" | |
| awk 'NR==1{print "/* FreeBSD-gettimeofday-xsi */\n#ifndef _XOPEN_SOURCE\n#define _XOPEN_SOURCE 600\n#endif\n#include <sys/time.h>"}1' "$SSL_LIB" > /tmp/_ssl.cc | |
| mv /tmp/_ssl.cc "$SSL_LIB" | |
| echo "Patched $SSL_LIB" | |
| } | |
| fi | |
| # async-http-client CAsyncHTTPClient.c: strptime_l is behind __BSD_VISIBLE in | |
| # FreeBSD time.h; no locale.h included for FreeBSD. Forward-declare both. | |
| AHC=$(find .build/checkouts -path "*/CAsyncHTTPClient/CAsyncHTTPClient.c" 2>/dev/null | head -1) | |
| if [ -n "$AHC" ]; then | |
| grep -qF 'FreeBSD-strptime-fix' "$AHC" || { | |
| awk 'NR==1{print "/* FreeBSD-strptime-fix */\n#if defined(__FreeBSD__)\n#include <locale.h>\nextern char *strptime_l(const char *, const char *, struct tm *, locale_t);\n#endif"}1' \ | |
| "$AHC" > /tmp/_ahc.c | |
| mv /tmp/_ahc.c "$AHC" | |
| echo "Patched $AHC" | |
| } | |
| fi | |
| # CNIOBoringSSL: getentropy in unistd.h is behind __BSD_VISIBLE on FreeBSD. | |
| # Forward-declare it directly so no feature macros are needed. | |
| GE=$(find .build/checkouts -path "*/CNIOBoringSSL*/crypto/rand/getentropy.cc" 2>/dev/null | head -1) | |
| if [ -n "$GE" ]; then | |
| grep -qF 'FreeBSD-getentropy-fix' "$GE" || { | |
| awk 'NR==1{print "/* FreeBSD-getentropy-fix */\n#if defined(__FreeBSD__)\nextern \"C\" int getentropy(void*, __SIZE_TYPE__);\n#endif"}1' "$GE" > /tmp/_ge.cc | |
| mv /tmp/_ge.cc "$GE" | |
| echo "Patched $GE" | |
| } | |
| fi | |
| # CNIOBoringSSL umbrella header: arpa/inet.h is not included on FreeBSD, so | |
| # inet_ntop/inet_pton are invisible to Swift code (e.g. NIOSSL SubjectAlternativeName.swift). | |
| # Header lives at swift-nio-ssl/Sources/CNIOBoringSSL/include/CNIOBoringSSL.h (no subdir). | |
| CNIO_HDR=$(find .build/checkouts -path "*/CNIOBoringSSL/include/CNIOBoringSSL.h" 2>/dev/null | head -1) | |
| if [ -n "$CNIO_HDR" ]; then | |
| grep -qF 'FreeBSD-arpa-inet-v2' "$CNIO_HDR" || { | |
| sed -i '' '/^\/\* FreeBSD-arpa-inet/,$d' "$CNIO_HDR" | |
| printf '\n/* FreeBSD-arpa-inet-v2: inet_ntop/inet_pton are #defined to __inet_ntop/__inet_pton\n' >> "$CNIO_HDR" | |
| printf ' * in <arpa/inet.h> on FreeBSD, hiding them from the Swift Clang importer.\n' >> "$CNIO_HDR" | |
| printf ' * Both symbols are exported by libc at the same address. Undef and re-declare. */\n' >> "$CNIO_HDR" | |
| printf '#if defined(__FreeBSD__)\n#include <arpa/inet.h>\n#undef inet_ntop\n#undef inet_pton\n' >> "$CNIO_HDR" | |
| printf 'const char *inet_ntop(int, const void * __restrict, char * __restrict, unsigned int);\n' >> "$CNIO_HDR" | |
| printf 'int inet_pton(int, const char * __restrict, void * __restrict);\n#endif\n' >> "$CNIO_HDR" | |
| echo "Patched $CNIO_HDR" | |
| } | |
| fi | |
| # Other NIO-style lock files (NIOLock.swift etc, not swift-nio itself): | |
| # FreeBSD pthread types are opaque pointers like OpenBSD | |
| find .build/checkouts \( -name "Locks.swift" -o -name "NIOLock.swift" \) \ | |
| ! -path "*/swift-nio/*" 2>/dev/null | while read LOCKS; do | |
| grep -qF "pthread" "$LOCKS" || continue | |
| grep -qF "os(OpenBSD) || os(FreeBSD)" "$LOCKS" || \ | |
| sed -i '' 's/os(OpenBSD)/os(OpenBSD) || os(FreeBSD)/g' "$LOCKS" | |
| grep -qF "pthread_rwlock_t?>" "$LOCKS" || \ | |
| sed -i '' 's/pthread_rwlock_t> =/pthread_rwlock_t?> =/' "$LOCKS" | |
| grep -qF "LockPrimitive = pthread_mutex_t?" "$LOCKS" || \ | |
| sed -i '' 's/typealias LockPrimitive = pthread_mutex_t$/typealias LockPrimitive = pthread_mutex_t?/' "$LOCKS" | |
| grep -qF "pthread_mutexattr_t(bitPattern:" "$LOCKS" || \ | |
| sed -i '' 's/var attr = pthread_mutexattr_t()/var attr: pthread_mutexattr_t? = pthread_mutexattr_t(bitPattern: 0)/' "$LOCKS" | |
| echo "Patched $LOCKS" | |
| done | |
| # swift-log Logging.swift: Glibc.stdout/stderr non-optional on FreeBSD (unlike Linux) | |
| LOGGING=$(find .build/checkouts -path "*/swift-log*/Logging.swift" 2>/dev/null | head -1) | |
| if [ -n "$LOGGING" ]; then | |
| grep -qF "Glibc.stdout!" "$LOGGING" && \ | |
| sed -i '' 's/Glibc\.stdout!/Glibc.stdout/g; s/Glibc\.stderr!/Glibc.stderr/g' "$LOGGING" && \ | |
| echo "Patched $LOGGING" | |
| fi | |
| # swift-subprocess Thread.swift: two FreeBSD fixes — | |
| # 1) MutexType/ConditionType need optional typealiases (opaque pthread types) | |
| # 2) pthread_t is opaque on FreeBSD: exclude from canImport(Glibc) so FreeBSD | |
| # uses pthread_t? (like macOS); _subprocess_pthread_create wants ptr-to-optional | |
| SUBTHREAD=$(find .build/checkouts -path "*/swift-subprocess*/Thread.swift" 2>/dev/null | head -1) | |
| if [ -n "$SUBTHREAD" ]; then | |
| grep -qF "pthread_mutex_t?" "$SUBTHREAD" || { | |
| awk '/^private typealias MutexType = pthread_mutex_t$/{ | |
| print "#if os(FreeBSD) || os(OpenBSD)\nprivate typealias MutexType = pthread_mutex_t?\nprivate typealias ConditionType = pthread_cond_t?\n#else\n" $0; next} | |
| /^private typealias ConditionType = pthread_cond_t$/{print $0 "\n#endif"; next}1' \ | |
| "$SUBTHREAD" > /tmp/_th.swift | |
| mv /tmp/_th.swift "$SUBTHREAD" | |
| echo "Patched $SUBTHREAD (typealiases)" | |
| } | |
| # Remove stray column-0 #endif left by a prior bad undo (awk guard race) | |
| awk 'prev == " var thread = pthread_t()" && /^#endif$/{next} {prev=$0; print}' \ | |
| "$SUBTHREAD" > /tmp/_th.swift && mv /tmp/_th.swift "$SUBTHREAD" | |
| # pthread_t is also opaque on FreeBSD: exclude from canImport(Glibc) | |
| # so FreeBSD falls into #else branch and uses pthread_t? like macOS | |
| grep -qF '!os(FreeBSD) && !os(OpenBSD)) || canImport(Bionic)' "$SUBTHREAD" || { | |
| sed -i '' 's/#if canImport(Glibc) || canImport(Bionic)/#if (canImport(Glibc) \&\& !os(FreeBSD) \&\& !os(OpenBSD)) || canImport(Bionic)/g' "$SUBTHREAD" | |
| echo "Patched $SUBTHREAD (pthread_t? fix)" | |
| } | |
| fi | |
| # swift-service-lifecycle Lock.swift: only #if os(Windows)/#else, no FreeBSD case. | |
| # Insert #elseif os(OpenBSD||FreeBSD) with pthread_mutex_t? before the #else branch, | |
| # and fix pthread_mutexattr_t() which has no default init on FreeBSD. | |
| SVC=$(find .build/checkouts -path "*/swift-service-lifecycle*/ConcurrencyHelpers/Lock.swift" 2>/dev/null | head -1) | |
| if [ -n "$SVC" ]; then | |
| grep -qF "LockPrimitive = pthread_mutex_t?" "$SVC" || { | |
| awk '/^typealias LockPrimitive = SRWLOCK$/{a=1; print; next} | |
| a&&/^#else$/{print "#elseif os(OpenBSD) || os(FreeBSD)"; a=0; b=1; next} | |
| b&&/^@usableFromInline$/{b=0; print; c=1; next} | |
| c&&/^typealias LockPrimitive = pthread_mutex_t$/{print "typealias LockPrimitive = pthread_mutex_t?\n#else\n@usableFromInline\n"$0; c=0; next} | |
| 1' "$SVC" > /tmp/_svc.swift | |
| mv /tmp/_svc.swift "$SVC" | |
| sed -i '' 's/var attr = pthread_mutexattr_t()/var attr: pthread_mutexattr_t? = pthread_mutexattr_t(bitPattern: 0)/' "$SVC" | |
| echo "Patched $SVC" | |
| } | |
| fi | |
| # swift-async-algorithms Locking.swift: Glibc branch uses pthread_mutex_t. | |
| # Add os(FreeBSD)||os(OpenBSD) branch BEFORE it (2-space indent = typealias block only). | |
| LOCKING=$(find .build/checkouts -path "*/swift-async-algorithms*/AsyncAlgorithms/Locking.swift" 2>/dev/null | head -1) | |
| if [ -n "$LOCKING" ]; then | |
| grep -qF "os(FreeBSD) || os(OpenBSD)" "$LOCKING" || { | |
| sed -i '' 's/^ #elseif canImport(Glibc) || canImport(Musl) || canImport(Bionic) || canImport(wasi_pthread)$/ #elseif os(FreeBSD) || os(OpenBSD)\n typealias Primitive = pthread_mutex_t?\n #elseif canImport(Glibc) || canImport(Musl) || canImport(Bionic) || canImport(wasi_pthread)/' "$LOCKING" | |
| echo "Patched $LOCKING" | |
| } | |
| fi | |
| # NIOSSL LinuxCABundle.swift: add FreeBSD CA cert search paths. | |
| # The file already has os(FreeBSD) in its #if guard but only lists Linux paths. | |
| CABUNDLE=$(find .build/checkouts -path "*/NIOSSL/LinuxCABundle.swift" 2>/dev/null | head -1) | |
| if [ -n "$CABUNDLE" ]; then | |
| grep -qF '/usr/local/share/certs/ca-root-nss.crt' "$CABUNDLE" || { | |
| awk '/\/etc\/pki\/tls\/certs\/ca-bundle.crt/{print; print " \"/etc/ssl/cert.pem\", // FreeBSD (base)"; print " \"/usr/local/etc/ssl/cert.pem\", // FreeBSD (ca_root_nss)"; print " \"/usr/local/share/certs/ca-root-nss.crt\", // FreeBSD (ca_root_nss)"; next}1' "$CABUNDLE" > /tmp/_cab.swift | |
| mv /tmp/_cab.swift "$CABUNDLE" | |
| echo "Patched $CABUNDLE" | |
| } | |
| fi | |
| # NIOSSL SSLContext.swift: add FreeBSD branch to platformDefaultConfiguration. | |
| # Linux branch calls .withCString on non-optional; FreeBSD must guard-let first | |
| # because LinuxCABundle declares rootCAFilePath as String? (optional). | |
| SSLCTX=$(find .build/checkouts -path "*/NIOSSL/SSLContext.swift" 2>/dev/null | head -1) | |
| if [ -n "$SSLCTX" ]; then | |
| grep -qF 'os(FreeBSD)' "$SSLCTX" || { | |
| awk '/^ #elseif os\(Android\)$/ && !done{print " #elseif os(FreeBSD)"; print " guard let caFile = rootCAFilePath else { return }"; print " let result = caFile.withCString { fp in"; print " CNIOBoringSSL_SSL_CTX_load_verify_locations(context, fp, nil)"; print " }"; print " if result == 0 {"; print " let errorStack = BoringSSLError.buildErrorStack()"; print " throw BoringSSLError.unknownError(errorStack)"; print " }"; done=1}{print}' "$SSLCTX" > /tmp/_ctx.swift | |
| mv /tmp/_ctx.swift "$SSLCTX" | |
| echo "Patched $SSLCTX" | |
| } | |
| fi | |
| swift build 2>&1 | |
| swift test --filter SwiftlyTests 2>&1 | |
| ENDSSH | |
| timeout-minutes: 90 | |
| freebsd-aarch64-14-tests: | |
| name: Test / FreeBSD 14.4 aarch64 | |
| runs-on: ubuntu-latest | |
| if: ${{ github.repository == 'networkextension/swiftly' || github.repository == 'swiftlang/swiftly' }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Install cloudflared | |
| run: | | |
| curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null | |
| echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main' | sudo tee /etc/apt/sources.list.d/cloudflared.list | |
| sudo apt-get update && sudo apt-get install -y cloudflared | |
| - name: Set up SSH key and known hosts | |
| env: | |
| FREEBSD_SSH_KEY_144: ${{ secrets.FREEBSD_SSH_KEY }} | |
| FREEBSD_CF_HOSTNAME_144: ${{ secrets.FREEBSD_CF_HOSTNAME_144 }} | |
| run: | | |
| mkdir -p "$HOME/.ssh" | |
| printf '%s\n' "$FREEBSD_SSH_KEY_144" > "$HOME/.ssh/freebsd_ci_key_144" | |
| chmod 600 "$HOME/.ssh/freebsd_ci_key_144" | |
| printf '%s ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIQes9chPEJ0dX9K07yo8gtd8JzNw9mrPn7KwKC46Jje\n' \ | |
| "$FREEBSD_CF_HOSTNAME_144" >> "$HOME/.ssh/known_hosts" | |
| chmod 600 "$HOME/.ssh/known_hosts" | |
| printf 'Host freebsd-ci-2\n HostName %s\n User swift\n IdentityFile %s/.ssh/freebsd_ci_key_144\n ProxyCommand cloudflared access tcp --hostname %%h\n' \ | |
| "$FREEBSD_CF_HOSTNAME_144" "$HOME" > "$HOME/.ssh/config" | |
| chmod 600 "$HOME/.ssh/config" | |
| - name: Sync source to board | |
| run: | | |
| ssh -F "$HOME/.ssh/config" freebsd-ci-2 'mkdir -p /home/swift/ci/swiftly' | |
| rsync -avz --delete \ | |
| --exclude='.build' --exclude='.git' \ | |
| -e "ssh -F $HOME/.ssh/config" \ | |
| . freebsd-ci-2:/home/swift/ci/swiftly/ | |
| - name: Build and Test on FreeBSD 14.4 aarch64 | |
| run: | | |
| ssh -F "$HOME/.ssh/config" freebsd-ci-2 /bin/sh << 'ENDSSH' | |
| set -e | |
| # Ensure gnupg is installed — tests use gpg to sign mock toolchain archives. | |
| which gpg >/dev/null 2>&1 || doas pkg install -y gnupg 2>&1 || true | |
| # Stop any swift processes left over from a previous cancelled/timed-out run. | |
| pkill -u swift -x swift-test 2>/dev/null || true | |
| pkill -u swift -x swift-build 2>/dev/null || true | |
| pkill -u swift -f "swift-package resolve" 2>/dev/null || true | |
| pkill -u swift -f "swiftlyPackageTests.xctest" 2>/dev/null || true | |
| . "$HOME/swift632-env.sh" | |
| cd "$HOME/ci/swiftly" | |
| _NIODIR_PRE=$(find .build/checkouts -maxdepth 1 -name "swift-nio" -type d 2>/dev/null | head -1) | |
| if [ -n "$_NIODIR_PRE" ]; then | |
| chmod -R u+w "$_NIODIR_PRE" 2>/dev/null || true | |
| git config --global --add safe.directory "$(realpath "$_NIODIR_PRE")" 2>/dev/null || true | |
| git -C "$_NIODIR_PRE" reset --hard 2>/dev/null || true | |
| git -C "$_NIODIR_PRE" clean -ffd 2>/dev/null || true | |
| fi | |
| swift package resolve 2>&1 | |
| chmod -R u+w .build/checkouts 2>/dev/null || true | |
| NIODIR=$(find .build/checkouts -maxdepth 1 -name "swift-nio" -type d 2>/dev/null | head -1) | |
| if [ -n "$NIODIR" ]; then | |
| chmod -R u+w "$NIODIR/.git" 2>/dev/null || true | |
| git config --global --add safe.directory "$(realpath "$NIODIR")" 2>/dev/null || true | |
| git -C "$NIODIR" reset --hard 2>/dev/null || true | |
| git -C "$NIODIR" clean -ffd 2>/dev/null || true | |
| git -C "$NIODIR" remote add networkextension https://github.com/networkextension/swift-nio.git 2>/dev/null || true | |
| git -C "$NIODIR" fetch networkextension freebsd-compat-2.93.0 | |
| git -C "$NIODIR" checkout networkextension/freebsd-compat-2.93.0 | |
| echo "Switched swift-nio to networkextension/freebsd-compat-2.93.0" | |
| fi | |
| SSL_LIB=$(find .build/checkouts -path "*/CNIOBoringSSL*/ssl/ssl_lib.cc" 2>/dev/null | head -1) | |
| if [ -n "$SSL_LIB" ]; then | |
| grep -qF 'FreeBSD-gettimeofday-xsi' "$SSL_LIB" || { | |
| grep -q 'FreeBSD-gettimeofday' "$SSL_LIB" && sed -i '' '1,2d' "$SSL_LIB" | |
| awk 'NR==1{print "/* FreeBSD-gettimeofday-xsi */\n#ifndef _XOPEN_SOURCE\n#define _XOPEN_SOURCE 600\n#endif\n#include <sys/time.h>"}1' "$SSL_LIB" > /tmp/_ssl.cc | |
| mv /tmp/_ssl.cc "$SSL_LIB" | |
| echo "Patched $SSL_LIB" | |
| } | |
| fi | |
| AHC=$(find .build/checkouts -path "*/CAsyncHTTPClient/CAsyncHTTPClient.c" 2>/dev/null | head -1) | |
| if [ -n "$AHC" ]; then | |
| grep -qF 'FreeBSD-strptime-fix' "$AHC" || { | |
| awk 'NR==1{print "/* FreeBSD-strptime-fix */\n#if defined(__FreeBSD__)\n#include <locale.h>\nextern char *strptime_l(const char *, const char *, struct tm *, locale_t);\n#endif"}1' \ | |
| "$AHC" > /tmp/_ahc.c | |
| mv /tmp/_ahc.c "$AHC" | |
| echo "Patched $AHC" | |
| } | |
| fi | |
| GE=$(find .build/checkouts -path "*/CNIOBoringSSL*/crypto/rand/getentropy.cc" 2>/dev/null | head -1) | |
| if [ -n "$GE" ]; then | |
| grep -qF 'FreeBSD-getentropy-fix' "$GE" || { | |
| awk 'NR==1{print "/* FreeBSD-getentropy-fix */\n#if defined(__FreeBSD__)\nextern \"C\" int getentropy(void*, __SIZE_TYPE__);\n#endif"}1' "$GE" > /tmp/_ge.cc | |
| mv /tmp/_ge.cc "$GE" | |
| echo "Patched $GE" | |
| } | |
| fi | |
| CNIO_HDR=$(find .build/checkouts -path "*/CNIOBoringSSL/include/CNIOBoringSSL.h" 2>/dev/null | head -1) | |
| if [ -n "$CNIO_HDR" ]; then | |
| grep -qF 'FreeBSD-arpa-inet-v2' "$CNIO_HDR" || { | |
| sed -i '' '/^\/\* FreeBSD-arpa-inet/,$d' "$CNIO_HDR" | |
| printf '\n/* FreeBSD-arpa-inet-v2: inet_ntop/inet_pton are #defined to __inet_ntop/__inet_pton\n' >> "$CNIO_HDR" | |
| printf ' * in <arpa/inet.h> on FreeBSD, hiding them from the Swift Clang importer.\n' >> "$CNIO_HDR" | |
| printf ' * Both symbols are exported by libc at the same address. Undef and re-declare. */\n' >> "$CNIO_HDR" | |
| printf '#if defined(__FreeBSD__)\n#include <arpa/inet.h>\n#undef inet_ntop\n#undef inet_pton\n' >> "$CNIO_HDR" | |
| printf 'const char *inet_ntop(int, const void * __restrict, char * __restrict, unsigned int);\n' >> "$CNIO_HDR" | |
| printf 'int inet_pton(int, const char * __restrict, void * __restrict);\n#endif\n' >> "$CNIO_HDR" | |
| echo "Patched $CNIO_HDR" | |
| } | |
| fi | |
| find .build/checkouts \( -name "Locks.swift" -o -name "NIOLock.swift" \) \ | |
| ! -path "*/swift-nio/*" 2>/dev/null | while read LOCKS; do | |
| grep -qF "pthread" "$LOCKS" || continue | |
| grep -qF "os(OpenBSD) || os(FreeBSD)" "$LOCKS" || \ | |
| sed -i '' 's/os(OpenBSD)/os(OpenBSD) || os(FreeBSD)/g' "$LOCKS" | |
| grep -qF "pthread_rwlock_t?>" "$LOCKS" || \ | |
| sed -i '' 's/pthread_rwlock_t> =/pthread_rwlock_t?> =/' "$LOCKS" | |
| grep -qF "LockPrimitive = pthread_mutex_t?" "$LOCKS" || \ | |
| sed -i '' 's/typealias LockPrimitive = pthread_mutex_t$/typealias LockPrimitive = pthread_mutex_t?/' "$LOCKS" | |
| grep -qF "pthread_mutexattr_t(bitPattern:" "$LOCKS" || \ | |
| sed -i '' 's/var attr = pthread_mutexattr_t()/var attr: pthread_mutexattr_t? = pthread_mutexattr_t(bitPattern: 0)/' "$LOCKS" | |
| echo "Patched $LOCKS" | |
| done | |
| LOGGING=$(find .build/checkouts -path "*/swift-log*/Logging.swift" 2>/dev/null | head -1) | |
| if [ -n "$LOGGING" ]; then | |
| grep -qF "Glibc.stdout!" "$LOGGING" && \ | |
| sed -i '' 's/Glibc\.stdout!/Glibc.stdout/g; s/Glibc\.stderr!/Glibc.stderr/g' "$LOGGING" && \ | |
| echo "Patched $LOGGING" | |
| fi | |
| SUBTHREAD=$(find .build/checkouts -path "*/swift-subprocess*/Thread.swift" 2>/dev/null | head -1) | |
| if [ -n "$SUBTHREAD" ]; then | |
| grep -qF "pthread_mutex_t?" "$SUBTHREAD" || { | |
| awk '/^private typealias MutexType = pthread_mutex_t$/{ | |
| print "#if os(FreeBSD) || os(OpenBSD)\nprivate typealias MutexType = pthread_mutex_t?\nprivate typealias ConditionType = pthread_cond_t?\n#else\n" $0; next} | |
| /^private typealias ConditionType = pthread_cond_t$/{print $0 "\n#endif"; next}1' \ | |
| "$SUBTHREAD" > /tmp/_th.swift | |
| mv /tmp/_th.swift "$SUBTHREAD" | |
| echo "Patched $SUBTHREAD (typealiases)" | |
| } | |
| awk 'prev == " var thread = pthread_t()" && /^#endif$/{next} {prev=$0; print}' \ | |
| "$SUBTHREAD" > /tmp/_th.swift && mv /tmp/_th.swift "$SUBTHREAD" | |
| grep -qF '!os(FreeBSD) && !os(OpenBSD)) || canImport(Bionic)' "$SUBTHREAD" || { | |
| sed -i '' 's/#if canImport(Glibc) || canImport(Bionic)/#if (canImport(Glibc) \&\& !os(FreeBSD) \&\& !os(OpenBSD)) || canImport(Bionic)/g' "$SUBTHREAD" | |
| echo "Patched $SUBTHREAD (pthread_t? fix)" | |
| } | |
| fi | |
| SVC=$(find .build/checkouts -path "*/swift-service-lifecycle*/ConcurrencyHelpers/Lock.swift" 2>/dev/null | head -1) | |
| if [ -n "$SVC" ]; then | |
| grep -qF "LockPrimitive = pthread_mutex_t?" "$SVC" || { | |
| awk '/^typealias LockPrimitive = SRWLOCK$/{a=1; print; next} | |
| a&&/^#else$/{print "#elseif os(OpenBSD) || os(FreeBSD)"; a=0; b=1; next} | |
| b&&/^@usableFromInline$/{b=0; print; c=1; next} | |
| c&&/^typealias LockPrimitive = pthread_mutex_t$/{print "typealias LockPrimitive = pthread_mutex_t?\n#else\n@usableFromInline\n"$0; c=0; next} | |
| 1' "$SVC" > /tmp/_svc.swift | |
| mv /tmp/_svc.swift "$SVC" | |
| sed -i '' 's/var attr = pthread_mutexattr_t()/var attr: pthread_mutexattr_t? = pthread_mutexattr_t(bitPattern: 0)/' "$SVC" | |
| echo "Patched $SVC" | |
| } | |
| fi | |
| LOCKING=$(find .build/checkouts -path "*/swift-async-algorithms*/AsyncAlgorithms/Locking.swift" 2>/dev/null | head -1) | |
| if [ -n "$LOCKING" ]; then | |
| grep -qF "os(FreeBSD) || os(OpenBSD)" "$LOCKING" || { | |
| sed -i '' 's/^ #elseif canImport(Glibc) || canImport(Musl) || canImport(Bionic) || canImport(wasi_pthread)$/ #elseif os(FreeBSD) || os(OpenBSD)\n typealias Primitive = pthread_mutex_t?\n #elseif canImport(Glibc) || canImport(Musl) || canImport(Bionic) || canImport(wasi_pthread)/' "$LOCKING" | |
| echo "Patched $LOCKING" | |
| } | |
| fi | |
| # NIOSSL LinuxCABundle.swift: add FreeBSD CA cert search paths. | |
| CABUNDLE=$(find .build/checkouts -path "*/NIOSSL/LinuxCABundle.swift" 2>/dev/null | head -1) | |
| if [ -n "$CABUNDLE" ]; then | |
| grep -qF '/usr/local/share/certs/ca-root-nss.crt' "$CABUNDLE" || { | |
| awk '/\/etc\/pki\/tls\/certs\/ca-bundle.crt/{print; print " \"/etc/ssl/cert.pem\", // FreeBSD (base)"; print " \"/usr/local/etc/ssl/cert.pem\", // FreeBSD (ca_root_nss)"; print " \"/usr/local/share/certs/ca-root-nss.crt\", // FreeBSD (ca_root_nss)"; next}1' "$CABUNDLE" > /tmp/_cab.swift | |
| mv /tmp/_cab.swift "$CABUNDLE" | |
| echo "Patched $CABUNDLE" | |
| } | |
| fi | |
| # NIOSSL SSLContext.swift: add FreeBSD branch to platformDefaultConfiguration. | |
| SSLCTX=$(find .build/checkouts -path "*/NIOSSL/SSLContext.swift" 2>/dev/null | head -1) | |
| if [ -n "$SSLCTX" ]; then | |
| grep -qF 'os(FreeBSD)' "$SSLCTX" || { | |
| awk '/^ #elseif os\(Android\)$/ && !done{print " #elseif os(FreeBSD)"; print " guard let caFile = rootCAFilePath else { return }"; print " let result = caFile.withCString { fp in"; print " CNIOBoringSSL_SSL_CTX_load_verify_locations(context, fp, nil)"; print " }"; print " if result == 0 {"; print " let errorStack = BoringSSLError.buildErrorStack()"; print " throw BoringSSLError.unknownError(errorStack)"; print " }"; done=1}{print}' "$SSLCTX" > /tmp/_ctx.swift | |
| mv /tmp/_ctx.swift "$SSLCTX" | |
| echo "Patched $SSLCTX" | |
| } | |
| fi | |
| swift build 2>&1 | |
| swift test --filter SwiftlyTests 2>&1 | |
| ENDSSH | |
| timeout-minutes: 90 | |
| macos-tests-selfhosted: | |
| name: Test (Self Hosted) / macOS Tahoe ARM64 | |
| runs-on: [self-hosted, macos, tahoe, ARM64] | |
| if: ${{ github.repository == 'swiftlang/swiftly' }} | |
| strategy: | |
| fail-fast: false | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Prepare the action | |
| run: ./scripts/prep-gh-action.sh --install-swiftly | |
| - name: Build and Test | |
| run: swift test | |
| timeout-minutes: 60 | |
| tests-selfhosted: | |
| name: Test (Self Hosted) / ${{ matrix.container }} | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| container: ["ubuntu:22.04", "ubuntu:24.04", "redhat/ubi9", "debian:12", "fedora:39"] | |
| container: | |
| image: ${{ matrix.container }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Prepare the action | |
| run: ./scripts/prep-gh-action.sh --install-swiftly | |
| - name: Build and Test | |
| # UBI 9 - See https://github.com/swiftlang/swift/issues/80908 | |
| # UBI 9 - See https://github.com/swiftlang/swift/issues/80909 | |
| run: bash -c 'if [[ "${{ matrix.container }}" == "redhat/ubi9" ]]; then swift build --build-tests; else swift test; fi' | |
| # Verify new platforms without a released Swiftly build | |
| tests-bootstrapped: | |
| name: Test (Bootstrapped) / ${{ matrix.container }} | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| container: ["swiftlang/swift:nightly-6.3-fedora41"] | |
| container: | |
| image: ${{ matrix.container }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Prepare the action | |
| run: ./scripts/prep-gh-action.sh | |
| - name: Build and Test | |
| run: swift test | |
| releasebuildcheck: | |
| name: Release Build Check / Linux | |
| runs-on: ubuntu-latest | |
| container: | |
| image: "redhat/ubi9" | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Prepare the action | |
| run: ./scripts/prep-gh-action.sh --install-swiftly | |
| - name: Build Artifact | |
| run: swift run build-swiftly-release --test --skip "999.0.0" | |
| - name: Upload Artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: swiftly-release-x86_64 | |
| path: .build/release/swiftly-*.tar.gz | |
| if-no-files-found: error | |
| retention-days: 1 | |
| - name: Upload Tests | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: test-swiftly-linux-x86_64 | |
| path: .build/debug/test-swiftly-linux-x86_64.tar.gz | |
| if-no-files-found: error | |
| retention-days: 1 | |
| releasebuildcheckmacos: | |
| name: Release Build Check / macOS | |
| runs-on: [self-hosted, macos, tahoe, ARM64] | |
| if: ${{ github.repository == 'swiftlang/swiftly' }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Prepare the action | |
| run: ./scripts/prep-gh-action.sh --install-swiftly | |
| - name: Build Artifact | |
| run: swift run build-swiftly-release --test --skip "999.0.0" | |
| - name: Upload Artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: swiftly-release-darwin | |
| path: .build/release/swiftly-*.pkg | |
| if-no-files-found: error | |
| retention-days: 1 | |
| - name: Upload Tests | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: test-swiftly-macos | |
| path: .build/release/test-swiftly-macos.tar.gz | |
| if-no-files-found: error | |
| retention-days: 1 | |
| releasetest: | |
| name: Test Release / ${{matrix.shell.pkg}} | |
| needs: releasebuildcheck | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shell: [ | |
| {"pkg": "bash", "bin": "/bin/bash"}, | |
| {"pkg": "zsh", "bin": "/bin/zsh"}, | |
| {"pkg": "fish", "bin": "/bin/fish"} | |
| ] | |
| container: | |
| image: "ubuntu:24.04" | |
| steps: | |
| - name: Prepare System | |
| run: apt-get update && DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install ca-certificates gpg tzdata ${{matrix.shell.pkg}} && chsh -s ${{matrix.shell.bin}} | |
| - name: Download Release | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: swiftly-release-x86_64 | |
| - name: Download Tests | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: test-swiftly-linux-x86_64 | |
| - name: Extract and Run Workflow Tests | |
| run: cp swiftly-*.tar.gz /root/swiftly.tar.gz && cp test-swiftly-*.tar.gz /root && cd /root && tar zxf test-swiftly-*.tar.gz && ./test-swiftly -y ./swiftly.tar.gz | |
| release-custom-install-test: | |
| name: Test Release - Custom Install Location | |
| needs: releasebuildcheck | |
| runs-on: ubuntu-latest | |
| container: | |
| image: "ubuntu:24.04" | |
| steps: | |
| - name: Prepare System | |
| run: apt-get update && DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install ca-certificates gpg tzdata | |
| - name: Download Release | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: swiftly-release-x86_64 | |
| - name: Download Tests | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: test-swiftly-linux-x86_64 | |
| - name: Extract and Run Workflow Tests | |
| run: cp swiftly-*.tar.gz /root/swiftly.tar.gz && cp test-swiftly-*.tar.gz /root && cd /root && tar zxf test-swiftly-*.tar.gz && ./test-swiftly -y --custom-location ./swiftly.tar.gz | |
| formatcheck: | |
| name: Format Check | |
| runs-on: ubuntu-latest | |
| container: | |
| image: ubuntu:22.04 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Prepare the action | |
| run: ./scripts/prep-gh-action.sh --install-swiftly | |
| - name: Check format | |
| run: swift run swiftformat --lint --dryrun . || (echo "Please run 'swift run swiftformat .' to format the source code."; exit 1) | |
| docscheck: | |
| name: Documentation Check | |
| runs-on: ubuntu-latest | |
| container: | |
| image: ubuntu:24.04 | |
| steps: | |
| - name: Install git | |
| run: apt-get update && apt-get -y install git | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Prepare the action | |
| run: ./scripts/prep-gh-action.sh --install-swiftly | |
| - name: Generate Swiftly CLI Reference and Check for Differences | |
| run: swift package plugin --allow-writing-to-package-directory generate-docs-reference && bash -c 'git config --global --add safe.directory $(pwd)' && git diff --exit-code Documentation/SwiftlyDocs.docc/swiftly-cli-reference.md || (echo "The documentation hasn't been updated with the latest swiftly command-line reference. Please run 'swift package plugin generate-docs-reference' and commit/push the changes."; exit 1) | |
| - name: Generate Documentation Set | |
| run: swift package --allow-writing-to-directory .build/docs generate-documentation --target SwiftlyDocs --output-path .build/docs | |
| - name: Upload Documentation Artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: swiftly-docs | |
| path: .build/docs/** | |
| if-no-files-found: error | |
| retention-days: 1 |