I have a simple cli written in Swift that I want to run on Alpine. I want the binary to be self-contained. How can I do that? I have tried:
swift run
and swift build
with --static-swift-stdlib
(but that has no musl support?)docker run -v "$PWD:/sources" -w /sources --platform linux/amd64 swift:latest swiftc -emit-executable -static-executable -O main.swift testit
, but then the executable testit
when running it on Alpine, says it is Unable to obtain Swift runtime path Aborted
.The code of the program:
// The Swift Programming Language
// https://docs.swift.org/swift-book
func getSecret() -> String {
let CharArr: [Character] = ["T", "h", "i", "s", " ", "a", " ", "s", "e", "c", "r", "e", "t"]
let newStr = String(CharArr)
return newStr
}
if(CommandLine.arguments.count == 2){
if (CommandLine.arguments[1] == "spoil"){
print(getSecret())
} else {
if (CommandLine.arguments[1] == getSecret()){
print("This is correct! Congrats!");
}else{
print("This is incorrect. Try again");
}
}
} else if (CommandLine.arguments.count==1) {
print("Welcome to the wrongsecrets Swift binary which hides a secret.");
print("Use args spoil or a string to guess the password.");
} else {
print("Too many arguments supplied.");
}
Can you help me please?
Update (2024-06): Swift 6 will support static Linux builds (which use musl). Instructions on (cross-compiling) static Linux binaries can be found on the Swift page (although the instructions use incorrect flags at the time of writing, but I reckon this will be fixed by the final release of Swift 6)
As you figured out in the meantime, Swift doesn't run on musl-based systems, and can't generate musl binaries.
While waiting for Alpine Linux support in Swift, the following 2 alternatives work for me:
Install a glibc-based distribution in a chroot, and set up the loader using symlinks, as described on the Alpine wiki article on running Glibc programs. When this is done, binaries that have been built with Swift (using --static-swift-stdlib
) on a glibc system run on Alpine.
Use --static-swift-stdlib -Xlinker --dynamic-linker=/usr/lib/my-package/ld-linux-x86-64.so.2 -Xlinker -rpath='$ORIGIN'/../lib/my-package
to let the resulting binary use its own private version of the ld
linker, and bundle the linker and all the dynamic library dependencies in /usr/lib/my-package/
. This allows you to create a distributable package that works on Alpine, without relying on anything special in the Alpine installation (since the binary uses its own private linker and version of its libraries). I use a script that does all this, which in turn is used in an Alpine package script to create the final packages.