| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 1 | # The whole toolchain, as one closure. |
| 2 | # |
| 3 | # This replaces buck2 and DotSlash together, and for the same reason each of |
| 4 | # them was here: a checkout should build on a machine with nothing installed. |
| 5 | # DotSlash pinned five tools by digest and fetched them on first use; buck2 knew |
| 6 | # how to run them. Nix pins the compiler, the C toolchain, the system libraries |
| 7 | # *and* the crate graph in one lockfile, so the pinning and the running stop |
| 8 | # being two problems. |
| 9 | # |
| 10 | # nix develop # the edit loop: cargo, just, everything below |
| 11 | # nix build # all three desktop objects into result/lib |
| 12 | # nix build .#android # the two device objects, cross-compiled |
| 13 | # nix flake check # fmt, clippy, tests |
| 14 | # |
| Publish the shared objects instead of dropping them b6001e6 nandi 11d ago | 15 | # Every push to main publishes the same two outputs to the GitLab package |
| 16 | # registry as tarballs rooted at lib/ and include/, so another flake can link |
| 17 | # against them without building this one: |
| 18 | # |
| 19 | # inputs.jolt-native-libs = { |
| 20 | # url = "https://gitlab.example/api/v4/projects/<id>/packages/generic/jolt-native/<sha>/android-arm64-v8a.tar.gz"; |
| 21 | # flake = false; # a plain tarball, not a flake — nix unpacks it as-is |
| 22 | # }; |
| 23 | # # then: ${jolt-native-libs}/lib/arm64-v8a/libjoltmoq.so |
| 24 | # |
| 25 | # The URL carries a commit sha and flake.lock pins the unpacked tree's narHash, |
| 26 | # so the input is immutable from both ends; moving it is an edit plus a lock |
| 27 | # update. There is deliberately no `latest` URL — a moving target under a |
| The window nobody could unpack 121e5f1 nandi 20h ago | 28 | # pinned hash is a lockfile that lies. The desktop tarballs are published the |
| 29 | # same way, and there are two of them because there are two kinds of consumer: |
| 30 | # |
| 31 | # x86_64-linux.tar.gz the store objects, RUNPATH into the |
| 32 | # *builder's* /nix/store. They resolve only on |
| 33 | # a machine holding those paths, so a nix |
| 34 | # consumer should take this repo as a flake |
| 35 | # input and build .#libs rather than unpack it. |
| 36 | # x86_64-linux-portable.tar.gz .#libsPortable — the same objects with their |
| 37 | # NEEDED closure beside them and RUNPATH |
| 38 | # $ORIGIN. Unpack it anywhere, point |
| 39 | # LD_LIBRARY_PATH at its lib/, and dlopen |
| 40 | # works with no nix on the machine. The GL |
| 41 | # driver is deliberately NOT in it: that one |
| 42 | # has to be the host's. |
| Publish the shared objects instead of dropping them b6001e6 nandi 11d ago | 43 | # |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 44 | # What went away with buck2: third-party/rust's reindeer-generated BUCK graph |
| 45 | # (Cargo.lock is the one dependency graph now), the RBE container and its GitLab |
| 46 | # job, and scripts/ entire — zcc, zxx, zig-include, the .dotslash manifests and |
| 47 | # the generator that restated them for buck. The zig sysroot in particular was |
| 48 | # only ever standing in for a system C toolchain; nix supplies a real one, which |
| 49 | # is also why cpal's `pipewire` feature no longer has to be off. |
| 50 | { |
| 51 | description = "jolt's native backends — glimmer/egui, a terminal painter, and a MoQ media plane, each behind a C ABI"; |
| 52 | |
| 53 | inputs = { |
| 54 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; |
| 55 | rust-overlay = { |
| 56 | url = "github:oxalica/rust-overlay"; |
| 57 | inputs.nixpkgs.follows = "nixpkgs"; |
| 58 | }; |
| 59 | crane.url = "github:ipetkov/crane"; |
| 60 | flake-utils.url = "github:numtide/flake-utils"; |
| 61 | }; |
| 62 | |
| 63 | outputs = { self, nixpkgs, rust-overlay, crane, flake-utils }: |
| 64 | flake-utils.lib.eachDefaultSystem (system: |
| 65 | let |
| 66 | pkgs = import nixpkgs { |
| 67 | inherit system; |
| 68 | overlays = [ (import rust-overlay) ]; |
| 69 | # The NDK is unfree, and only the android outputs pull it in. |
| 70 | config.allowUnfree = true; |
| 71 | config.android_sdk.accept_license = true; |
| 72 | }; |
| 73 | |
| 74 | inherit (pkgs) lib; |
| 75 | |
| 76 | # One toolchain for both directions. The Android std comes from the same |
| 77 | # rustc rather than a second pinned tarball, which is what |
| 78 | # scripts/rust-std-android.dotslash and the sysroot symlink farm in |
| 79 | # scripts/rustc existed to stitch together by hand. |
| 80 | rustToolchain = pkgs.rust-bin.stable.latest.default.override { |
| 81 | extensions = [ "rust-src" "clippy" "rustfmt" ]; |
| 82 | targets = [ "aarch64-linux-android" ]; |
| 83 | }; |
| 84 | |
| 85 | craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchain; |
| 86 | |
| 87 | # crane's own cleanCargoSource keeps .rs and the manifests and drops |
| 88 | # everything else — which here would drop the C headers each crate |
| 89 | # publishes and vidya-core's font and emoji assets, all of which are |
| 90 | # `include_bytes!`d or shipped beside the object. Keep them. |
| 91 | src = lib.cleanSourceWith { |
| 92 | src = ./.; |
| 93 | name = "jolt-native-source"; |
| 94 | filter = path: type: |
| 95 | let rel = lib.removePrefix (toString ./. + "/") (toString path); |
| 96 | in |
| 97 | (craneLib.filterCargoSources path type) |
| 98 | || lib.hasInfix "/include/" rel |
| Move iroh-live forward and let the vendored cpal go 789bb13 nandi 12d ago | 99 | || lib.hasInfix "/assets/" rel; |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 100 | }; |
| 101 | |
| 102 | # Built by a build script, linked into the objects, or opened by them at |
| 103 | # run time. Split out because the Android graph wants almost none of it. |
| 104 | desktopBuildInputs = with pkgs; [ |
| 105 | alsa-lib # cpal's ALSA host |
| 106 | pipewire # cpal's `pipewire` feature, via libspa-sys |
| 107 | libGL # glow/glutin |
| 108 | libxkbcommon # winit |
| 109 | wayland |
| 110 | libx11 |
| 111 | libxcursor |
| 112 | libxi |
| 113 | libxrandr |
| 114 | ]; |
| 115 | |
| 116 | nativeBuildInputs = with pkgs; [ |
| 117 | pkg-config |
| 118 | # aws-lc-sys (under rustls and moq-native) configures with cmake and |
| 119 | # generates its assembly with perl and go. |
| 120 | cmake |
| 121 | ninja |
| 122 | perl |
| 123 | go |
| 124 | # Sets LIBCLANG_PATH and the clang resource-dir include for every |
| 125 | # bindgen build script in the graph — v4l2r, libspa-sys, aws-lc-sys. |
| 126 | rustPlatform.bindgenHook |
| 127 | ]; |
| 128 | |
| 129 | # v4l2r's build script wants linux/videodev2.h. On a machine with kernel |
| 130 | # headers installed that is /usr/include; on one without it is nowhere, |
| 131 | # which is the whole reason the justfile used to reach into zig's |
| 132 | # bundled copies. nixpkgs has them as a package. |
| 133 | v4l2Env = { |
| 134 | V4L2R_VIDEODEV2_H_PATH = "${pkgs.linuxHeaders}/include"; |
| 135 | BINDGEN_EXTRA_CLANG_ARGS = "-I${pkgs.linuxHeaders}/include"; |
| 136 | }; |
| 137 | |
| 138 | commonArgs = { |
| 139 | inherit src; |
| 140 | strictDeps = true; |
| 141 | inherit nativeBuildInputs; |
| 142 | buildInputs = desktopBuildInputs; |
| 143 | } // v4l2Env; |
| 144 | |
| Build each object's dependencies, not every object's 2975a43 nandi 9d ago | 145 | # Every external crate, compiled once and shared by the objects that |
| 146 | # actually want it. |
| 147 | # |
| 148 | # This used to be ONE derivation for the whole workspace, and the |
| 149 | # sharing was the point: three objects, one dependency build. What it |
| 150 | # cost was invisible until a consumer wanted only some of them. |
| 151 | # jolt-moq brings moq-net, iroh, quinn, rustls and aws-lc-sys behind |
| 152 | # it -- 440 crates that nothing else here touches -- so a build of |
| 153 | # libvidya alone still paid for the media plane. A client that has |
| 154 | # stopped loading libjoltmoq paid for it too, which is the case that |
| 155 | # made this worth splitting. |
| 156 | # |
| 157 | # Split by CONSUMER rather than per package: vidya and tui share |
| 158 | # nearly everything, and giving them an artifact each would trade one |
| 159 | # kind of waste for another. |
| 160 | depsFor = { pname, packages }: |
| 161 | craneLib.buildDepsOnly (commonArgs // { |
| 162 | inherit pname; |
| 163 | version = "0.1.0"; |
| 164 | cargoExtraArgs = |
| 165 | "--locked " + lib.concatMapStringsSep " " (p: "-p " + p) packages; |
| 166 | }); |
| 167 | |
| 168 | uiArtifacts = depsFor { |
| 169 | pname = "jolt-native-ui-deps"; |
| 170 | packages = [ "vidya-ffi" "jolt-tui" ]; |
| 171 | }; |
| 172 | moqArtifacts = depsFor { |
| 173 | pname = "jolt-native-moq-deps"; |
| 174 | packages = [ "jolt-moq" ]; |
| 175 | }; |
| Build libjoltcosmic out of the flake, so frq can link it 4706c92 nandi 7d ago | 176 | # Its own artifact rather than a share of the UI one, for the reason |
| 177 | # crates/jolt-cosmic/Cargo.toml gives: libcosmic drags iced, wgpu, |
| 178 | # cosmic-text and zbus in behind it, and none of that belongs in the |
| 179 | # dependency build a terminal backend waits on. |
| 180 | cosmicArtifacts = depsFor { |
| 181 | pname = "jolt-native-cosmic-deps"; |
| 182 | packages = [ "jolt-cosmic" ]; |
| 183 | }; |
| Build each object's dependencies, not every object's 2975a43 nandi 9d ago | 184 | |
| 185 | # The whole workspace, for the clippy and test runs — those do build |
| 186 | # everything, and want the sharing this split gives up. |
| Move iroh-live forward and let the vendored cpal go 789bb13 nandi 12d ago | 187 | cargoArtifacts = craneLib.buildDepsOnly (commonArgs // { |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 188 | pname = "jolt-native-deps"; |
| 189 | version = "0.1.0"; |
| 190 | }); |
| 191 | |
| 192 | # cargo does not install a cdylib, so crane's default install phase — |
| 193 | # `cargo install`, which only knows about binaries — has nothing to do. |
| 194 | # Take the objects out of the target directory instead. |
| Run the formatter over the tree 3e8c6f0 nandi 13d ago | 195 | # `dir` is named separately because a crate's directory and its cargo |
| 196 | # package name are not the same thing here: vidya-ffi lives in |
| 197 | # crates/jolt-vidya. Nothing is silenced — a header that stops being |
| 198 | # there should fail the build rather than ship an object with no ABI |
| 199 | # beside it. |
| Build libjoltcosmic out of the flake, so frq can link it 4706c92 nandi 7d ago | 200 | # `headers` is false for a backend that exports no C API of its own. |
| 201 | # jolt-cosmic is one: the jolt side reaches it through jolt.ffi by |
| 202 | # symbol name, so there is no header for an embedder to include and no |
| 203 | # crates/jolt-cosmic/include to copy. The others keep the check that |
| 204 | # a vanished header fails the build. |
| 205 | soPackage = { pname, package, dir, soname, artifacts, headers ? true }: |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 206 | craneLib.buildPackage (commonArgs // { |
| Build each object's dependencies, not every object's 2975a43 nandi 9d ago | 207 | inherit pname; |
| 208 | cargoArtifacts = artifacts; |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 209 | version = "0.1.0"; |
| 210 | cargoExtraArgs = "--locked -p ${package}"; |
| 211 | doCheck = false; |
| 212 | installPhaseCommand = '' |
| Build libjoltcosmic out of the flake, so frq can link it 4706c92 nandi 7d ago | 213 | mkdir -p $out/lib |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 214 | cp target/release/${soname} $out/lib/ |
| Build libjoltcosmic out of the flake, so frq can link it 4706c92 nandi 7d ago | 215 | '' + lib.optionalString headers '' |
| 216 | mkdir -p $out/include |
| Run the formatter over the tree 3e8c6f0 nandi 13d ago | 217 | cp -r crates/${dir}/include/. $out/include/ |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 218 | ''; |
| 219 | }); |
| 220 | |
| 221 | libvidya = soPackage { |
| Build each object's dependencies, not every object's 2975a43 nandi 9d ago | 222 | artifacts = uiArtifacts; |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 223 | pname = "libvidya"; |
| 224 | package = "vidya-ffi"; |
| Run the formatter over the tree 3e8c6f0 nandi 13d ago | 225 | dir = "jolt-vidya"; |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 226 | soname = "libvidya.so"; |
| 227 | }; |
| 228 | libjolttui = soPackage { |
| Build each object's dependencies, not every object's 2975a43 nandi 9d ago | 229 | artifacts = uiArtifacts; |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 230 | pname = "libjolttui"; |
| 231 | package = "jolt-tui"; |
| Run the formatter over the tree 3e8c6f0 nandi 13d ago | 232 | dir = "jolt-tui"; |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 233 | soname = "libjolttui.so"; |
| 234 | }; |
| 235 | libjoltmoq = soPackage { |
| Build each object's dependencies, not every object's 2975a43 nandi 9d ago | 236 | artifacts = moqArtifacts; |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 237 | pname = "libjoltmoq"; |
| 238 | package = "jolt-moq"; |
| Run the formatter over the tree 3e8c6f0 nandi 13d ago | 239 | dir = "jolt-moq"; |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 240 | soname = "libjoltmoq.so"; |
| 241 | }; |
| Build libjoltcosmic out of the flake, so frq can link it 4706c92 nandi 7d ago | 242 | libjoltcosmic = soPackage { |
| 243 | artifacts = cosmicArtifacts; |
| 244 | pname = "libjoltcosmic"; |
| 245 | package = "jolt-cosmic"; |
| 246 | dir = "jolt-cosmic"; |
| 247 | soname = "libjoltcosmic.so"; |
| 248 | headers = false; |
| 249 | }; |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 250 | |
| 251 | # The phone. Same sources, same Cargo.lock, only the target |
| 252 | # configuration moves — which is what //:libs-android and its pair of |
| 253 | # configured_alias targets used to say. Build scripts and proc macros |
| 254 | # still compile for the host; cargo arranges that on its own. |
| 255 | androidApi = 28; |
| 256 | androidTarget = "aarch64-linux-android"; |
| 257 | |
| 258 | androidComposition = pkgs.androidenv.composeAndroidPackages { |
| 259 | includeNDK = true; |
| 260 | ndkVersions = [ "27.2.12479018" ]; |
| 261 | platformVersions = [ "${toString androidApi}" ]; |
| 262 | abiVersions = [ "arm64-v8a" ]; |
| 263 | }; |
| 264 | ndkRoot = "${androidComposition.androidsdk}/libexec/android-sdk/ndk-bundle"; |
| 265 | ndkBin = "${ndkRoot}/toolchains/llvm/prebuilt/linux-x86_64/bin"; |
| 266 | |
| 267 | # The NDK's clang is the linker rustc runs and the compiler cc-rs runs. |
| 268 | # scripts/android-cc existed only because -Clinker takes a plain string |
| 269 | # and so needed something with a stable name; a nix store path is one. |
| 270 | androidCC = "${ndkBin}/clang"; |
| 271 | |
| 272 | androidEnv = { |
| 273 | CARGO_BUILD_TARGET = androidTarget; |
| 274 | "CC_${builtins.replaceStrings ["-"] ["_"] androidTarget}" = androidCC; |
| 275 | "CXX_${builtins.replaceStrings ["-"] ["_"] androidTarget}" = "${ndkBin}/clang++"; |
| 276 | "AR_${builtins.replaceStrings ["-"] ["_"] androidTarget}" = "${ndkBin}/llvm-ar"; |
| 277 | CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER = androidCC; |
| 278 | # cc-rs and clang both need the API level; rustc's target triple does |
| 279 | # not carry one, so it is passed as a flag on every C compile and on |
| 280 | # the link. |
| 281 | "CFLAGS_${builtins.replaceStrings ["-"] ["_"] androidTarget}" = |
| 282 | "--target=${androidTarget}${toString androidApi}"; |
| 283 | "CXXFLAGS_${builtins.replaceStrings ["-"] ["_"] androidTarget}" = |
| 284 | "--target=${androidTarget}${toString androidApi}"; |
| 285 | CARGO_TARGET_AARCH64_LINUX_ANDROID_RUSTFLAGS = |
| 286 | "-Clink-arg=--target=${androidTarget}${toString androidApi}"; |
| 287 | ANDROID_NDK_HOME = ndkRoot; |
| 288 | ANDROID_NDK_ROOT = ndkRoot; |
| 289 | }; |
| 290 | |
| 291 | # None of the desktop libraries cross: the camera is Camera2 over JNI |
| 292 | # rather than V4L2, and the audio is cpal's AAudio host rather than |
| 293 | # PipeWire or ALSA. The bindgen hook still comes along for aws-lc-sys. |
| 294 | androidArgs = { |
| 295 | inherit src; |
| 296 | strictDeps = true; |
| 297 | nativeBuildInputs = nativeBuildInputs ++ [ androidComposition.androidsdk ]; |
| 298 | buildInputs = [ ]; |
| 299 | doCheck = false; |
| 300 | cargoExtraArgs = "--locked -p vidya-ffi -p jolt-moq"; |
| 301 | } // androidEnv; |
| 302 | |
| Move iroh-live forward and let the vendored cpal go 789bb13 nandi 12d ago | 303 | androidArtifacts = craneLib.buildDepsOnly (androidArgs // { |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 304 | pname = "jolt-native-android-deps"; |
| 305 | version = "0.1.0"; |
| 306 | }); |
| 307 | |
| 308 | libsAndroid = craneLib.buildPackage (androidArgs // { |
| 309 | pname = "jolt-native-android"; |
| 310 | version = "0.1.0"; |
| 311 | cargoArtifacts = androidArtifacts; |
| 312 | installPhaseCommand = '' |
| 313 | mkdir -p $out/lib/arm64-v8a |
| 314 | cp target/${androidTarget}/release/libvidya.so $out/lib/arm64-v8a/ |
| 315 | cp target/${androidTarget}/release/libjoltmoq.so $out/lib/arm64-v8a/ |
| Publish the shared objects instead of dropping them b6001e6 nandi 11d ago | 316 | # Both objects NEEDED it — aws-lc-sys and libspa-sys pull in the |
| 317 | # C++ runtime — and the NDK ships it as a shared library that |
| 318 | # nothing else provides on the device. It has to ride along in the |
| 319 | # same directory the APK packages, or dlopen fails at run time. |
| 320 | cp ${ndkBin}/../sysroot/usr/lib/${androidTarget}/libc++_shared.so \ |
| 321 | $out/lib/arm64-v8a/ |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 322 | ''; |
| 323 | }); |
| 324 | |
| 325 | # The directory a consumer points LD_LIBRARY_PATH at — the same shape |
| 326 | # `just build` leaves in target/release, and the same one //:libs used |
| 327 | # to stage into build/lib. |
| The window nobody could unpack 121e5f1 nandi 20h ago | 328 | # |
| 329 | # libjoltcosmic is in here now. It was left out while it was the new |
| 330 | # backend and nothing took this attribute for it, and what that cost |
| 331 | # was not local: `.#libs` is what CI stages and tars, so the window |
| 332 | # every desktop consumer actually paints with was the one object never |
| 333 | # published. frq takes libjolttui and libjoltcosmic by name out of |
| 334 | # this flake's packages; the tarball is what a consumer WITHOUT nix |
| 335 | # gets, and it had a retired backend in it instead. |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 336 | libs = pkgs.symlinkJoin { |
| 337 | name = "jolt-native-libs"; |
| The window nobody could unpack 121e5f1 nandi 20h ago | 338 | paths = [ libvidya libjolttui libjoltmoq libjoltcosmic ]; |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 339 | }; |
| The window nobody could unpack 121e5f1 nandi 20h ago | 340 | |
| 341 | # The same objects, for a machine with no /nix/store. |
| 342 | # |
| 343 | # `libs` is only usable under nix, and not by accident: crane links |
| 344 | # against the closure, so every object here NEEDs sonames that resolve |
| 345 | # through a RUNPATH naming store paths the consumer does not have. |
| 346 | # That is the right answer for a nix consumer and the whole problem |
| 347 | # for anyone else — a tarball of `libs` unpacked on Debian is a set of |
| 348 | # objects dlopen fails on, which is what the x86_64-linux tarball has |
| 349 | # quietly been. |
| 350 | # |
| 351 | # So: copy each object, walk its NEEDED closure, bring along every |
| 352 | # library that closure names, and set RUNPATH to $ORIGIN so the copies |
| 353 | # find each other wherever the tarball lands. |
| 354 | # |
| 355 | # Two kinds of library are deliberately NOT brought along, and the |
| 356 | # distinction is what keeps this honest rather than a second AppImage: |
| 357 | # |
| 358 | # the driver stack — libGL, libEGL, libvulkan, libdrm, libgbm — which |
| 359 | # must be the HOST's or the window does not open. This is the thing |
| 360 | # nixGL exists to paper over, and the reason it exists is that a nix |
| 361 | # closure carries its own Mesa. Not carrying one means not needing a |
| 362 | # nixGL. Nothing here NEEDs them anyway: wgpu and glutin open the |
| 363 | # driver with dlopen at run time, which is exactly the seam that lets |
| 364 | # the host's win. |
| 365 | # |
| 366 | # glibc and the loader, which are the host's for the usual reason: |
| 367 | # a newer ld.so can load an older program's libraries and not the |
| 368 | # reverse, so bundling ours would set the floor at nixpkgs' glibc |
| 369 | # rather than at the oldest thing we actually build against. |
| 370 | # |
| 371 | # libstdc++ IS bundled: it is the other way round — the C++ runtime |
| 372 | # nixpkgs links against is routinely newer than a stable distro's, and |
| 373 | # a missing GLIBCXX_3.4.3x is the failure this avoids. |
| 374 | portableDenyList = [ |
| 375 | "libGL" "libEGL" "libGLX" "libGLdispatch" "libOpenGL" |
| 376 | "libvulkan" "libdrm" "libgbm" |
| 377 | "libc" "libm" "libdl" "libpthread" "librt" "libresolv" "libutil" |
| 378 | "ld-linux-x86-64" "ld-linux-aarch64" |
| 379 | ]; |
| 380 | |
| 381 | libsPortable = pkgs.runCommand "jolt-native-libs-portable" |
| 382 | { |
| 383 | nativeBuildInputs = [ pkgs.patchelf ]; |
| 384 | # The fallback search path. The real one is each file's OWN |
| 385 | # RUNPATH, read below: crane records the closure the linker used, |
| 386 | # which is complete, where a path built from the flake's inputs is |
| 387 | # only the direct ones — it has libxcb because libxcb is a |
| 388 | # buildInput, and not libXau, which is libxcb's. |
| 389 | searchPath = lib.makeLibraryPath |
| 390 | ([ libvidya libjolttui libjoltmoq libjoltcosmic ] |
| 391 | ++ desktopBuildInputs |
| 392 | ++ [ pkgs.stdenv.cc.cc.lib ]); |
| 393 | deny = lib.concatStringsSep " " portableDenyList; |
| 394 | } |
| 395 | '' |
| 396 | set -eu |
| 397 | mkdir -p "$out/lib" "$out/include" |
| 398 | cp -r ${libs}/include/. "$out/include/" |
| 399 | for so in ${libs}/lib/*.so; do |
| 400 | install -m 0755 "$so" "$out/lib/" |
| 401 | done |
| 402 | |
| 403 | denied() { |
| 404 | # Match on the soname up to the first ".so", so libGL.so.1 and |
| 405 | # libGL.so are the same answer. |
| 406 | stem=''${1%%.so*} |
| 407 | for d in $deny; do [ "$stem" = "$d" ] && return 0; done |
| 408 | return 1 |
| 409 | } |
| 410 | |
| 411 | # Breadth-first over NEEDED, because a bundled library has NEEDED |
| 412 | # entries of its own — libspa pulls the C++ runtime in, and a pass |
| 413 | # that only looked at our four objects would leave it out. |
| 414 | pending=$(ls "$out/lib") |
| 415 | while [ -n "$pending" ]; do |
| 416 | next="" |
| 417 | for f in $pending; do |
| 418 | for need in $(patchelf --print-needed "$out/lib/$f"); do |
| 419 | [ -e "$out/lib/$need" ] && continue |
| 420 | denied "$need" && continue |
| 421 | found="" |
| 422 | # The file's own RUNPATH first: it is the closure this |
| 423 | # object was linked against, so the answer is in it unless |
| 424 | # the library is dlopened rather than NEEDED. |
| 425 | IFS=: read -ra dirs <<< "$(patchelf --print-rpath "$out/lib/$f"):$searchPath" |
| 426 | for d in "''${dirs[@]}"; do |
| 427 | if [ -e "$d/$need" ]; then found="$d/$need"; break; fi |
| 428 | done |
| 429 | if [ -z "$found" ]; then |
| 430 | echo "no $need on the search path" >&2 |
| 431 | exit 1 |
| 432 | fi |
| 433 | install -m 0755 -T "$found" "$out/lib/$need" |
| 434 | next="$next $need" |
| 435 | done |
| 436 | done |
| 437 | pending=$next |
| 438 | done |
| 439 | |
| 440 | # One RUNPATH for the lot, and $ORIGIN rather than a directory: |
| 441 | # the consumer decides where this unpacks, and only the loader |
| 442 | # knows where that turned out to be. |
| 443 | for f in "$out/lib"/*; do |
| 444 | patchelf --set-rpath '$ORIGIN' "$f" |
| 445 | done |
| 446 | |
| 447 | echo "bundled:" |
| 448 | ls -la "$out/lib" |
| 449 | ''; |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 450 | in |
| 451 | { |
| 452 | packages = { |
| 453 | default = libs; |
| 454 | android = libsAndroid; |
| The window nobody could unpack 121e5f1 nandi 20h ago | 455 | inherit libs libsPortable libvidya libjolttui libjoltmoq libjoltcosmic; |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 456 | }; |
| 457 | |
| Run the glimmer-gfx counter from the repo root 5416ab2 nandi 11d ago | 458 | # Just the X11 client library on LD_LIBRARY_PATH, for `jolt gfx-demo`. |
| 459 | # The default shell would do too, but it evaluates the rust toolchain |
| 460 | # and the crane graph to hand a Clojure program one dlopen target. |
| 461 | devShells.gfx = pkgs.mkShell { |
| 462 | LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.libx11 ]; |
| 463 | }; |
| 464 | |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 465 | devShells.default = pkgs.mkShell ({ |
| 466 | packages = [ rustToolchain pkgs.just pkgs.sccache pkgs.cargo-nextest ] |
| 467 | ++ nativeBuildInputs |
| 468 | ++ desktopBuildInputs; |
| 469 | |
| 470 | # egui opens libGL and the Wayland/X11 client libraries with dlopen, |
| 471 | # so they have to be findable at run time and not only at link time. |
| 472 | LD_LIBRARY_PATH = lib.makeLibraryPath desktopBuildInputs; |
| 473 | } // v4l2Env); |
| 474 | |
| 475 | checks = { |
| The window nobody could unpack 121e5f1 nandi 20h ago | 476 | inherit libvidya libjolttui libjoltmoq libjoltcosmic; |
| Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago | 477 | |
| 478 | clippy = craneLib.cargoClippy (commonArgs // { |
| 479 | inherit cargoArtifacts; |
| 480 | pname = "jolt-native-clippy"; |
| 481 | cargoClippyExtraArgs = "--workspace --all-targets -- -D warnings"; |
| 482 | }); |
| 483 | |
| 484 | fmt = craneLib.cargoFmt { |
| 485 | inherit src; |
| 486 | pname = "jolt-native-fmt"; |
| 487 | }; |
| 488 | |
| 489 | test = craneLib.cargoTest (commonArgs // { |
| 490 | inherit cargoArtifacts; |
| 491 | pname = "jolt-native-test"; |
| 492 | cargoTestExtraArgs = "--workspace"; |
| 493 | }); |
| 494 | }; |
| 495 | |
| 496 | formatter = pkgs.nixpkgs-fmt; |
| 497 | }); |
| 498 | } |