nandi/jolt-nativepublic Fork 0
8d68cc1481d3dc46b723b97313bbb7cd714d3325
Commits
Clone
git clone https://git.rickub.com/nandi/jolt-native.git
git clone ssh://git@rickub.com/nandi/jolt-native.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

flake.nix · 279 lines · 11.3 KBNix Blame HistoryRaw
Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago1# 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#
15# What went away with buck2: third-party/rust's reindeer-generated BUCK graph
16# (Cargo.lock is the one dependency graph now), the RBE container and its GitLab
17# job, and scripts/ entire — zcc, zxx, zig-include, the .dotslash manifests and
18# the generator that restated them for buck. The zig sysroot in particular was
19# only ever standing in for a system C toolchain; nix supplies a real one, which
20# is also why cpal's `pipewire` feature no longer has to be off.
21{
22 description = "jolt's native backends glimmer/egui, a terminal painter, and a MoQ media plane, each behind a C ABI";
23
24 inputs = {
25 nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
26 rust-overlay = {
27 url = "github:oxalica/rust-overlay";
28 inputs.nixpkgs.follows = "nixpkgs";
29 };
30 crane.url = "github:ipetkov/crane";
31 flake-utils.url = "github:numtide/flake-utils";
32 };
33
34 outputs = { self, nixpkgs, rust-overlay, crane, flake-utils }:
35 flake-utils.lib.eachDefaultSystem (system:
36 let
37 pkgs = import nixpkgs {
38 inherit system;
39 overlays = [ (import rust-overlay) ];
40 # The NDK is unfree, and only the android outputs pull it in.
41 config.allowUnfree = true;
42 config.android_sdk.accept_license = true;
43 };
44
45 inherit (pkgs) lib;
46
47 # One toolchain for both directions. The Android std comes from the same
48 # rustc rather than a second pinned tarball, which is what
49 # scripts/rust-std-android.dotslash and the sysroot symlink farm in
50 # scripts/rustc existed to stitch together by hand.
51 rustToolchain = pkgs.rust-bin.stable.latest.default.override {
52 extensions = [ "rust-src" "clippy" "rustfmt" ];
53 targets = [ "aarch64-linux-android" ];
54 };
55
56 craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchain;
57
58 # crane's own cleanCargoSource keeps .rs and the manifests and drops
59 # everything else — which here would drop the C headers each crate
60 # publishes and vidya-core's font and emoji assets, all of which are
61 # `include_bytes!`d or shipped beside the object. Keep them.
62 src = lib.cleanSourceWith {
63 src = ./.;
64 name = "jolt-native-source";
65 filter = path: type:
66 let rel = lib.removePrefix (toString ./. + "/") (toString path);
67 in
68 (craneLib.filterCargoSources path type)
69 || lib.hasInfix "/include/" rel
Move iroh-live forward and let the vendored cpal go 789bb13 nandi 12d ago70 || lib.hasInfix "/assets/" rel;
Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago71 };
72
73 # Built by a build script, linked into the objects, or opened by them at
74 # run time. Split out because the Android graph wants almost none of it.
75 desktopBuildInputs = with pkgs; [
76 alsa-lib # cpal's ALSA host
77 pipewire # cpal's `pipewire` feature, via libspa-sys
78 libGL # glow/glutin
79 libxkbcommon # winit
80 wayland
81 libx11
82 libxcursor
83 libxi
84 libxrandr
85 ];
86
87 nativeBuildInputs = with pkgs; [
88 pkg-config
89 # aws-lc-sys (under rustls and moq-native) configures with cmake and
90 # generates its assembly with perl and go.
91 cmake
92 ninja
93 perl
94 go
95 # Sets LIBCLANG_PATH and the clang resource-dir include for every
96 # bindgen build script in the graph — v4l2r, libspa-sys, aws-lc-sys.
97 rustPlatform.bindgenHook
98 ];
99
100 # v4l2r's build script wants linux/videodev2.h. On a machine with kernel
101 # headers installed that is /usr/include; on one without it is nowhere,
102 # which is the whole reason the justfile used to reach into zig's
103 # bundled copies. nixpkgs has them as a package.
104 v4l2Env = {
105 V4L2R_VIDEODEV2_H_PATH = "${pkgs.linuxHeaders}/include";
106 BINDGEN_EXTRA_CLANG_ARGS = "-I${pkgs.linuxHeaders}/include";
107 };
108
109 commonArgs = {
110 inherit src;
111 strictDeps = true;
112 inherit nativeBuildInputs;
113 buildInputs = desktopBuildInputs;
114 } // v4l2Env;
115
116 # Every external crate, compiled once and shared by the three objects,
117 # the clippy run and the test run.
Move iroh-live forward and let the vendored cpal go 789bb13 nandi 12d ago118 cargoArtifacts = craneLib.buildDepsOnly (commonArgs // {
Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago119 pname = "jolt-native-deps";
120 version = "0.1.0";
121 });
122
123 # cargo does not install a cdylib, so crane's default install phase —
124 # `cargo install`, which only knows about binaries — has nothing to do.
125 # Take the objects out of the target directory instead.
Run the formatter over the tree 3e8c6f0 nandi 13d ago126 # `dir` is named separately because a crate's directory and its cargo
127 # package name are not the same thing here: vidya-ffi lives in
128 # crates/jolt-vidya. Nothing is silenced — a header that stops being
129 # there should fail the build rather than ship an object with no ABI
130 # beside it.
131 soPackage = { pname, package, dir, soname }:
Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago132 craneLib.buildPackage (commonArgs // {
133 inherit pname cargoArtifacts;
134 version = "0.1.0";
135 cargoExtraArgs = "--locked -p ${package}";
136 doCheck = false;
137 installPhaseCommand = ''
138 mkdir -p $out/lib $out/include
139 cp target/release/${soname} $out/lib/
Run the formatter over the tree 3e8c6f0 nandi 13d ago140 cp -r crates/${dir}/include/. $out/include/
Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago141 '';
142 });
143
144 libvidya = soPackage {
145 pname = "libvidya";
146 package = "vidya-ffi";
Run the formatter over the tree 3e8c6f0 nandi 13d ago147 dir = "jolt-vidya";
Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago148 soname = "libvidya.so";
149 };
150 libjolttui = soPackage {
151 pname = "libjolttui";
152 package = "jolt-tui";
Run the formatter over the tree 3e8c6f0 nandi 13d ago153 dir = "jolt-tui";
Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago154 soname = "libjolttui.so";
155 };
156 libjoltmoq = soPackage {
157 pname = "libjoltmoq";
158 package = "jolt-moq";
Run the formatter over the tree 3e8c6f0 nandi 13d ago159 dir = "jolt-moq";
Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago160 soname = "libjoltmoq.so";
161 };
162
163 # The phone. Same sources, same Cargo.lock, only the target
164 # configuration moves — which is what //:libs-android and its pair of
165 # configured_alias targets used to say. Build scripts and proc macros
166 # still compile for the host; cargo arranges that on its own.
167 androidApi = 28;
168 androidTarget = "aarch64-linux-android";
169
170 androidComposition = pkgs.androidenv.composeAndroidPackages {
171 includeNDK = true;
172 ndkVersions = [ "27.2.12479018" ];
173 platformVersions = [ "${toString androidApi}" ];
174 abiVersions = [ "arm64-v8a" ];
175 };
176 ndkRoot = "${androidComposition.androidsdk}/libexec/android-sdk/ndk-bundle";
177 ndkBin = "${ndkRoot}/toolchains/llvm/prebuilt/linux-x86_64/bin";
178
179 # The NDK's clang is the linker rustc runs and the compiler cc-rs runs.
180 # scripts/android-cc existed only because -Clinker takes a plain string
181 # and so needed something with a stable name; a nix store path is one.
182 androidCC = "${ndkBin}/clang";
183
184 androidEnv = {
185 CARGO_BUILD_TARGET = androidTarget;
186 "CC_${builtins.replaceStrings ["-"] ["_"] androidTarget}" = androidCC;
187 "CXX_${builtins.replaceStrings ["-"] ["_"] androidTarget}" = "${ndkBin}/clang++";
188 "AR_${builtins.replaceStrings ["-"] ["_"] androidTarget}" = "${ndkBin}/llvm-ar";
189 CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER = androidCC;
190 # cc-rs and clang both need the API level; rustc's target triple does
191 # not carry one, so it is passed as a flag on every C compile and on
192 # the link.
193 "CFLAGS_${builtins.replaceStrings ["-"] ["_"] androidTarget}" =
194 "--target=${androidTarget}${toString androidApi}";
195 "CXXFLAGS_${builtins.replaceStrings ["-"] ["_"] androidTarget}" =
196 "--target=${androidTarget}${toString androidApi}";
197 CARGO_TARGET_AARCH64_LINUX_ANDROID_RUSTFLAGS =
198 "-Clink-arg=--target=${androidTarget}${toString androidApi}";
199 ANDROID_NDK_HOME = ndkRoot;
200 ANDROID_NDK_ROOT = ndkRoot;
201 };
202
203 # None of the desktop libraries cross: the camera is Camera2 over JNI
204 # rather than V4L2, and the audio is cpal's AAudio host rather than
205 # PipeWire or ALSA. The bindgen hook still comes along for aws-lc-sys.
206 androidArgs = {
207 inherit src;
208 strictDeps = true;
209 nativeBuildInputs = nativeBuildInputs ++ [ androidComposition.androidsdk ];
210 buildInputs = [ ];
211 doCheck = false;
212 cargoExtraArgs = "--locked -p vidya-ffi -p jolt-moq";
213 } // androidEnv;
214
Move iroh-live forward and let the vendored cpal go 789bb13 nandi 12d ago215 androidArtifacts = craneLib.buildDepsOnly (androidArgs // {
Pin the toolchain with a flake, and let buck2 and DotSlash go 5ae197f nandi 13d ago216 pname = "jolt-native-android-deps";
217 version = "0.1.0";
218 });
219
220 libsAndroid = craneLib.buildPackage (androidArgs // {
221 pname = "jolt-native-android";
222 version = "0.1.0";
223 cargoArtifacts = androidArtifacts;
224 installPhaseCommand = ''
225 mkdir -p $out/lib/arm64-v8a
226 cp target/${androidTarget}/release/libvidya.so $out/lib/arm64-v8a/
227 cp target/${androidTarget}/release/libjoltmoq.so $out/lib/arm64-v8a/
228 '';
229 });
230
231 # The directory a consumer points LD_LIBRARY_PATH at — the same shape
232 # `just build` leaves in target/release, and the same one //:libs used
233 # to stage into build/lib.
234 libs = pkgs.symlinkJoin {
235 name = "jolt-native-libs";
236 paths = [ libvidya libjolttui libjoltmoq ];
237 };
238 in
239 {
240 packages = {
241 default = libs;
242 android = libsAndroid;
243 inherit libs libvidya libjolttui libjoltmoq;
244 };
245
246 devShells.default = pkgs.mkShell ({
247 packages = [ rustToolchain pkgs.just pkgs.sccache pkgs.cargo-nextest ]
248 ++ nativeBuildInputs
249 ++ desktopBuildInputs;
250
251 # egui opens libGL and the Wayland/X11 client libraries with dlopen,
252 # so they have to be findable at run time and not only at link time.
253 LD_LIBRARY_PATH = lib.makeLibraryPath desktopBuildInputs;
254 } // v4l2Env);
255
256 checks = {
257 inherit libvidya libjolttui libjoltmoq;
258
259 clippy = craneLib.cargoClippy (commonArgs // {
260 inherit cargoArtifacts;
261 pname = "jolt-native-clippy";
262 cargoClippyExtraArgs = "--workspace --all-targets -- -D warnings";
263 });
264
265 fmt = craneLib.cargoFmt {
266 inherit src;
267 pname = "jolt-native-fmt";
268 };
269
270 test = craneLib.cargoTest (commonArgs // {
271 inherit cargoArtifacts;
272 pname = "jolt-native-test";
273 cargoTestExtraArgs = "--workspace";
274 });
275 };
276
277 formatter = pkgs.nixpkgs-fmt;
278 });
279}