import std/[os, sequtils, strutils, tables, unittest] import ../src/nimstatic const sampleIndex = """ C:Q1aaa= P:openssl-libs-static V:3.3.7-r1 A:x86_64 S:13040623 T:Toolkit for Transport Layer Security (TLS) (static library) L:Apache-2.0 o:openssl C:Q1bbb= P:openssl-dev V:3.3.7-r1 A:x86_64 S:3000 T:TLS toolkit (development files) D:libcrypto3=3.3.7-r1 pkgconfig p:openssl3-dev=3.3.7-r1 pc:libssl=3.3.7 C:Q1ccc= P:libcrypto3 V:3.3.7-r1 A:x86_64 S:2000 T:crypto library D:so:libc.musl-x86_64.so.1 p:so:libcrypto.so.3=3 C:Q1ddd= P:musl V:1.2.5-r11 A:x86_64 S:400 T:the musl c library p:so:libc.musl-x86_64.so.1=1 C:Q1eee= P:pkgconf V:2.3.0-r0 A:x86_64 S:100 T:pkg-config compatible p:pkgconfig=2.3.0-r0 """ suite "index parsing": let idx = parseIndex(sampleIndex, "main") test "records and fields": check idx.packages.len == 5 let p = idx.packages["openssl-libs-static"] check p.version == "3.3.7-r1" check p.size == 13040623 check p.repo == "main" check p.description.endsWith("(static library)") test "provides are indexed": check idx.find("pkgconfig") == "pkgconf" check idx.find("so:libcrypto.so.3") == "libcrypto3" check idx.find("openssl3-dev") == "openssl-dev" check idx.find("nope") == "" test "version constraints are stripped": check stripConstraint("musl=1.2.5-r11") == "musl" check stripConstraint("so:libssl.so.3=3") == "so:libssl.so.3" check stripConstraint("cmd>=2") == "cmd" check stripConstraint("plain") == "plain" test "conflicts are recognized": check isConflict("!busybox") check not isConflict("busybox") suite "resolution": let idx = parseIndex(sampleIndex, "main") test "dependencies come before dependents": let order = idx.resolve(["openssl-dev"]) let names = block: var s: seq[string] for p in order: s.add p.name s check names[^1] == "openssl-dev" check "libcrypto3" in names check "musl" in names # reached through so:libc.musl-x86_64.so.1 check "pkgconf" in names # reached through the pkgconfig provide check names.find("libcrypto3") < names.find("openssl-dev") test "a package is visited once": let order = idx.resolve(["openssl-dev", "libcrypto3", "openssl-dev"]) var seen: CountTable[string] for p in order: seen.inc p.name for name, n in seen: check n == 1 test "--no-deps stops at the request": let order = idx.resolve(["openssl-dev"], withDeps = false) check order.len == 1 check order[0].name == "openssl-dev" test "an unsatisfiable request names its requester": expect KeyError: discard parseIndex("C:Q1\nP:lonely\nV:1\nD:ghost\n").resolve(["lonely"]) test "merge lets a later repo shadow an earlier one": var a = parseIndex("C:Q1\nP:foo\nV:1\n", "main") a.merge parseIndex("C:Q1\nP:foo\nV:2\n", "community") check a.packages["foo"].version == "2" check a.packages["foo"].repo == "community" suite "sysroot manifest": let root = getTempDir() / "nimstatic-test-root" removeDir root test "record then read back": record(root, [Pkg(name: "zlib-static", version: "1.3.2-r0", repo: "main")]) record(root, [Pkg(name: "openssl-libs-static", version: "3.3.7-r1", repo: "main")]) let entries = readManifest(root) check entries.len == 2 check installed(root, "zlib-static") check not installed(root, "absent") test "re-recording replaces rather than duplicates": record(root, [Pkg(name: "zlib-static", version: "1.3.3-r0", repo: "main")]) let entries = readManifest(root) check entries.len == 2 for e in entries: if e.name == "zlib-static": check e.version == "1.3.3-r0" test "static libs are found and sorted": createDir root / "usr" / "lib" for name in ["libz.a", "libcrypto.a"]: writeFile(root / "usr" / "lib" / name, "") let libs = staticLibs(root) check libs.len == 2 check libs[0].extractFilename == "libcrypto.a" removeDir root suite "flags": let root = getTempDir() / "nimstatic-test-flags" removeDir root createDir root / "usr" / "lib" test "plain sysroot": let f = nimFlags(root).join(" ") check ("--passC:-I" & root & "/usr/include") in f check "--passL:-static" in f check "dynlibOverride" notin f test "openssl archives switch on the static-TLS workarounds": for name in ["libssl.a", "libcrypto.a"]: writeFile(root / "usr" / "lib" / name, "") let f = nimFlags(root, overridesIn(root), ["z"]).join(" ") check "--dynlibOverride:ssl" in f check "--dynlibOverride:crypto" in f # OpenSSL 3 dropped the symbol Nim's wrapper still names check "-DSSL_get_peer_certificate=SSL_get1_peer_certificate" in f check "--passL:-lz" in f test "cc flags and pkg-config env": check ccFlags(root, ["ssl"]) == @["-I" & root & "/usr/include", "-L" & root & "/usr/lib", "-static", "-lssl"] check pkgConfigEnv(root)[0] == "PKG_CONFIG_SYSROOT_DIR=" & root test "an override with no archive in the sysroot is dropped": # Asking Nim not to dlopen a library we cannot link would break the build. let f = nimFlags(root, ["ghost"]).join(" ") check "--dynlibOverride:ghost" notin f test "libssl links before libcrypto": let f = nimFlags(root, ["crypto", "ssl"]).join(" ") check f.find("libssl.a") < f.find("libcrypto.a") test "nimcfg carries the compiler and the flags": let cfg = nimCfg(root, "/tmp/zigcc") check "--cc:clang" in cfg check "--clang.exe:\"/tmp/zigcc\"" in cfg check "--passL:-static" in cfg test "zigcc script targets the right triple": let s = zigccScript("aarch64-linux-musl") check "zig cc -target aarch64-linux-musl" in s check "\"$@\"" in s removeDir root suite "remote": test "urls are built from mirror, branch, repo and arch": let r = initRemote(branch = "edge", arch = "aarch64") check r.repoUrl("community") == "https://dl-cdn.alpinelinux.org/alpine/edge/community/aarch64" check apkFile(Pkg(name: "zlib-static", version: "1.3.2-r0")) == "zlib-static-1.3.2-r0.apk" suite "detection": test "sonames reduce to library names": check libFromSoname("libssl.so.3") == "ssl" check libFromSoname("libpcre2-8.so.0") == "pcre2-8" check libFromSoname("libcrypto.so(.3|.1.1|.10|)") == "crypto" check libFromSoname("notalib") == "" check libFromSoname("libnoso") == "" test "link libraries come off the link command, once each": let cmd = "gcc -o app a.o b.o -pthread -lm -lm -lrt -lpcre -ldl" check linkLibs(cmd) == @["m", "rt", "pcre", "dl"] test "dynlib candidates are found in generated C": let c = """ static char* sslCandidates[] = {"libssl.so.3", "libssl.so.1.1"}; static char* cryptoCandidates[] = {"libcrypto.so(.3|.1.1|)"}; const char* unrelated = "libera chat"; """ check dynlibsIn(c) == @["ssl", "crypto"] test "inspect maps libraries to packages and flags the rest": let cache = getTempDir() / "nimstatic-test-cache" removeDir cache createDir cache writeFile(cache / "app.json", """{"linkcmd": "gcc -o app a.o -lm -lsqlite3"}""") writeFile(cache / "app.c", """char* c[] = {"libssl.so.3", "libmystery.so.1"};""") let d = inspect(cache) # libc's own are never requested check not d.needs.anyIt(it.lib == "m") check d.needs.anyIt(it.lib == "sqlite3" and not it.dynlib) check d.needs.anyIt(it.lib == "ssl" and it.dynlib) check "openssl-libs-static" in packages(d) check "sqlite-static" in packages(d) check d.unmapped == @["mystery"] # only dlopen'd libraries need the override check dynlibOverrides(d) == @["ssl"] removeDir cache test "--map overrides the built-in table": let cache = getTempDir() / "nimstatic-test-cache2" removeDir cache createDir cache writeFile(cache / "app.json", """{"linkcmd": "gcc -o app a.o"}""") writeFile(cache / "app.c", """char* c[] = {"libmystery.so.1"};""") let d = inspect(cache, {"mystery": "mystery-static"}.toTable) check packages(d) == @["mystery-static"] check d.unmapped.len == 0 removeDir cache