-
Notifications
You must be signed in to change notification settings - Fork 1
Server guides: building #31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
d286385
WIP - migrating Building guide into DocC format
heckj 4b0b560
edits and cleanup
heckj db3b01f
remove section on frame pointers as no longer needed, added reference…
heckj 4a0e0ad
updated section heading to be specific to standard library, removed i…
heckj d89b737
adding link and reference to Static Linux SDK installation on install…
heckj 3720bbb
Update server-guides/Sources/ServerGuides.docc/building.md
heckj 2fbaea9
applying feedback edits
heckj File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,4 +2,9 @@ | |
|
|
||
| @Metadata { | ||
| @DisplayName("Server Guides") | ||
| } | ||
| } | ||
|
|
||
| ## Topics | ||
|
|
||
| - <doc:building> | ||
|
|
||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,246 @@ | ||
| # Building Swift Server Applications | ||
|
|
||
| Assemble your server applications using Swift Package Manager. | ||
|
|
||
| Use [Swift Package Manager](/documentation/package-manager/) to build server applications. | ||
| It provides a cross-platform foundation for building Swift code. | ||
| You can build using the command line or through an integrated development environment (IDE) such as Xcode or Visual Studio Code. | ||
|
|
||
| ## Choose a build configuration | ||
|
|
||
| Swift Package Manager supports two distinct build configurations, each optimized for different stages of your development workflow. | ||
| The configurations are `debug`, frequently used during development, and `release`, which you use when profiling or creating production artifacts. | ||
|
|
||
| ### Use debug builds during development | ||
|
|
||
| When you run `swift build` without additional flags, Swift Package Manager creates a debug build: | ||
|
|
||
| ```bash | ||
| swift build | ||
| ``` | ||
|
|
||
| Debug builds include full debugging symbols and runtime safety checks, which are essential during active development. | ||
| The compiler skips most optimizations to keep compilation times fast and preserve maximum debugging information. | ||
| This lets you quickly test changes and debug your app using lldb and breakpoints. | ||
| However, skipping optimizations can come at a significant cost to runtime performance. | ||
| Debug builds typically run more slowly than their release counterparts. | ||
|
|
||
| ### Create release builds for production | ||
|
|
||
| For production deployments, use the release configuration by adding the `-c release` flag: | ||
|
|
||
| ```bash | ||
| swift build -c release | ||
| ``` | ||
|
|
||
| The release configuration turns on all compiler optimizations. | ||
| The trade-off is longer compilation times because the optimizer performs extensive analysis and transformations. | ||
| Release builds still include some debugging information for crash analysis, but omit development-only checks and assertions. | ||
|
|
||
| ## Optimize your builds | ||
|
|
||
| Beyond choosing debug or release mode, several compiler flags can fine-tune your builds for specific scenarios. | ||
|
|
||
| ### Build specific products | ||
|
|
||
| If your package defines multiple executables or library products, Swift Package Manager builds everything declared in the package by default. | ||
| Build only what you need using the `--product` flag: | ||
|
|
||
| ```bash | ||
| swift build --product MyAPIServer | ||
| ``` | ||
|
|
||
| Building a specific product is useful in monorepo setups or packages with multiple deployment targets. | ||
| It avoids compiling tools or test utilities you don't need for a given deployment. | ||
|
|
||
| ### Enable cross-module optimization | ||
|
|
||
| Swift supports cross-module optimization, which lets the compiler optimize code across module boundaries: | ||
|
|
||
| ```bash | ||
| swift build -c release -Xswiftc -cross-module-optimization | ||
| ``` | ||
|
|
||
| The `-Xswiftc` flag passes options to the Swift compiler. | ||
| `-cross-module-optimization` tells the compiler to optimize across module boundaries. | ||
| By default, Swift optimizes each module in isolation. | ||
| Cross-module optimization removes this boundary, enabling techniques such as inlining function calls between modules. | ||
|
|
||
| For code that frequently calls small functions across module boundaries, this can yield meaningful performance improvements. | ||
| However, results vary by project because optimizations are specific to your code. | ||
| Always benchmark your specific workload with and without this flag before deploying to production. | ||
|
|
||
| ## Review your build artifacts | ||
|
|
||
| After compiling, locate your build artifacts. | ||
| Swift Package Manager places them in directories that vary by platform and architecture: | ||
|
|
||
| ```bash | ||
| # Show where debug build artifacts are located | ||
| swift build --show-bin-path | ||
|
|
||
| # Show where release build artifacts are located | ||
| swift build --show-bin-path -c release | ||
| ``` | ||
|
|
||
| The build products are written to the scratch path, which defaults to `.build`, but the specific location can vary based on platform or Swift compiler. | ||
| Use the `--show-bin-path` flag in deployment scripts to locate the build product without hardcoding platform-specific paths. | ||
|
|
||
| ### Build services for other platforms | ||
|
|
||
| Swift build artifacts are both platform- and architecture-specific. | ||
| Artifacts you create on macOS run only on macOS; those you create on Linux run only on Linux. | ||
| This creates a challenge when you work on macOS and deploy to Linux servers. | ||
|
|
||
| You can use Xcode for development, but it can't produce Linux artifacts for deployment. | ||
| Swift provides two main approaches for cross-platform building. | ||
|
heckj marked this conversation as resolved.
|
||
|
|
||
| #### Build with Linux containers | ||
|
|
||
| On macOS, you can use [Container](https://github.com/apple/container) or the Docker CLI to verify your project builds under Linux. | ||
| Apple publishes official Swift Docker images to [Docker Hub](https://hub.docker.com/_/swift), which provide complete Linux build environments. | ||
|
|
||
| To build your application using the latest Swift release image: | ||
|
|
||
| ```bash | ||
| # Build with Container | ||
| container run -c 2 -m 8g --rm -it \ | ||
| -v "$PWD:/code" -w /code \ | ||
| swift:latest swift build | ||
|
|
||
| # Build with Docker | ||
| docker run --rm -it \ | ||
| -v "$PWD:/code" -w /code \ | ||
| swift:latest swift build | ||
|
heckj marked this conversation as resolved.
|
||
| ``` | ||
|
|
||
| These commands mount your current directory as `/code` in the container, set it as the working directory, and run `swift build` inside the Linux environment. | ||
| The `swift:latest` container image provides this environment and `swift build` produces Linux-compatible build artifacts. | ||
|
|
||
| If you're on Apple silicon and need to target x86_64 Linux servers, you need to specify the target platform with the `--platform` option: | ||
|
|
||
| ```bash | ||
| # Build with Container | ||
| container run -c 2 -m 8g --rm -it \ | ||
| -v "$PWD:/code" -w /code \ | ||
| --platform linux/amd64 \ | ||
| -e QEMU_CPU=max \ | ||
| swift:latest swift build | ||
|
|
||
| # Build with Docker | ||
| docker run --rm -it \ | ||
| -v "$PWD:/code" -w /code \ | ||
| --platform linux/amd64 \ | ||
| -e QEMU_CPU=max \ | ||
| swift:latest swift build | ||
| ``` | ||
|
|
||
| The `--platform` flag runs the container with QEMU emulation. | ||
| The `-e QEMU_CPU=max` environment variable enables the maximum set of CPU features within the emulated environment, giving your code access to the broadest instruction set the emulation supports. | ||
|
|
||
| To build your code into a container, you typically use a container declaration — a Dockerfile or Containerfile — that specifies all the steps to assemble the container image holding your build artifacts. | ||
| Container-based builds work well in CI/CD pipelines and for validating that your code builds cleanly on Linux. | ||
| However, Docker container builds can be slower than native builds, especially on Apple silicon where x86_64 containers run through emulation. | ||
|
|
||
| For more information on packaging your application or service, read [Packaging Swift Server Applications](./packaging.md). | ||
|
|
||
| #### Choose static or dynamic linking for the standard library | ||
|
heckj marked this conversation as resolved.
|
||
|
|
||
| By default, Swift build artifacts link the standard library dynamically. | ||
| This keeps individual build artifacts smaller, and multiple programs can share a single copy of the Swift runtime. | ||
| However, dynamic linking requires the Swift runtime to be installed on your deployment target. | ||
|
|
||
| For deployment scenarios where you want more self-contained build artifacts, statically link the Swift standard library: | ||
|
|
||
| ```bash | ||
| swift build -c release --static-swift-stdlib | ||
| ``` | ||
|
|
||
| The resulting build artifacts still dynamically link to glibc, but have fewer other dependencies on the target system. | ||
| These executables bundle the Swift runtime directly: | ||
|
|
||
| | Aspect | Dynamic linking | Static linking | | ||
| |--------|----------------|----------------| | ||
| | Build artifact size | Smaller (runtime not included) | Larger (runtime included in binary) | | ||
| | Deployment complexity | Requires Swift runtime on target system | Self-contained, no runtime needed | | ||
| | Version management | Must match runtime version on system | Each artifact includes its own runtime version | | ||
|
|
||
| For deploying to VMs or bare metal where you don't control the system configuration, static linking removes the dependency on a pre-installed Swift runtime. | ||
|
|
||
| > Note: This technique doesn't apply on Apple platforms. | ||
|
|
||
| #### Cross-compile with the Static Linux SDK | ||
|
heckj marked this conversation as resolved.
|
||
|
|
||
| If the performance overhead of Docker-based builds affects your workflow, Swift 5.9 and later provide Static Linux SDKs. | ||
| Find the link to install the static Linux SDK on the [install page at Swift.org](https://www.swift.org/install/), and detailed instructions in the article [Getting Started with the Static Linux SDK](https://www.swift.org/documentation/articles/static-linux-getting-started.html). | ||
| The SDK enables cross-compilation directly from macOS to Linux without using a container: | ||
|
|
||
| ```bash | ||
| # Build for x86_64 Linux | ||
| swift build -c release --swift-sdk x86_64-swift-linux-musl | ||
|
|
||
| # Build for ARM64 Linux | ||
| swift build -c release --swift-sdk aarch64-swift-linux-musl | ||
| ``` | ||
|
|
||
| These SDK targets use musl libc (a lightweight C library) instead of glibc (the GNU C library) to produce statically linked build artifacts. | ||
| The resulting executables have minimal dependencies on the target Linux system, making them highly portable across Linux distributions. | ||
| However, the resulting executables are typically larger than dynamically linked equivalents. | ||
|
|
||
| Cross-compilation runs natively on your Mac's architecture without emulation, so it's faster than Docker-based builds. | ||
| The trade-off is build environment fidelity: you verify that your code cross-compiles to Linux, not that it builds on an actual Linux system. | ||
| Cross-compilation also limits you to the static libraries available in the SDK; | ||
| your code can't use `dlopen` or similar mechanisms to dynamically load libraries available on the target system. | ||
|
|
||
| For most projects, this distinction doesn't matter. | ||
| However, packages with complex C dependencies can behave differently when built natively on Linux versus cross-compiled. | ||
|
|
||
|
|
||
|
heckj marked this conversation as resolved.
|
||
| ### Build with VS Code using a Dev Container | ||
|
|
||
| Visual Studio Code supports the Dev Container feature that lets you open, build, and debug your project within a container running on your local machine. | ||
| The Dev Container builds your code in the Linux environment that the container provides. | ||
| For more information on using a Dev Container, read [Visual Studio Code Dev Containers](https://docs.swift.org/vscode/documentation/userdocs/remote-dev/). | ||
|
|
||
| ### Inspect a binary | ||
|
|
||
| If you're uncertain what platform a binary was built for, use the `file` command to inspect it: | ||
|
|
||
| ``` | ||
| file .build/debug/MyServer | ||
| ``` | ||
|
|
||
| Output from a debug build on macOS with Apple silicon: | ||
|
|
||
| ``` | ||
| .build/debug/MyServer: Mach-O 64-bit executable arm64 | ||
| ``` | ||
|
|
||
| Output from a debug build on Linux, built inside a container on Apple silicon: | ||
|
|
||
| ``` | ||
| .build/debug/MyServer: ELF 64-bit LSB pie executable, | ||
| ARM aarch64, version 1 (SYSV), dynamically linked, | ||
| interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, | ||
| BuildID[sha1]=ec68ac934b11eb7364fce53c95c42f5b83c3cb8d, | ||
| with debug_info, not stripped | ||
| ``` | ||
|
|
||
| Output from a debug build on macOS, built using the Container tool with x86_64 emulation: | ||
|
|
||
| ``` | ||
| .build/debug/MyServer: ELF 64-bit LSB pie executable, | ||
| x86-64, version 1 (SYSV), dynamically linked, | ||
| interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, | ||
| BuildID[sha1]=40357329617ac9629e934b94415ff4078681b45a, | ||
| with debug_info, not stripped | ||
| ``` | ||
|
|
||
| Output from a debug build using the static Linux SDK (`swift build --swift-sdk x86_64-swift-linux-musl`): | ||
|
|
||
| ``` | ||
| .build/debug/MyServer: ELF 64-bit LSB executable, | ||
| x86-64, version 1 (SYSV), statically linked, | ||
| BuildID[sha1]=04ae4f872265b1e0d85ff821fd26fc102993b9f2, | ||
| with debug_info, not stripped | ||
| ``` | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.