From d28638518cd960c7c424bf2d2c3b5aa9c069e58c Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Thu, 19 Feb 2026 14:40:53 -0800 Subject: [PATCH 1/5] WIP - migrating Building guide into DocC format --- .../ServerGuides.docc/Documentation.md | 7 +- .../Sources/ServerGuides.docc/building.md | 274 ++++++++++++++++++ 2 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 server-guides/Sources/ServerGuides.docc/building.md diff --git a/server-guides/Sources/ServerGuides.docc/Documentation.md b/server-guides/Sources/ServerGuides.docc/Documentation.md index 2cab231..8f21cb3 100644 --- a/server-guides/Sources/ServerGuides.docc/Documentation.md +++ b/server-guides/Sources/ServerGuides.docc/Documentation.md @@ -2,4 +2,9 @@ @Metadata { @DisplayName("Server Guides") -} \ No newline at end of file +} + +## Topics + +- + diff --git a/server-guides/Sources/ServerGuides.docc/building.md b/server-guides/Sources/ServerGuides.docc/building.md new file mode 100644 index 0000000..7b3a55e --- /dev/null +++ b/server-guides/Sources/ServerGuides.docc/building.md @@ -0,0 +1,274 @@ +# 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 + +The 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 +``` + +These 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, letting you quickly test changes. +However, this can come at a significant cost to runtime performance. Debug builds typically run slower 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 + +After selecting your build configuration, you can apply additional optimizations. +Beyond choosing debug or release mode, several compiler flags can fine-tune your builds for specific scenarios. + +### Preserve frame pointers + +The compiler can omit frame pointers to gain additional performance. +Frame pointers are data structures that enable accurate stack traces. +Without them, debugging production crashes becomes more difficult because stack traces are less reliable. +For production server applications, preserving frame pointers is usually worth the minimal performance cost. +The compiler doesn't omit frame pointers by default in release configurations, but you can be explicit to preserve them: + +```bash +swift build -c release -Xcc -fno-omit-frame-pointer +``` + +The `-Xcc` flag passes options to the C compiler. +Here, `-fno-omit-frame-pointer` [tells the compiler](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fomit-frame-pointer) to preserve frame pointers. +This ensures that debugging tools can produce accurate backtraces, which are critical when you need to diagnose a crash or profile performance. +The performance impact is typically negligible for server workloads, while the debugging benefits are substantial. + +### Enable cross-module optimization + +Swift 5.2 added 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. +Here, `-cross-module-optimization` tells the compiler to optimize code 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 makes frequent use of small functions across module boundaries, this can yield meaningful performance improvements. +However, results vary for every project because optimizations are specific to your code. +Always benchmark your specific workload with and without this flag before deploying to production. + +### Build specific products + +If your package defines multiple executables or library products, Swift Package Manager builds everything declared in the package by default. +You can build only what you need using the `--product` flag: + +```bash +swift build --product MyAPIServer +``` + +This is particularly useful in monorepo (single-repository) setups or packages with multiple deployment targets, because it avoids compiling tools or test utilities you don't need for a particular deployment. + +### Use package traits + +Beyond additional compiler flags, Swift 6.1 introduces another way to control what gets built. +Starting with Swift 6.1, packages can define traits, which enable or disable optional features at build time. + +With package traits, a package can define additional, optional code that you can enable for your service or library. +You can toggle experimental APIs, performance monitoring, or deployment settings without creating separate branches. +This can be much clearer than using preprocessor macros or toggling features using environment variables in package manifests. + +For details on defining traits, conditional dependencies, trait unification, and advanced use cases, see [Package Traits](https://docs.swift.org/swiftpm/documentation/packagemanagerdocs/packagetraits). + +## Find and review your build artifacts + +After compiling, locate your build artifacts. +Swift Package Manager places them in platform-specific directories. The location varies by build 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 +``` + +Typical paths include: + +- **Linux (x86_64):** `.build/x86_64-unknown-linux/debug` or `.build/x86_64-unknown-linux/release` +- **macOS (Intel):** `.build/x86_64-apple-macosx/debug` or `.build/x86_64-apple-macosx/release` +- **macOS (Apple silicon):** `.build/arm64-apple-macosx/debug` or `.build/arm64-apple-macosx/release` + +The `--show-bin-path` flag is particularly useful for deployment scripts, where you need to copy the build artifact to a specific location without hardcoding platform-specific paths. + +### Build services for other platforms + +Once you know where your build artifacts are located, you may need to target different 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 for a common development pattern where developers work on macOS and deploy to Linux servers. + +Many developers use Xcode for development but need to produce Linux artifacts for deployment. +Swift provides two main approaches for cross-platform building. + +#### Build with Linux containers + +On macOS, you can use [Container](https://github.com/apple/container) or the Docker CLI to create Linux build artifacts. +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 +``` + +This command mounts your current directory into the container and runs `swift build` inside a Linux environment. +The `swift:latest` container image provides this environment. +This produces Linux-compatible build artifacts. + +If you're on Apple silicon but need to target x86_64 Linux servers, specify the platform explicitly: + +```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 emulation using QEMU. +The `-e QEMU_CPU=max` environment variable turns on advanced CPU features in QEMU, including x86_64 support. + +Emulation does not guarantee all processor-specific extensions are available, but this setting enables the broadest feature set supported by your system. + +To build your code into a container, you typically use a container declaration, called a Dockerfile or Containerfile, which specifies all the steps used to assemble the container image that holds your build artifacts. +Container-based builds work particularly well in CI/CD pipelines and local development where you want to validate that your code builds cleanly on Linux. However, Docker containers can be slower than native builds, especially on Apple silicon where x86_64 containers run through emulation. + +For a more detailed example of creating a container declaration to build and package your application, see [Packaging Swift Server Applications](./packaging.md). + +#### Choose static or dynamic linking + +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, it also means you need to ensure the Swift runtime is installed on your deployment target. + +For deployment scenarios where you want more self-contained build artifacts, you can statically link the Swift standard library: + +```bash +swift build -c release --static-swift-stdlib +``` + +The resulting build artifacts still depend on dynamically linking to glibc, but have fewer dependency requirements on the target system. + +The resulting executables bundle the Swift runtime directly. +This creates self-contained artifacts with fewer system dependencies: + +| Aspect | Dynamic Linking | Static Linking | +|--------|----------------|----------------| +| **Build artifact size** | Smaller (runtime not included) | Larger (adds to binary size for stdlib) | +| **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 | +| **Best for** | Containerized deployments with Swift runtime | VMs or bare metal with unknown configurations | + +For containerized deployments, dynamic linking is usually preferable because the container already includes the Swift runtime. For deploying to VMs or bare metal where you don't control the system configuration, static linking can simplify operations significantly. + +#### Cross-compile with the Static Linux SDK + +If the performance overhead of Docker-based builds is a concern for your workflow, Swift 5.9 and later provide Static Linux SDKs that enable 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, they may be larger than dynamically linked equivalents. + +Cross-compilation runs significantly faster than Docker-based builds. +It runs natively on your Mac's architecture without emulation. +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. +Another trade-off is that you can use only the static libraries that are available in the SDK, and your code cannot use `dlopen` or other tools to dynamically load libraries that may be available on the target system. + +For most projects this distinction doesn't matter. +However, packages with complex C dependencies may behave differently when built natively on Linux versus cross-compiled. + + +### Inspect a binary + +If you're uncertain what platform a binary was built for, use the `file` command to inspect the binary and determine its platform target. +The following example inspects a Swift executable (`MyServer`). + +``` +file .build/debug/MyServer +``` + +The output when compiled, in debug configuration, on macOS with Apple silicon: + +``` +.build/debug/MyServer: Mach-O 64-bit executable arm64 +``` + +The output when compiled, in debug configuration, on Linux using 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 +``` + +The following is the output for the same executable compiled in debug configuration on macOS using the Container tool with x86_64 emulation (from the example above): + +``` +.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 +``` + +Finally, that same binary compiled with 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 +``` From 4b0b5609536aa68017b8e54aa5d4fc7857132725 Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Thu, 19 Feb 2026 15:41:56 -0800 Subject: [PATCH 2/5] edits and cleanup --- .../Sources/ServerGuides.docc/building.md | 155 +++++++++--------- 1 file changed, 75 insertions(+), 80 deletions(-) diff --git a/server-guides/Sources/ServerGuides.docc/building.md b/server-guides/Sources/ServerGuides.docc/building.md index 7b3a55e..4ce931b 100644 --- a/server-guides/Sources/ServerGuides.docc/building.md +++ b/server-guides/Sources/ServerGuides.docc/building.md @@ -1,6 +1,6 @@ # Building Swift Server Applications -Assemble your server applications using Swift Package Manager +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. @@ -8,7 +8,7 @@ You can build using the command line or through an integrated development enviro ## Choose a build configuration -The Swift Package Manager supports two distinct build configurations, each optimized for different stages of your development workflow. +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 @@ -19,9 +19,10 @@ When you run `swift build` without additional flags, Swift Package Manager creat swift build ``` -These debug builds include full debugging symbols and runtime safety checks, which are essential during active development. +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, letting you quickly test changes. -However, this can come at a significant cost to runtime performance. Debug builds typically run slower than their release counterparts. +However, skipping optimizations can come at a significant cost to runtime performance. +Debug builds typically run slower than their release counterparts. ### Create release builds for production @@ -37,69 +38,69 @@ Release builds still include some debugging information for crash analysis, but ## Optimize your builds -After selecting your build configuration, you can apply additional optimizations. Beyond choosing debug or release mode, several compiler flags can fine-tune your builds for specific scenarios. ### Preserve frame pointers The compiler can omit frame pointers to gain additional performance. -Frame pointers are data structures that enable accurate stack traces. +Frame pointers are saved register values that record the call stack, enabling accurate stack traces. Without them, debugging production crashes becomes more difficult because stack traces are less reliable. For production server applications, preserving frame pointers is usually worth the minimal performance cost. -The compiler doesn't omit frame pointers by default in release configurations, but you can be explicit to preserve them: +The compiler preserves frame pointers by default in release configurations. To guarantee this behavior regardless of future toolchain changes, you can pass the flag explicitly: ```bash swift build -c release -Xcc -fno-omit-frame-pointer ``` The `-Xcc` flag passes options to the C compiler. -Here, `-fno-omit-frame-pointer` [tells the compiler](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fomit-frame-pointer) to preserve frame pointers. -This ensures that debugging tools can produce accurate backtraces, which are critical when you need to diagnose a crash or profile performance. +`-fno-omit-frame-pointer` [tells the compiler](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fomit-frame-pointer) to preserve frame pointers, +ensuring that debugging tools can produce accurate backtraces when you diagnose a crash or profile performance. The performance impact is typically negligible for server workloads, while the debugging benefits are substantial. ### Enable cross-module optimization -Swift 5.2 added cross-module optimization, which lets the compiler optimize code across module boundaries: +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. -Here, `-cross-module-optimization` tells the compiler to optimize code across module boundaries. +`-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 makes frequent use of small functions across module boundaries, this can yield meaningful performance improvements. -However, results vary for every project because optimizations are specific to your code. +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. ### Build specific products If your package defines multiple executables or library products, Swift Package Manager builds everything declared in the package by default. -You can build only what you need using the `--product` flag: +Build only what you need using the `--product` flag: ```bash swift build --product MyAPIServer ``` -This is particularly useful in monorepo (single-repository) setups or packages with multiple deployment targets, because it avoids compiling tools or test utilities you don't need for a particular deployment. +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. ### Use package traits -Beyond additional compiler flags, Swift 6.1 introduces another way to control what gets built. -Starting with Swift 6.1, packages can define traits, which enable or disable optional features at build time. +Swift 6.1 introduces another way to control what gets built. +Packages can define *traits*, which enable or disable optional features at build time. -With package traits, a package can define additional, optional code that you can enable for your service or library. +With package traits, a package can define additional, optional code that you enable for your service or library. You can toggle experimental APIs, performance monitoring, or deployment settings without creating separate branches. -This can be much clearer than using preprocessor macros or toggling features using environment variables in package manifests. +In package manifests, traits are clearer than preprocessor macros or environment variable–based feature flags. For details on defining traits, conditional dependencies, trait unification, and advanced use cases, see [Package Traits](https://docs.swift.org/swiftpm/documentation/packagemanagerdocs/packagetraits). -## Find and review your build artifacts +## Review your build artifacts After compiling, locate your build artifacts. -Swift Package Manager places them in platform-specific directories. The location varies by build platform and architecture: +Swift Package Manager places them in directories that vary by platform and architecture: ```bash # Show where debug build artifacts are located @@ -111,20 +112,19 @@ swift build --show-bin-path -c release Typical paths include: -- **Linux (x86_64):** `.build/x86_64-unknown-linux/debug` or `.build/x86_64-unknown-linux/release` -- **macOS (Intel):** `.build/x86_64-apple-macosx/debug` or `.build/x86_64-apple-macosx/release` -- **macOS (Apple silicon):** `.build/arm64-apple-macosx/debug` or `.build/arm64-apple-macosx/release` +- Linux (x86_64): `.build/x86_64-unknown-linux/debug` or `.build/x86_64-unknown-linux/release` +- macOS (Intel): `.build/x86_64-apple-macosx/debug` or `.build/x86_64-apple-macosx/release` +- macOS (Apple silicon): `.build/arm64-apple-macosx/debug` or `.build/arm64-apple-macosx/release` -The `--show-bin-path` flag is particularly useful for deployment scripts, where you need to copy the build artifact to a specific location without hardcoding platform-specific paths. +The `--show-bin-path` flag is useful for deployment scripts where you need to copy the build artifact to a specific location without hardcoding platform-specific paths. ### Build services for other platforms -Once you know where your build artifacts are located, you may need to target different platforms. -Swift build artifacts are both platform and architecture-specific. +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 for a common development pattern where developers work on macOS and deploy to Linux servers. +This creates a challenge when you work on macOS and deploy to Linux servers. -Many developers use Xcode for development but need to produce Linux artifacts for deployment. +You can use Xcode for development, but you need to produce Linux artifacts for deployment. Swift provides two main approaches for cross-platform building. #### Build with Linux containers @@ -146,11 +146,10 @@ docker run --rm -it \ swift:latest swift build ``` -This command mounts your current directory into the container and runs `swift build` inside a Linux environment. -The `swift:latest` container image provides this environment. -This produces Linux-compatible build artifacts. +These commands mount your current directory into the container and run `swift build` inside a Linux environment. +The `swift:latest` container image provides this environment and produces Linux-compatible build artifacts. -If you're on Apple silicon but need to target x86_64 Linux servers, specify the platform explicitly: +If you're on Apple silicon and need to target x86_64 Linux servers, specify the platform explicitly: ```bash # Build with Container @@ -168,44 +167,43 @@ docker run --rm -it \ swift:latest swift build ``` -The `--platform` flag runs the container with emulation using QEMU. -The `-e QEMU_CPU=max` environment variable turns on advanced CPU features in QEMU, including x86_64 support. +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. -Emulation does not guarantee all processor-specific extensions are available, but this setting enables the broadest feature set supported by your system. +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 containers can be slower than native builds, especially on Apple silicon where x86_64 containers run through emulation. -To build your code into a container, you typically use a container declaration, called a Dockerfile or Containerfile, which specifies all the steps used to assemble the container image that holds your build artifacts. -Container-based builds work particularly well in CI/CD pipelines and local development where you want to validate that your code builds cleanly on Linux. However, Docker containers can be slower than native builds, especially on Apple silicon where x86_64 containers run through emulation. - -For a more detailed example of creating a container declaration to build and package your application, see [Packaging Swift Server Applications](./packaging.md). +For a detailed example of creating a container declaration to build and package your application, see [Packaging Swift Server Applications](./packaging.md). #### Choose static or dynamic linking -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, it also means you need to ensure the Swift runtime is installed on your deployment target. +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, you can statically link the Swift standard library: +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 depend on dynamically linking to glibc, but have fewer dependency requirements on the target system. - -The resulting executables bundle the Swift runtime directly. -This creates self-contained artifacts with fewer system dependencies: +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 | +| Aspect | Dynamic linking | Static linking | |--------|----------------|----------------| -| **Build artifact size** | Smaller (runtime not included) | Larger (adds to binary size for stdlib) | -| **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 | -| **Best for** | Containerized deployments with Swift runtime | VMs or bare metal with unknown configurations | +| 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 | +| Best for | Containerized deployments with Swift runtime | VMs or bare metal with unknown configurations | -For containerized deployments, dynamic linking is usually preferable because the container already includes the Swift runtime. For deploying to VMs or bare metal where you don't control the system configuration, static linking can simplify operations significantly. +For containerized deployments, dynamic linking is usually preferable because the container already includes the Swift runtime. +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. #### Cross-compile with the Static Linux SDK -If the performance overhead of Docker-based builds is a concern for your workflow, Swift 5.9 and later provide Static Linux SDKs that enable cross-compilation directly from macOS to Linux without using a container: +If the performance overhead of Docker-based builds affects your workflow, Swift 5.9 and later provide Static Linux SDKs that enable cross-compilation directly from macOS to Linux without using a container: ```bash # Build for x86_64 Linux @@ -217,58 +215,55 @@ 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, they may be larger than dynamically linked equivalents. - -Cross-compilation runs significantly faster than Docker-based builds. -It runs natively on your Mac's architecture without emulation. -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. -Another trade-off is that you can use only the static libraries that are available in the SDK, and your code cannot use `dlopen` or other tools to dynamically load libraries that may be available on the target system. +However, the resulting executables are typically larger than dynamically linked equivalents. -For most projects this distinction doesn't matter. -However, packages with complex C dependencies may behave differently when built natively on Linux versus cross-compiled. +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. ### Inspect a binary -If you're uncertain what platform a binary was built for, use the `file` command to inspect the binary and determine its platform target. -The following example inspects a Swift executable (`MyServer`). +If you're uncertain what platform a binary was built for, use the `file` command to inspect it: ``` file .build/debug/MyServer ``` -The output when compiled, in debug configuration, on macOS with Apple silicon: +Output from a debug build on macOS with Apple silicon: ``` .build/debug/MyServer: Mach-O 64-bit executable arm64 ``` -The output when compiled, in debug configuration, on Linux using a container on Apple silicon: +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, +.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 ``` -The following is the output for the same executable compiled in debug configuration on macOS using the Container tool with x86_64 emulation (from the example above): +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, +.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 ``` -Finally, that same binary compiled with the static Linux SDK (`swift build --swift-sdk x86_64-swift-linux-musl`): +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, +.build/debug/MyServer: ELF 64-bit LSB executable, + x86-64, version 1 (SYSV), statically linked, + BuildID[sha1]=04ae4f872265b1e0d85ff821fd26fc102993b9f2, with debug_info, not stripped ``` From db3b01fc5693698152efea914c501b9592eb0510 Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Mon, 2 Mar 2026 08:12:01 -0600 Subject: [PATCH 3/5] remove section on frame pointers as no longer needed, added reference and pointer to VS Code for building projects --- .../Sources/ServerGuides.docc/building.md | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/server-guides/Sources/ServerGuides.docc/building.md b/server-guides/Sources/ServerGuides.docc/building.md index 4ce931b..c45d3f8 100644 --- a/server-guides/Sources/ServerGuides.docc/building.md +++ b/server-guides/Sources/ServerGuides.docc/building.md @@ -22,7 +22,7 @@ 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, letting you quickly test changes. However, skipping optimizations can come at a significant cost to runtime performance. -Debug builds typically run slower than their release counterparts. +Debug builds typically run more slowly than their release counterparts. ### Create release builds for production @@ -40,23 +40,6 @@ Release builds still include some debugging information for crash analysis, but Beyond choosing debug or release mode, several compiler flags can fine-tune your builds for specific scenarios. -### Preserve frame pointers - -The compiler can omit frame pointers to gain additional performance. -Frame pointers are saved register values that record the call stack, enabling accurate stack traces. -Without them, debugging production crashes becomes more difficult because stack traces are less reliable. -For production server applications, preserving frame pointers is usually worth the minimal performance cost. -The compiler preserves frame pointers by default in release configurations. To guarantee this behavior regardless of future toolchain changes, you can pass the flag explicitly: - -```bash -swift build -c release -Xcc -fno-omit-frame-pointer -``` - -The `-Xcc` flag passes options to the C compiler. -`-fno-omit-frame-pointer` [tells the compiler](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fomit-frame-pointer) to preserve frame pointers, -ensuring that debugging tools can produce accurate backtraces when you diagnose a crash or profile performance. -The performance impact is typically negligible for server workloads, while the debugging benefits are substantial. - ### Enable cross-module optimization Swift supports cross-module optimization, which lets the compiler optimize code across module boundaries: @@ -124,7 +107,7 @@ 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 you need to produce Linux artifacts for deployment. +You can use Xcode for development, but it can't produce Linux artifacts for deployment. Swift provides two main approaches for cross-platform building. #### Build with Linux containers @@ -147,7 +130,7 @@ docker run --rm -it \ ``` These commands mount your current directory into the container and run `swift build` inside a Linux environment. -The `swift:latest` container image provides this environment and produces Linux-compatible build artifacts. +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, specify the platform explicitly: @@ -198,7 +181,6 @@ These executables bundle the Swift runtime directly: | Version management | Must match runtime version on system | Each artifact includes its own runtime version | | Best for | Containerized deployments with Swift runtime | VMs or bare metal with unknown configurations | -For containerized deployments, dynamic linking is usually preferable because the container already includes the Swift runtime. 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. #### Cross-compile with the Static Linux SDK @@ -225,6 +207,12 @@ your code can't use `dlopen` or similar mechanisms to dynamically load libraries 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. +### 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: From 4a0e0ade6fd0a6ad0fa74e03068a2634b3ba182b Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Mon, 2 Mar 2026 08:30:46 -0600 Subject: [PATCH 4/5] updated section heading to be specific to standard library, removed incorrect reference in table about static vs. dynamic linking --- server-guides/Sources/ServerGuides.docc/building.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server-guides/Sources/ServerGuides.docc/building.md b/server-guides/Sources/ServerGuides.docc/building.md index c45d3f8..cc2d953 100644 --- a/server-guides/Sources/ServerGuides.docc/building.md +++ b/server-guides/Sources/ServerGuides.docc/building.md @@ -159,7 +159,7 @@ However, Docker containers can be slower than native builds, especially on Apple For a detailed example of creating a container declaration to build and package your application, see [Packaging Swift Server Applications](./packaging.md). -#### Choose static or dynamic linking +#### Choose static or dynamic linking for the standard library 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. @@ -179,7 +179,6 @@ These executables bundle the Swift runtime directly: | 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 | -| Best for | Containerized deployments with Swift runtime | VMs or bare metal with unknown configurations | 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. From d89b7371c2622ebcd9b57e943232c523273b92dc Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Mon, 2 Mar 2026 09:08:27 -0600 Subject: [PATCH 5/5] adding link and reference to Static Linux SDK installation on install page, and reference the getting started article --- server-guides/Sources/ServerGuides.docc/building.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server-guides/Sources/ServerGuides.docc/building.md b/server-guides/Sources/ServerGuides.docc/building.md index cc2d953..dac46ba 100644 --- a/server-guides/Sources/ServerGuides.docc/building.md +++ b/server-guides/Sources/ServerGuides.docc/building.md @@ -184,7 +184,9 @@ For deploying to VMs or bare metal where you don't control the system configurat #### Cross-compile with the Static Linux SDK -If the performance overhead of Docker-based builds affects your workflow, Swift 5.9 and later provide Static Linux SDKs that enable cross-compilation directly from macOS to Linux without using a container: +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 @@ -206,6 +208,8 @@ your code can't use `dlopen` or similar mechanisms to dynamically load libraries 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. + + ### 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.