Swift on FreeBSD

The language is being promoted as a replacement for the “monstrous” one. Objective-C for all types of application development for Apple products, so for example, all modern iOS developers write code in Swift.

Swift and FreeBSD

Back in 2016, it was made portwhich was maintained for three years by one person, but due to the lack of significant interest from the community and the enormous pace of development of Swift itself, the port was eventually abandoned:

It was not possible to use the work of the author of this port due to the very large difference between the versions – everything has changed a lot over the past years, so we had to go another way.

Digging into Swift sources and build scripts, I found references to OpenBSD and Haiku and even Android – upstream apparently accepts absolutely any kind of crap.

But I think you can judge for yourself how well it all works in practice using the steps described below.

As I noted above, the Swift toolchain is developing very dynamically, so even minor versions (5.7 – 5.8) differ greatly, for example, the difference between version 5.10 and 5.8 is about a hundred new source files, which is a lot for the C++ code, which is basically what everything is written in.

The instructions below are written for the latest stable version at the time of this article's creation – 5.10but most likely it will lose its relevance within six months due to the high rate of development of both the Swift language itself and its toolchain.

Collect and rule

I took as a basis this one now outdated instructions, the steps of which had to be changed for the new version 5.10 and running on FreeBSD 14.1 instead of the author's 13th.

The first and easiest step is to install the necessary packages:

pkg install bash cmake e2fsprogs-libuuid git ninja python3

Everything is simple and obvious here.

But the next step is to do something incredible – change the system headers a little:

The libc++ headers in FreeBSD require slight modifications in order to build Swift.

  1. The implementation of std::pair must be modified such that its copy constructor is trivial (as the C++ standard requires). For historical reasons, this is not currently the case in FreeBSD 13.1 (though there is ongoing work to fix it for FreeBSD 14.0).

  2. The libc++ module.modulemap requires a small tweak to fix the std.depr.stdint_h module. See this bug report for details.

I can't predict the possible consequences of such edits for the assembly of other projects, so it's worth using a virtual environment.

There are two header files that need to be fixed:

/usr/include/c++/v1/__config

--- __config
+++ __config
@@ -127,7 +127,7 @@
 #  endif
 // Feature macros for disabling pre ABI v1 features. All of these options
 // are deprecated.
-#  if defined(__FreeBSD__)
+#  if defined(__FreeBSD__) && (0)
 #    define _LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR
 #  endif
 #endif

The line numbers do not match because the patch is for FreeBSD 13, so you won't be able to apply the patch using standard tools, but I think the gist is clear anyway.

Condition:

if defined(__FreeBSD__)

is replaced by:

if defined(__FreeBSD__) && (0)

which will never work and thus the option _LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR will be disabled.

/usr/include/c++/v1/module.modulemap

Patch:

+    module stdint_h {
+      header "stdint.h"
+      export *
+      // FIXME: This module only exists on OS X and for some reason the
+      // wildcard above doesn't export it.
+      export Darwin.C.stdint
+    }

Despite the + at the beginning of the line, such a block is already present and all you need to do is find it using search and add it to the end of the line:

export Darwin.C.stdint

I think you've already guessed from the location of the files that you'll have to make edits as root.

The next step is to take the source code:

mkdir swift && cd swift
git clone -b release/5.10 --depth=1 https://github.com/apple/swift
git clone -b swift/release/5.10 --depth=1 https://github.com/apple/llvm-project
git clone -b release/5.10 --depth=1 https://github.com/apple/swift-cmark cmark
git clone -b release/5.10 --depth=1 https://github.com/apple/swift-syntax
git clone -b swift/release/5.10 --depth=1 https://github.com/apple/swift-experimental-string-processing.git

Next you need to create a build script, my version is slightly different from the original:

#!/bin/sh

SRCROOT="`pwd`/.."

cmake \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_PREFIX=/usr/local \
    -DCMAKE_SHARED_LINKER_FLAGS=-Wl,--undefined-version \
    -DLLVM_ENABLE_PROJECTS=clang \
    -DLLVM_TARGETS_TO_BUILD=X86 \
    -DLLVM_EXTERNAL_PROJECTS="cmark;swift" \
    -DLLVM_EXTERNAL_CMARK_SOURCE_DIR="${SRCROOT}/cmark" \
    -DLLVM_EXTERNAL_SWIFT_SOURCE_DIR="${SRCROOT}/swift" \
    -DSWIFT_PATH_TO_SWIFT_SYNTAX_SOURCE="${SRCROOT}/swift-syntax" \
    -DSWIFT_ENABLE_DISPATCH=OFF \
    -DSWIFT_IMPLICIT_CONCURRENCY_IMPORT=OFF \
    -DSWIFT_USE_LINKER=ld \
    -DSWIFT_BUILD_STATIC_STDLIB=ON \
    -DBOOTSTRAPPING_MODE=BOOTSTRAPPING \
    -DSWIFT_ENABLE_EXPERIMENTAL_STRING_PROCESSING=ON \
    -DSWIFT_PATH_TO_STRING_PROCESSING_SOURCE="${SRCROOT}/swift-experimental-string-processing" \
    -G Ninja \
    ../llvm-project/llvm    

The script must be placed one level below the build directory:

Problem with linker

For quite a long time I couldn’t even assemble the toolchain to the first stage; both the outdated 5.7 and 5.8 versions and the new 5.10 broke.

After a long period of shamanism over search engines, I found this such a post:

In llvm17, the linker option –no-allow-shlib-undefined became default. If there are any symbol not present in the library specified in the version script, the linker exits with error.

It turns out that LLVM's default behavior has changed, and a key that was once optional has suddenly become mandatory – i.e. this logic is now applied by default.

To disable you need this key:

LDFLAGS+= -Wl,--undefined-version

Which, in terms of a custom build script and cmake, turned into:

 -DCMAKE_SHARED_LINKER_FLAGS=-Wl,--undefined-version \   

Patch cmake scripts

But the above was not enough, we need another one interesting patchwithout which the build fails at the second stage – when the compiled Swift compiler compiles its environment, written in Swift itself:

Without this magic line, the build will crash with such spectacular errors that their mentions will not be found in any search engine.

My version of the patch is slightly different:

if(SWIFT_HOST_VARIANT_SDK MATCHES "LINUX|ANDROID|OPENBSD|FREEBSD")
      target_link_options(${target} PRIVATE "SHELL:-Xlinker -z -Xlinker nostart-stop-gc")
    endif()
 endif()

This block needs to be inserted into the script swift/cmake/modules/AddSwift.cmakeapproximate location on the screenshot below:

After all these edits, you can finally try to run the build:

mkdir build && cd build

First, we run the configure.sh script:

../configure.sh

then we run the build itself:

ninja

If the build goes without errors, you can proceed to installing the compiled version:

env DESTDIR=/opt/app/swift-sdk ninja install-compiler install-autolink-driver install-stdlib install-sdk-overlay

As a result, the directory /opt/app/swift-sdk will have a structure similar to the binary build of the Swift environment, supplied officially with site Apple.

Launch

Unfortunately, the Swift compiler has a problem finding its libraries in the FreeBSD environment, it needs a little help with LD_LIBRARY_PATH:

LD_LIBRARY_PATH=/opt/app/swift-sdk/usr/local/lib/swift/freebsd

After this, compilation should work normally:

Total

Outside the Apple ecosystem, Swift looks and works frankly not very well – toolchain builds are huge due to the LLVM added inside, and language support beyond syntax highlighting is actually only available in the official XCode, which runs on MacOS.

The current state of the open source version of Swift resembles Rust or Haskell:

rapid development with no regard for backward compatibility and a constantly breaking environment

Time will tell what this will lead to, but for now I would not recommend doing any serious development in Swift outside the Apple ecosystem – too many of the most incredible errors are generated by using LLVM “under the hood”.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *