Skip to content

Commit f4519da

Browse files
authored
Add a Getting Started document for the Static Linux SDK. (#686)
* Add a Getting Started document for the Static Linux SDK. First pass at a Getting Started document for the Static Linux SDK. rdar://129217209 * Add a link to the Articles section * Fixed a missing line, and use console everywhere. * Added a note about versions * Add SDK URL * Updated after review comments
1 parent a9fb0da commit f4519da

File tree

2 files changed

+268
-0
lines changed

2 files changed

+268
-0
lines changed

_data/documentation.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@
5858
url: /documentation/concurrency/
5959
description: |
6060
Prepare for Swift 6 by enabling complete concurrency checking in your SwiftPM packages, Xcode projects, and CI scripts.
61+
- title: Getting Started with the Static Linux SDK
62+
url: /documentation/articles/static-linux-getting-started.html
63+
description: |
64+
Learn how to get started building binaries for Linux with no system dependencies (not even the Swift runtime or C library).
65+
Even better, you can do this from any system with a Swift toolchain, allowing you to develop on macOS or Windows and easily deploy to Linux when you go to production.
6166
#----------------------------------------------------
6267
- header: Contributing
6368
pages:
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
---
2+
layout: page
3+
date: 2024-06-04 12:00:00
4+
title: Getting Started with the Static Linux SDK
5+
author: [al45tair]
6+
---
7+
8+
It's well known that Swift can be used to build software for Apple
9+
platforms such as macOS or iOS, but Swift is also supported on other
10+
platforms, including Linux and Windows.
11+
12+
Building for Linux is especially interesting because, historically,
13+
Linux programs written in Swift needed to ensure that a copy of the
14+
Swift runtime---and all of its dependencies---was installed on the
15+
target system. Additionally, a program built for a particular
16+
distribution, or even a particular major version of a particular
17+
distribution, would not necessarily run on any other distribution or
18+
in some cases even on a different major version of the same
19+
distribution.
20+
21+
The Swift Static Linux SDK solves both of these problems by allowing
22+
you to build your program as a _fully statically linked_ executable,
23+
with no external dependencies at all (not even the C library), which
24+
means that it will run on _any_ Linux distribution as the only thing
25+
it depends on is the Linux system call interface.
26+
27+
Additionally, the Static Linux SDK can be used from any platform
28+
supported by the Swift compiler and package manager; this means that
29+
you can develop and test your program on macOS before building and
30+
deploying it to a Linux-based server, whether running locally or
31+
somewhere in the cloud.
32+
33+
### Static vs Dynamic Linking
34+
35+
_Linking_ is the process of taking different pieces of a computer
36+
program and wiring up any references between those pieces. For
37+
_static_ linking, generally speaking those pieces are _object files_,
38+
or _static libraries_ (which are really just collections of object
39+
files).
40+
41+
For _dynamic_ linking, the pieces are _executables_ and _dynamic
42+
libraries_ (aka dylibs, shared objects, or DLLs).
43+
44+
There are two key differences between dynamic and static linking:
45+
46+
* The time at which linking takes place. Static linking happens when
47+
you build your program; dynamic linking happens at runtime.
48+
49+
* The fact that a static library (or _archive_) is really a collection
50+
of individual object files, whereas a dynamic library is monolithic.
51+
52+
The latter is important because traditionally, the static linker will
53+
include every object explicitly listed on its command line, but it
54+
will _only_ include an object from a static library if doing so lets
55+
it resolve an unresolved symbolic reference. If you statically link
56+
against a library that you do not actually use, a traditional static
57+
linker will completely discard that library and not include any code
58+
from it in your final binary.
59+
60+
In practice, things can be more complicated---the static linker may
61+
actually work on the basis of individual _sections_ or _atoms_ from
62+
your object files, so it may in fact be able to discard individual
63+
functions or pieces of data rather than just whole objects.
64+
65+
### Pros and Cons of Static Linking
66+
67+
Pros of static linking:
68+
69+
* No runtime overhead.
70+
71+
* Only include code from libraries that is actually needed.
72+
73+
* No need for separately installed dynamic libraries.
74+
75+
* No versioning issues at runtime.
76+
77+
Cons of static linking:
78+
79+
* Programs cannot share code (higher overall memory usage).
80+
81+
* No way to update dependencies without rebuilding program.
82+
83+
* Larger executables (though this can be offset by not having to
84+
install separate dynamic libraries).
85+
86+
On Linux in particular, it's also possible to use static linking to
87+
completely eliminate dependencies on system libraries supplied by the
88+
distribution, resulting in executables that work on any distribution
89+
and can be installed by simply copying.
90+
91+
### Installing the SDK
92+
93+
Before you start, it's important to note:
94+
95+
* You will need to [install an Open Source toolchain from
96+
swift.org](https://www.swift.org/install/).
97+
98+
* You cannot use the toolchain provided with Xcode to build programs
99+
using the SDK.
100+
101+
* If you are using macOS, you will also need to ensure that you use
102+
the Swift compiler from this toolchain by [following the
103+
instructions
104+
here](https://www.swift.org/install/macos/#installation-via-swiftorg-package-installer).
105+
106+
* The toolchain must match the version of the Static Linux SDK that
107+
you install. The Static Linux SDK includes the corresponding Swift
108+
version in its filename to help identify the correct version of the
109+
SDK.
110+
111+
Once that is out of the way, actually installing the Static Linux SDK
112+
is easy; at a prompt, enter
113+
114+
```console
115+
$ swift sdk install <URL-or-filename-here>
116+
```
117+
118+
giving the URL or filename at which the SDK can be found.
119+
120+
For instance, assuming you have installed the
121+
`swift-6.0-DEVELOPMENT-SNAPSHOT-2024-06-06-a` toolchain, you would
122+
need to enter
123+
124+
```console
125+
$ 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
126+
```
127+
128+
to install the corresponding Static Linux SDK.
129+
130+
Swift will download and install the SDK on your system. You can get a
131+
list of installed SDKs with
132+
133+
```console
134+
$ swift sdk list
135+
```
136+
137+
and it's also possible to remove them using
138+
139+
```console
140+
$ swift sdk remove <name-of-SDK>
141+
```
142+
143+
### Your first statically linked Linux program
144+
145+
First, create a directory to hold your code:
146+
147+
```console
148+
$ mkdir hello
149+
$ cd hello
150+
```
151+
152+
Next, ask Swift to create a new program package for you:
153+
154+
```console
155+
$ swift package init --type executable
156+
```
157+
158+
You can build and run this locally:
159+
160+
```console
161+
$ swift build
162+
Building for debugging...
163+
[8/8] Applying hello
164+
Build complete! (15.29s)
165+
$ .build/debug/hello
166+
Hello, world!
167+
```
168+
169+
But with the Static Linux SDK installed, you can also build Linux
170+
binaries for x86-64 and ARM64 machines:
171+
172+
```console
173+
$ swift build --sdk x86_64-swift-linux-musl
174+
Building for debugging...
175+
[8/8] Linking hello
176+
Build complete! (2.04s)
177+
$ file .build/x86_64-swift-linux-musl/debug/hello
178+
.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
179+
```
180+
181+
```console
182+
$ swift build --sdk aarch64-swift-linux-musl
183+
Building for debugging...
184+
[8/8] Linking hello
185+
Build complete! (2.00s)
186+
$ file .build/aarch64-swift-linux-musl/debug/hello
187+
.build/aarch64-swift-linux-musl/debug/hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, with debug_info, not stripped
188+
```
189+
190+
These can be copied to an appropriate Linux-based system and executed:
191+
192+
```console
193+
$ scp .build/x86_64-swift-linux-musl/debug/hello linux:~/hello
194+
$ ssh linux ~/hello
195+
Hello, world!
196+
```
197+
198+
### What about package dependencies?
199+
200+
Swift packages that make use of Foundation or Swift NIO should just
201+
work. If you try to use a package that uses the C library, however,
202+
you may have a little work to do. Such packages often contain files
203+
with code like the following:
204+
205+
```swift
206+
#if os(macOS) || os(iOS)
207+
import Darwin
208+
#elseif os(Linux)
209+
import Glibc
210+
#elseif os(Windows)
211+
import ucrt
212+
#else
213+
#error(Unknown platform)
214+
#endif
215+
```
216+
217+
The Static Linux SDK does not use Glibc; instead, it is built on top
218+
of an alternative C library for Linux called
219+
[Musl](https://musl-libc.org). We chose this approach for two
220+
reasons:
221+
222+
1. Musl has excellent support for static linking.
223+
224+
2. Musl is permissively licensed, which makes it easy to distribute
225+
executables statically linked with it.
226+
227+
If you are using such a dependency, you will therefore need to adjust
228+
it to import the `Musl` module instead of the `Glibc` module:
229+
230+
```swift
231+
#if os(macOS) || os(iOS)
232+
import Darwin
233+
#elseif canImport(Glibc)
234+
import Glibc
235+
#elseif canImport(Musl)
236+
import Musl
237+
#elseif os(Windows)
238+
import ucrt
239+
#else
240+
#error(Unknown platform)
241+
#endif
242+
```
243+
244+
Occasionally there might be a difference between the way a C library
245+
type gets imported between Musl and Glibc; this sometimes happens if
246+
someone has added nullability annotations, or where a pointer type is
247+
using a forward-declared `struct` for which no actual definition is
248+
ever provided. Usually the problem will be obvious---a function
249+
argument or result will be `Optional` in one case and non-`Optional`
250+
in another, or a pointer type will be imported as `OpaquePointer`
251+
rather than `UnsafePointer<FOO>`.
252+
253+
If you do find yourself needing to make these kinds of adjustments,
254+
you can make your local copy of the package dependency editable by
255+
doing
256+
257+
```console
258+
$ swift package edit SomePackage
259+
```
260+
261+
and then editing the files in the `Packages` directory that appears in
262+
your program's source directory. You may wish to consider raising PRs
263+
upstream with any fixes you may have.

0 commit comments

Comments
 (0)