muslkit
musl-linked static libraries from Alpine, without Alpine.
Alpine builds everything against musl and ships *-static packages for most of
it. An .apk is just a tarball, and an APKINDEX is just a text file — so you
don't need apk, a chroot, a container or a distro to get libcrypto.a built
for musl. muslkit fetches the index, resolves a dependency closure, downloads
the packages and unpacks them into a sysroot directory you own.
Nothing is installed system-wide. Nothing needs root.
muslkit add openssl-libs-static
nim c -d:ssl $(muslkit nimflags) --cc:clang --clang.exe:./zigcc app.nim
Why
Getting a fully static Nim (or C, or Zig) binary with TLS on a glibc distro
means finding musl-built archives, and the usual answers are "install Alpine",
"run Docker" or "use Nix". Alpine's mirrors already serve exactly the files —
this just takes them.
Usage
muslkit add <pkg>... Download packages and unpack into the sysroot
muslkit list Show what the sysroot holds
muslkit libs Show the static libraries in the sysroot
muslkit search <text> Search the index (names and descriptions)
muslkit show <pkg> Index record for one package
muslkit nimflags [-l lib] Print nim flags for a static build
muslkit ccflags [-l lib] Print cc/clang flags
muslkit nimcfg [-o file] Write a nim.cfg fragment
muslkit zigcc [-o file] Write a `zig cc -target …-musl` wrapper script
muslkit env Shell exports (PKG_CONFIG_*, MUSLKIT_ROOT)
muslkit path Print the sysroot path
muslkit clean Remove the sysroot (cache is kept)
Options: -r/--root, -b/--branch (default v3.21, edge for rolling),
-a/--arch, -m/--mirror, --repo main,community, -l/--lib, --cc,
--no-deps, --refresh, -q/--quiet.
The sysroot defaults to $XDG_DATA_HOME/muslkit/sysroot and honors
MUSLKIT_ROOT. Downloads are cached under $XDG_CACHE_HOME/muslkit, so a
second add is offline and the index is re-fetched only once a day.
A full static build, start to finish
muslkit add openssl-libs-static zlib-static
muslkit zigcc # writes ./zigcc (needs zig on PATH)
nim c -d:release -d:ssl \
--cc:clang --clang.exe:./zigcc --clang.linkerexe:./zigcc \
$(muslkit nimflags) --passL:-s \
-o:app src/app.nim
muslkit nimflags emits more than include and library paths, because two
things bite every static Nim build with TLS:
-d:sslmakes Nimdlopenlibssl at runtime. A static binary cannot,
and dies at startup withcould not load: libcrypto.so(...)— even on code
paths that never touch the network. The fix is--dynlibOverride:ssl --dynlibOverride:cryptoplus the archives on the link line.- OpenSSL 3 removed
SSL_get_peer_certificate, which Nim's wrapper still
references, so the link fails on one undefined symbol. The fix is
-DSSL_get_peer_certificate=SSL_get1_peer_certificate.
Both are emitted automatically when libssl.a and libcrypto.a are present in
the sysroot. A static binary carries no CA trust store, so set SSL_CERT_FILE
to a bundle on the host that runs it.
Cross-compiling is the same command with --arch; muslkit add -a aarch64 openssl-libs-static and muslkit zigcc -a aarch64 line up.
Trust
Packages come over HTTPS from the mirror and are unpacked as-is. muslkit checks
the size recorded in the index but does not verify Alpine's RSA signatures —
apk's checksum field covers the package's control segment, not the file, so a
real check means implementing apk's signature format. Treat a sysroot as build
input, not as a trust root. If that matters for your use, pin a mirror you run.
Install
nimble install
or build in place with nim c -d:ssl -o:muslkit src/muslkit.nim. Requires
tar on PATH (an .apk is concatenated gzip streams, which GNU tar reads) and,
for the zigcc helper, zig.
Library
import muslkit
let remote = initRemote(branch = "edge")
let idx = remote.fetchIndex()
for pkg in idx.resolve(["openssl-libs-static"]):
echo pkg.name, " ", pkg.version, " ", remote.fetchPackage(pkg)
Modules: muslkit/index (APKINDEX parsing, provides and dependency
resolution), muslkit/repo (mirror, cache, download, unpack),
muslkit/sysroot (manifest, static-lib discovery), muslkit/flags (nim/cc/
pkg-config flag emission).
Tests
nimble test
The suite is offline — index parsing, resolution through so:/pkg: provides,
manifest round-trips and flag emission all run against fixtures.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
|