atproto blogging
1{
2 description = "Build a cargo workspace";
3
4 inputs = {
5 nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
6 flake-utils.url = "github:numtide/flake-utils";
7
8 advisory-db = {
9 url = "github:rustsec/advisory-db";
10 flake = false;
11 };
12
13 rust-overlay.url = "github:oxalica/rust-overlay";
14 crane.url = "github:ipetkov/crane";
15 dioxus.url = "github:DioxusLabs/dioxus";
16 };
17
18 outputs = {
19 self,
20 nixpkgs,
21 crane,
22 rust-overlay,
23 flake-utils,
24 advisory-db,
25 dioxus,
26 ...
27 }: let
28 name = "weaver";
29 in
30 flake-utils.lib.eachDefaultSystem (system: let
31 pkgs = import nixpkgs {
32 inherit system;
33 overlays = [
34 (import rust-overlay)
35 (_: prev: {
36 dioxus-cli = dioxus.packages.${prev.system}.dioxus-cli;
37 })
38 ];
39 };
40 inherit (pkgs) lib;
41
42 rustToolchainFor = p:
43 p.rust-bin.selectLatestNightlyWith (toolchain:
44 toolchain.default.override {
45 # Set the build targets supported by the toolchain,
46 # wasm32-unknown-unknown is required for trunk.
47 targets = ["wasm32-unknown-unknown" "wasm32-wasip1" "wasm32-wasip2"];
48 extensions = [
49 "rust-src"
50 "rust-analyzer"
51 "clippy"
52 ];
53 });
54 craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchainFor;
55 src = craneLib.cleanCargoSource ./.;
56
57 # Common arguments can be set here to avoid repeating them later
58 commonArgs = {
59 inherit src;
60 strictDeps = true;
61
62 buildInputs = with pkgs;
63 [
64 # Add additional build inputs here
65 sqlite
66 pkg-config
67 openssl
68 ]
69 ++ lib.optionals pkgs.stdenv.isDarwin [
70 # Additional darwin specific inputs can be set here
71 pkgs.libiconv
72 ];
73 nativeBuildInputs = with pkgs; [
74 sqlite
75 pkg-config
76 openssl
77 ];
78 # Additional environment variables can be set directly
79 # MY_CUSTOM_VAR = "some value";
80 };
81
82 # Build *just* the cargo dependencies (of the entire workspace),
83 # so we can reuse all of that work (e.g. via cachix) when running in CI
84 # It is *highly* recommended to use something like cargo-hakari to avoid
85 # cache misses when building individual top-level-crates
86 cargoArtifacts = craneLib.buildDepsOnly commonArgs;
87
88 individualCrateArgs =
89 commonArgs
90 // {
91 inherit cargoArtifacts;
92 inherit (craneLib.crateNameFromCargoToml {inherit src;}) version;
93 # NB: we disable tests since we'll run them all via cargo-nextest
94 doCheck = false;
95 };
96
97 fileSetForCrate = crate:
98 lib.fileset.toSource {
99 root = ./.;
100 fileset = lib.fileset.unions [
101 ./Cargo.toml
102 ./Cargo.lock
103 (lib.fileset.maybeMissing ./crates/weaver-app/Dioxus.lock)
104 (craneLib.fileset.commonCargoSources ./crates/weaver-common)
105 (craneLib.fileset.commonCargoSources crate)
106 ];
107 };
108
109 # Build the top-level crates of the workspace as individual derivations.
110 # This allows consumers to only depend on (and build) only what they need.
111 # Though it is possible to build the entire workspace as a single derivation,
112 # so this is left up to you on how to organize things
113 #
114 # Note that the cargo workspace must define `workspace.members` using wildcards,
115 # otherwise, omitting a crate (like we do below) will result in errors since
116 # cargo won't be able to find the sources for all members.
117 weaver-cli = craneLib.buildPackage (individualCrateArgs
118 // {
119 pname = "${name}";
120 cargoExtraArgs = "-p ${name}-cli";
121 src = fileSetForCrate ./crates/weaver-cli;
122 });
123 weaver-app = craneLib.buildPackage (individualCrateArgs
124 // {
125 pname = "${name}-app";
126 cargoExtraArgs = "-p ${name}-app";
127 src = fileSetForCrate ./crates/weaver-app;
128 });
129 weaver-renderer = craneLib.buildPackage (individualCrateArgs
130 // {
131 pname = "${name}-renderer";
132 cargoExtraArgs = "-p ${name}-renderer";
133 src = fileSetForCrate ./crates/weaver-renderer;
134 });
135 in {
136 checks = {
137 # Build the crates as part of `nix flake check` for convenience
138 inherit weaver-cli weaver-app weaver-renderer;
139
140 # Run clippy (and deny all warnings) on the workspace source,
141 # again, reusing the dependency artifacts from above.
142 #
143 # Note that this is done as a separate derivation so that
144 # we can block the CI if there are issues here, but not
145 # prevent downstream consumers from building our crate by itself.
146 "${name}-workspace-clippy" = craneLib.cargoClippy (commonArgs
147 // {
148 inherit cargoArtifacts;
149 cargoClippyExtraArgs = "--all-targets -- --deny warnings";
150 });
151
152 "${name}-workspace-doc" = craneLib.cargoDoc (commonArgs
153 // {
154 inherit cargoArtifacts;
155 });
156
157 # Check formatting
158 "${name}-workspace-fmt" = craneLib.cargoFmt {
159 inherit src;
160 };
161
162 # "${name}-workspace-toml-fmt" = craneLib.taploFmt {
163 # src = pkgs.lib.sources.sourceFilesBySuffices src [".toml"];
164 # # taplo arguments can be further customized below as needed
165 # taploExtraArgs = "--config ./taplo.toml";
166 # };
167
168 # Audit dependencies
169 "${name}-workspace-audit" = craneLib.cargoAudit {
170 inherit src advisory-db;
171 };
172
173 # Audit licenses
174 "${name}-workspace-deny" = craneLib.cargoDeny {
175 inherit src;
176 };
177
178 # Run tests with cargo-nextest
179 # Consider setting `doCheck = false` on other crate derivations
180 # if you do not want the tests to run twice
181 "${name}-workspace-nextest" = craneLib.cargoNextest (commonArgs
182 // {
183 inherit cargoArtifacts;
184 partitions = 1;
185 partitionType = "count";
186 cargoNextestPartitionsExtraArgs = "--no-tests=pass";
187 });
188 };
189
190 packages =
191 {
192 inherit weaver-cli weaver-app;
193 }
194 // lib.optionalAttrs (!pkgs.stdenv.isDarwin) {
195 # weaver-workspace-llvm-coverage = craneLibLLvmTools.cargoLlvmCov (commonArgs // {
196 # inherit cargoArtifacts;
197 # });
198 };
199
200 apps = {
201 weaver-cli = flake-utils.lib.mkApp {
202 drv = weaver-cli;
203 };
204 weaver-app = flake-utils.lib.mkApp {
205 drv = weaver-app;
206 };
207 };
208
209 devShells.default = let
210 # dioxus-cli = pkgs.dioxus-cli.overrideAttrs (_: {
211 # postPatch = ''
212 # rm Cargo.lock
213 # cp ${./crates/weaver-app/Dioxus.lock} Cargo.lock
214 # '';
215 # cargoDeps = pkgs.rustPlatform.importCargoLock {
216 # lockFile = ./crates/weaver-app/Dioxus.lock;
217 # };
218 # });
219 cargoLock = builtins.fromTOML (builtins.readFile ./Cargo.lock);
220
221 wasmBindgen =
222 pkgs.lib.findFirst
223 (pkg: pkg.name == "wasm-bindgen")
224 (throw "Could not find wasm-bindgen package")
225 cargoLock.package;
226
227 wasm-bindgen-cli = pkgs.buildWasmBindgenCli rec {
228 src = pkgs.fetchCrate {
229 pname = "wasm-bindgen-cli";
230 version = wasmBindgen.version;
231 hash = "sha256-M6WuGl7EruNopHZbqBpucu4RWz44/MSdv6f0zkYw+44=";
232 };
233
234 cargoDeps = pkgs.rustPlatform.fetchCargoVendor {
235 inherit src;
236 inherit (src) pname version;
237 hash = "sha256-ElDatyOwdKwHg3bNH/1pcxKI7LXkhsotlDPQjiLHBwA=";
238 };
239 };
240 in
241 craneLib.devShell {
242 inherit name;
243 # Inherit inputs from checks.
244 checks = self.checks.${system};
245 NIX_LD_LIBRARY_PATH = with pkgs;
246 lib.makeLibraryPath [
247 stdenv.cc.cc
248 openssl
249 # ...
250 ];
251 NIX_LD = lib.fileContents "${pkgs.stdenv.cc}/nix-support/dynamic-linker";
252
253 LD_LIBRARY_PATH = "$LD_LIBRARY_PATH:$NIX_LD_LIBRARY_PATH";
254
255 # musl linker for static builds
256 #CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER = "${pkgs.pkgsMusl.stdenv.cc}/bin/cc";
257
258 # Additional dev-shell environment variables can be set directly
259 # MY_CUSTOM_DEVELOPMENT_VAR = "something else";
260
261 # Extra inputs can be added here; cargo and rustc are provided by default.
262 packages = with pkgs; [
263 nixd
264 alejandra
265 diesel-cli
266 postgresql
267 cargo-insta
268 cargo-bloat
269 jq
270 dioxus-cli
271 wasm-bindgen-cli
272 wasm-pack
273 twiggy
274 binaryen.out
275 patchelf
276 llvmPackages_18.clang-unwrapped
277 llvmPackages_18.llvm
278 llvmPackages_18.libclang
279 wabt
280 ];
281
282 nativeBuildInputs = [pkgs.llvmPackages_18.clang-unwrapped pkgs.llvmPackages_18.bintools-unwrapped];
283
284 shellHook = ''
285 export CC_wasm32_unknown_unknown="${pkgs.llvmPackages_18.clang-unwrapped}/bin/clang"
286 export AR_wasm32_unknown_unknown="${pkgs.llvmPackages_18.bintools-unwrapped}/bin/llvm-ar"
287 CLANG_MAJOR_VERSION="18"
288 CLANG_RESOURCE_DIR="${pkgs.llvmPackages_18.clang-unwrapped}/lib/clang/$CLANG_MAJOR_VERSION"
289
290 # Use libclang's include directory which has the standard headers
291 LIBCLANG_INCLUDE="${pkgs.llvmPackages_18.libclang.lib}/lib/clang/$CLANG_MAJOR_VERSION/include"
292
293 export CFLAGS_wasm32_unknown_unknown="-isystem $LIBCLANG_INCLUDE -resource-dir $CLANG_RESOURCE_DIR"
294 export CPPFLAGS="-isystem $LIBCLANG_INCLUDE -resource-dir $CLANG_RESOURCE_DIR"
295
296 # Debug: Print the paths to verify they exist
297 echo "Clang resource dir: $CLANG_RESOURCE_DIR"
298 echo "Libclang include dir: $LIBCLANG_INCLUDE"
299 if [ -f "$LIBCLANG_INCLUDE/stddef.h" ]; then
300 echo "Found stddef.h at: $LIBCLANG_INCLUDE/stddef.h"
301 else
302 echo "stddef.h not found in $LIBCLANG_INCLUDE"
303 fi
304 '';
305 };
306 });
307}