Swift 静态 Linux SDK
Swift Static Linux SDK

原始链接: https://www.swift.org/documentation/articles/static-linux-getting-started.html

Swift 编程语言可以在 Apple 的 macOS 和 iOS 系统之外使用,为 Linux 和 Windows 平台构建软件。 从历史上看,为 Linux 创建 Swift 应用程序需要确保兼容的 Swift 运行时安装及其所有依赖项。 这导致了各种发行版和版本之间的可移植性有限。 然而,Swift Static Linux SDK 的推出解决了这些挑战。 它使开发人员能够仅依赖于 Linux 系统调用接口构建独立的、静态链接的可执行文件。 通过采用这种方法,Swift 应用程序可以跨多个发行版无缝运行,而不会遇到依赖冲突。 此外,开发和测试过程可以在任何支持 Swift 编译器和包管理器的系统上独立进行。 总而言之,Swift Static Linux SDK 通过静态链接的可执行文件简化了 Linux 上 Swift 应用程序的创建和部署,消除了对特定发行版或其各自的 Swift 运行时安装的依赖。

用户讨论了由于特定的运行时要求而在各种 Linux 发行版上运行 Swift 程序所面临的挑战。 他们将其与 Node.js 或 Python 等语言进行了对比,这些语言中的包通常是在包含的虚拟环境中构建的。 用户承认 Swift 提供静态链接,但更喜欢共享库提供的灵活性,并对与静态链接二进制文件相关的潜在复杂性和安全风险表示担忧。 然而,该用户承认他们的理解可能有限,并分享了在 Linux 上使用 Rust、Go 和 Python 处理依赖关系和编译程序的经验。 最后,用户反思了与 Node.js、Rust 和 Go 等更成熟的语言相比,使用 Swift 的包管理时遇到的困难。
相关文章

原文

It’s well known that Swift can be used to build software for Apple platforms such as macOS or iOS, but Swift is also supported on other platforms, including Linux and Windows.

Building for Linux is especially interesting because, historically, Linux programs written in Swift needed to ensure that a copy of the Swift runtime—and all of its dependencies—was installed on the target system. Additionally, a program built for a particular distribution, or even a particular major version of a particular distribution, would not necessarily run on any other distribution or in some cases even on a different major version of the same distribution.

The Swift Static Linux SDK solves both of these problems by allowing you to build your program as a fully statically linked executable, with no external dependencies at all (not even the C library), which means that it will run on any Linux distribution as the only thing it depends on is the Linux system call interface.

Additionally, the Static Linux SDK can be used from any platform supported by the Swift compiler and package manager; this means that you can develop and test your program on macOS before building and deploying it to a Linux-based server, whether running locally or somewhere in the cloud.

Linking is the process of taking different pieces of a computer program and wiring up any references between those pieces. For static linking, generally speaking those pieces are object files, or static libraries (which are really just collections of object files).

For dynamic linking, the pieces are executables and dynamic libraries (aka dylibs, shared objects, or DLLs).

There are two key differences between dynamic and static linking:

  • The time at which linking takes place. Static linking happens when you build your program; dynamic linking happens at runtime.

  • The fact that a static library (or archive) is really a collection of individual object files, whereas a dynamic library is monolithic.

The latter is important because traditionally, the static linker will include every object explicitly listed on its command line, but it will only include an object from a static library if doing so lets it resolve an unresolved symbolic reference. If you statically link against a library that you do not actually use, a traditional static linker will completely discard that library and not include any code from it in your final binary.

In practice, things can be more complicated—the static linker may actually work on the basis of individual sections or atoms from your object files, so it may in fact be able to discard individual functions or pieces of data rather than just whole objects.

Pros and Cons of Static Linking

Pros of static linking:

  • No runtime overhead.

  • Only include code from libraries that is actually needed.

  • No need for separately installed dynamic libraries.

  • No versioning issues at runtime.

Cons of static linking:

  • Programs cannot share code (higher overall memory usage).

  • No way to update dependencies without rebuilding program.

  • Larger executables (though this can be offset by not having to install separate dynamic libraries).

On Linux in particular, it’s also possible to use static linking to completely eliminate dependencies on system libraries supplied by the distribution, resulting in executables that work on any distribution and can be installed by simply copying.

Before you start, it’s important to note:

  • You will need to install an Open Source toolchain from swift.org.

  • You cannot use the toolchain provided with Xcode to build programs using the SDK.

  • If you are using macOS, you will also need to ensure that you use the Swift compiler from this toolchain by following the instructions here.

  • The toolchain must match the version of the Static Linux SDK that you install. The Static Linux SDK includes the corresponding Swift version in its filename to help identify the correct version of the SDK.

Once that is out of the way, actually installing the Static Linux SDK is easy; at a prompt, enter

$ swift sdk install <URL-or-filename-here>

giving the URL or filename at which the SDK can be found.

For instance, assuming you have installed the swift-6.0-DEVELOPMENT-SNAPSHOT-2024-06-06-a toolchain, you would need to enter

$ swift sdk install https://download.swift.org/development/static-sdk/swift-DEVELOPMENT-SNAPSHOT-2024-06-06-a/swift-DEVELOPMENT-SNAPSHOT-2024-06-06-a_static-linux-0.0.1.artifactbundle.tar.gz

to install the corresponding Static Linux SDK.

Swift will download and install the SDK on your system. You can get a list of installed SDKs with

and it’s also possible to remove them using

$ swift sdk remove <name-of-SDK>

First, create a directory to hold your code:

Next, ask Swift to create a new program package for you:

$ swift package init --type executable

You can build and run this locally:

$ swift build
Building for debugging...
[8/8] Applying hello
Build complete! (15.29s)
$ .build/debug/hello
Hello, world!

But with the Static Linux SDK installed, you can also build Linux binaries for x86-64 and ARM64 machines:

$ swift build --swift-sdk x86_64-swift-linux-musl
Building for debugging...
[8/8] Linking hello
Build complete! (2.04s)
$ file .build/x86_64-swift-linux-musl/debug/hello
.build/x86_64-swift-linux-musl/debug/hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, with debug_info, not stripped
$ swift build --swift-sdk aarch64-swift-linux-musl
Building for debugging...
[8/8] Linking hello
Build complete! (2.00s)
$ file .build/aarch64-swift-linux-musl/debug/hello
.build/aarch64-swift-linux-musl/debug/hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, with debug_info, not stripped

These can be copied to an appropriate Linux-based system and executed:

$ scp .build/x86_64-swift-linux-musl/debug/hello linux:~/hello
$ ssh linux ~/hello
Hello, world!

Swift packages that make use of Foundation or Swift NIO should just work. If you try to use a package that uses the C library, however, you may have a little work to do. Such packages often contain files with code like the following:

#if os(macOS) || os(iOS)
import Darwin
#elseif os(Linux)
import Glibc
#elseif os(Windows)
import ucrt
#else
#error(Unknown platform)
#endif

The Static Linux SDK does not use Glibc; instead, it is built on top of an alternative C library for Linux called Musl. We chose this approach for two reasons:

  1. Musl has excellent support for static linking.

  2. Musl is permissively licensed, which makes it easy to distribute executables statically linked with it.

If you are using such a dependency, you will therefore need to adjust it to import the Musl module instead of the Glibc module:

#if os(macOS) || os(iOS)
import Darwin
#elseif canImport(Glibc)
import Glibc
#elseif canImport(Musl)
import Musl
#elseif os(Windows)
import ucrt
#else
#error(Unknown platform)
#endif

Occasionally there might be a difference between the way a C library type gets imported between Musl and Glibc; this sometimes happens if someone has added nullability annotations, or where a pointer type is using a forward-declared struct for which no actual definition is ever provided. Usually the problem will be obvious—a function argument or result will be Optional in one case and non-Optional in another, or a pointer type will be imported as OpaquePointer rather than UnsafePointer<FOO>.

If you do find yourself needing to make these kinds of adjustments, you can make your local copy of the package dependency editable by doing

$ swift package edit SomePackage

and then editing the files in the Packages directory that appears in your program’s source directory. You may wish to consider raising PRs upstream with any fixes you may have.

Contributed by

Alastair Houghton works on the Swift and Objective-C language runtimes at Apple.

联系我们 contact @ memedata.com