| dist: nimstatic 0.1.0 x86_64-linux 7e028da nandi 12h ago | 1 | import std/[os, sequtils, strutils, tables, unittest] |
| 2 | import ../src/nimstatic |
| 3 | |
| 4 | const sampleIndex = """ |
| 5 | C:Q1aaa= |
| 6 | P:openssl-libs-static |
| 7 | V:3.3.7-r1 |
| 8 | A:x86_64 |
| 9 | S:13040623 |
| 10 | T:Toolkit for Transport Layer Security (TLS) (static library) |
| 11 | L:Apache-2.0 |
| 12 | o:openssl |
| 13 | |
| 14 | C:Q1bbb= |
| 15 | P:openssl-dev |
| 16 | V:3.3.7-r1 |
| 17 | A:x86_64 |
| 18 | S:3000 |
| 19 | T:TLS toolkit (development files) |
| 20 | D:libcrypto3=3.3.7-r1 pkgconfig |
| 21 | p:openssl3-dev=3.3.7-r1 pc:libssl=3.3.7 |
| 22 | |
| 23 | C:Q1ccc= |
| 24 | P:libcrypto3 |
| 25 | V:3.3.7-r1 |
| 26 | A:x86_64 |
| 27 | S:2000 |
| 28 | T:crypto library |
| 29 | D:so:libc.musl-x86_64.so.1 |
| 30 | p:so:libcrypto.so.3=3 |
| 31 | |
| 32 | C:Q1ddd= |
| 33 | P:musl |
| 34 | V:1.2.5-r11 |
| 35 | A:x86_64 |
| 36 | S:400 |
| 37 | T:the musl c library |
| 38 | p:so:libc.musl-x86_64.so.1=1 |
| 39 | |
| 40 | C:Q1eee= |
| 41 | P:pkgconf |
| 42 | V:2.3.0-r0 |
| 43 | A:x86_64 |
| 44 | S:100 |
| 45 | T:pkg-config compatible |
| 46 | p:pkgconfig=2.3.0-r0 |
| 47 | """ |
| 48 | |
| 49 | suite "index parsing": |
| 50 | let idx = parseIndex(sampleIndex, "main") |
| 51 | |
| 52 | test "records and fields": |
| 53 | check idx.packages.len == 5 |
| 54 | let p = idx.packages["openssl-libs-static"] |
| 55 | check p.version == "3.3.7-r1" |
| 56 | check p.size == 13040623 |
| 57 | check p.repo == "main" |
| 58 | check p.description.endsWith("(static library)") |
| 59 | |
| 60 | test "provides are indexed": |
| 61 | check idx.find("pkgconfig") == "pkgconf" |
| 62 | check idx.find("so:libcrypto.so.3") == "libcrypto3" |
| 63 | check idx.find("openssl3-dev") == "openssl-dev" |
| 64 | check idx.find("nope") == "" |
| 65 | |
| 66 | test "version constraints are stripped": |
| 67 | check stripConstraint("musl=1.2.5-r11") == "musl" |
| 68 | check stripConstraint("so:libssl.so.3=3") == "so:libssl.so.3" |
| 69 | check stripConstraint("cmd>=2") == "cmd" |
| 70 | check stripConstraint("plain") == "plain" |
| 71 | |
| 72 | test "conflicts are recognized": |
| 73 | check isConflict("!busybox") |
| 74 | check not isConflict("busybox") |
| 75 | |
| 76 | suite "resolution": |
| 77 | let idx = parseIndex(sampleIndex, "main") |
| 78 | |
| 79 | test "dependencies come before dependents": |
| 80 | let order = idx.resolve(["openssl-dev"]) |
| 81 | let names = block: |
| 82 | var s: seq[string] |
| 83 | for p in order: s.add p.name |
| 84 | s |
| 85 | check names[^1] == "openssl-dev" |
| 86 | check "libcrypto3" in names |
| 87 | check "musl" in names # reached through so:libc.musl-x86_64.so.1 |
| 88 | check "pkgconf" in names # reached through the pkgconfig provide |
| 89 | check names.find("libcrypto3") < names.find("openssl-dev") |
| 90 | |
| 91 | test "a package is visited once": |
| 92 | let order = idx.resolve(["openssl-dev", "libcrypto3", "openssl-dev"]) |
| 93 | var seen: CountTable[string] |
| 94 | for p in order: seen.inc p.name |
| 95 | for name, n in seen: check n == 1 |
| 96 | |
| 97 | test "--no-deps stops at the request": |
| 98 | let order = idx.resolve(["openssl-dev"], withDeps = false) |
| 99 | check order.len == 1 |
| 100 | check order[0].name == "openssl-dev" |
| 101 | |
| 102 | test "an unsatisfiable request names its requester": |
| 103 | expect KeyError: |
| 104 | discard parseIndex("C:Q1\nP:lonely\nV:1\nD:ghost\n").resolve(["lonely"]) |
| 105 | |
| 106 | test "merge lets a later repo shadow an earlier one": |
| 107 | var a = parseIndex("C:Q1\nP:foo\nV:1\n", "main") |
| 108 | a.merge parseIndex("C:Q1\nP:foo\nV:2\n", "community") |
| 109 | check a.packages["foo"].version == "2" |
| 110 | check a.packages["foo"].repo == "community" |
| 111 | |
| 112 | suite "sysroot manifest": |
| 113 | let root = getTempDir() / "nimstatic-test-root" |
| 114 | removeDir root |
| 115 | |
| 116 | test "record then read back": |
| 117 | record(root, [Pkg(name: "zlib-static", version: "1.3.2-r0", repo: "main")]) |
| 118 | record(root, [Pkg(name: "openssl-libs-static", version: "3.3.7-r1", repo: "main")]) |
| 119 | let entries = readManifest(root) |
| 120 | check entries.len == 2 |
| 121 | check installed(root, "zlib-static") |
| 122 | check not installed(root, "absent") |
| 123 | |
| 124 | test "re-recording replaces rather than duplicates": |
| 125 | record(root, [Pkg(name: "zlib-static", version: "1.3.3-r0", repo: "main")]) |
| 126 | let entries = readManifest(root) |
| 127 | check entries.len == 2 |
| 128 | for e in entries: |
| 129 | if e.name == "zlib-static": check e.version == "1.3.3-r0" |
| 130 | |
| 131 | test "static libs are found and sorted": |
| 132 | createDir root / "usr" / "lib" |
| 133 | for name in ["libz.a", "libcrypto.a"]: |
| 134 | writeFile(root / "usr" / "lib" / name, "") |
| 135 | let libs = staticLibs(root) |
| 136 | check libs.len == 2 |
| 137 | check libs[0].extractFilename == "libcrypto.a" |
| 138 | |
| 139 | removeDir root |
| 140 | |
| 141 | suite "flags": |
| 142 | let root = getTempDir() / "nimstatic-test-flags" |
| 143 | removeDir root |
| 144 | createDir root / "usr" / "lib" |
| 145 | |
| 146 | test "plain sysroot": |
| 147 | let f = nimFlags(root).join(" ") |
| 148 | check ("--passC:-I" & root & "/usr/include") in f |
| 149 | check "--passL:-static" in f |
| 150 | check "dynlibOverride" notin f |
| 151 | |
| 152 | test "openssl archives switch on the static-TLS workarounds": |
| 153 | for name in ["libssl.a", "libcrypto.a"]: |
| 154 | writeFile(root / "usr" / "lib" / name, "") |
| 155 | let f = nimFlags(root, overridesIn(root), ["z"]).join(" ") |
| 156 | check "--dynlibOverride:ssl" in f |
| 157 | check "--dynlibOverride:crypto" in f |
| 158 | # OpenSSL 3 dropped the symbol Nim's wrapper still names |
| 159 | check "-DSSL_get_peer_certificate=SSL_get1_peer_certificate" in f |
| 160 | check "--passL:-lz" in f |
| 161 | |
| 162 | test "cc flags and pkg-config env": |
| 163 | check ccFlags(root, ["ssl"]) == @["-I" & root & "/usr/include", |
| 164 | "-L" & root & "/usr/lib", |
| 165 | "-static", "-lssl"] |
| 166 | check pkgConfigEnv(root)[0] == "PKG_CONFIG_SYSROOT_DIR=" & root |
| 167 | |
| 168 | test "an override with no archive in the sysroot is dropped": |
| 169 | # Asking Nim not to dlopen a library we cannot link would break the build. |
| 170 | let f = nimFlags(root, ["ghost"]).join(" ") |
| 171 | check "--dynlibOverride:ghost" notin f |
| 172 | |
| 173 | test "libssl links before libcrypto": |
| 174 | let f = nimFlags(root, ["crypto", "ssl"]).join(" ") |
| 175 | check f.find("libssl.a") < f.find("libcrypto.a") |
| 176 | |
| 177 | test "nimcfg carries the compiler and the flags": |
| 178 | let cfg = nimCfg(root, "/tmp/zigcc") |
| 179 | check "--cc:clang" in cfg |
| 180 | check "--clang.exe:\"/tmp/zigcc\"" in cfg |
| 181 | check "--passL:-static" in cfg |
| 182 | |
| 183 | test "zigcc script targets the right triple": |
| 184 | let s = zigccScript("aarch64-linux-musl") |
| 185 | check "zig cc -target aarch64-linux-musl" in s |
| 186 | check "\"$@\"" in s |
| 187 | |
| 188 | removeDir root |
| 189 | |
| 190 | suite "remote": |
| 191 | test "urls are built from mirror, branch, repo and arch": |
| 192 | let r = initRemote(branch = "edge", arch = "aarch64") |
| 193 | check r.repoUrl("community") == |
| 194 | "https://dl-cdn.alpinelinux.org/alpine/edge/community/aarch64" |
| 195 | check apkFile(Pkg(name: "zlib-static", version: "1.3.2-r0")) == |
| 196 | "zlib-static-1.3.2-r0.apk" |
| 197 | |
| 198 | suite "detection": |
| 199 | test "sonames reduce to library names": |
| 200 | check libFromSoname("libssl.so.3") == "ssl" |
| 201 | check libFromSoname("libpcre2-8.so.0") == "pcre2-8" |
| 202 | check libFromSoname("libcrypto.so(.3|.1.1|.10|)") == "crypto" |
| 203 | check libFromSoname("notalib") == "" |
| 204 | check libFromSoname("libnoso") == "" |
| 205 | |
| 206 | test "link libraries come off the link command, once each": |
| 207 | let cmd = "gcc -o app a.o b.o -pthread -lm -lm -lrt -lpcre -ldl" |
| 208 | check linkLibs(cmd) == @["m", "rt", "pcre", "dl"] |
| 209 | |
| 210 | test "dynlib candidates are found in generated C": |
| 211 | let c = """ |
| 212 | static char* sslCandidates[] = {"libssl.so.3", "libssl.so.1.1"}; |
| 213 | static char* cryptoCandidates[] = {"libcrypto.so(.3|.1.1|)"}; |
| 214 | const char* unrelated = "libera chat"; |
| 215 | """ |
| 216 | check dynlibsIn(c) == @["ssl", "crypto"] |
| 217 | |
| 218 | test "inspect maps libraries to packages and flags the rest": |
| 219 | let cache = getTempDir() / "nimstatic-test-cache" |
| 220 | removeDir cache |
| 221 | createDir cache |
| 222 | writeFile(cache / "app.json", """{"linkcmd": "gcc -o app a.o -lm -lsqlite3"}""") |
| 223 | writeFile(cache / "app.c", """char* c[] = {"libssl.so.3", "libmystery.so.1"};""") |
| 224 | let d = inspect(cache) |
| 225 | |
| 226 | # libc's own are never requested |
| 227 | check not d.needs.anyIt(it.lib == "m") |
| 228 | check d.needs.anyIt(it.lib == "sqlite3" and not it.dynlib) |
| 229 | check d.needs.anyIt(it.lib == "ssl" and it.dynlib) |
| 230 | check "openssl-libs-static" in packages(d) |
| 231 | check "sqlite-static" in packages(d) |
| 232 | check d.unmapped == @["mystery"] |
| 233 | # only dlopen'd libraries need the override |
| 234 | check dynlibOverrides(d) == @["ssl"] |
| 235 | removeDir cache |
| 236 | |
| 237 | test "--map overrides the built-in table": |
| 238 | let cache = getTempDir() / "nimstatic-test-cache2" |
| 239 | removeDir cache |
| 240 | createDir cache |
| 241 | writeFile(cache / "app.json", """{"linkcmd": "gcc -o app a.o"}""") |
| 242 | writeFile(cache / "app.c", """char* c[] = {"libmystery.so.1"};""") |
| 243 | let d = inspect(cache, {"mystery": "mystery-static"}.toTable) |
| 244 | check packages(d) == @["mystery-static"] |
| 245 | check d.unmapped.len == 0 |
| 246 | removeDir cache |