Search code examples
swiftalpine-linuxmusl

How can I compile a simple Swift cli program for Alpine?


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:

  • using swift run and swift build with --static-swift-stdlib (but that has no musl support?)
  • static linking with 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?


Solution

  • 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.