diff options
author | Alex Auvolat <lx@deuxfleurs.fr> | 2025-02-01 21:48:10 +0100 |
---|---|---|
committer | Alex Auvolat <lx@deuxfleurs.fr> | 2025-02-03 16:39:50 +0100 |
commit | 390a5d97fece744bcad3c5b7b7d31b2722d0b092 (patch) | |
tree | 02ca809d1305f8f76eedfac145fb843489d65d35 /nix | |
parent | 4dc2bc337f10be616a141ba8b52564038ef3ae41 (diff) | |
download | garage-390a5d97fece744bcad3c5b7b7d31b2722d0b092.tar.gz garage-390a5d97fece744bcad3c5b7b7d31b2722d0b092.zip |
nix, ci: build with Crane
This removes our dependency on cargo2nix, which was causing us some
issues. Whereas cargo2nix creates one Nix derivation per crate, Crane
uses only two derivations:
1. Build dependencies only
2. Build the final binary
This means that during the second step, no caching can be done. For
instance, if we do a change in garage_model, we need to recompile all of
the Garage crates including those that do not depend on garage_model.
On the upside, this allows all of the Garage crates to be built at once
using cargo build logic, which is optimized for high parallelism and
better pipelining between all of the steps of the build. All in all,
this makes most builds faster than cargo2nix.
A few other changes have been made to the build scripts and CI:
- Unit tests are now run within a Nix derivation. In fact, we have
different derivations to run the tests using LMDB and Sqlite as
metadata db engines.
- For debug builds, most CI steps now run in parallel (with the notable
exception of the smoke test that runs after the build, which is
inevitable).
- We no longer pass the GIT_VERSION argument when building debug builds
and running the tests. This means that dev binaries and test
binaries don't know the exact version of Garage they are from. That
shouldn't be an issue in most cases.
- The not-dynamic.sh scripts has been fixed to fail if the file does not
exist.
Diffstat (limited to 'nix')
-rw-r--r-- | nix/build_index.nix | 2 | ||||
-rw-r--r-- | nix/common.nix | 10 | ||||
-rw-r--r-- | nix/compile.nix | 238 |
3 files changed, 152 insertions, 98 deletions
diff --git a/nix/build_index.nix b/nix/build_index.nix index 7cc4f62c..7869566f 100644 --- a/nix/build_index.nix +++ b/nix/build_index.nix @@ -2,7 +2,7 @@ with import ./common.nix; let - pkgs = import pkgsSrc { }; + pkgs = import nixpkgs { }; lib = pkgs.lib; /* Converts a key list and a value list to a set diff --git a/nix/common.nix b/nix/common.nix index 1ad809bb..2c03f04e 100644 --- a/nix/common.nix +++ b/nix/common.nix @@ -10,9 +10,9 @@ let flake = (import flake-compat { system = builtins.currentSystem; src = ../.; }); in -rec { - pkgsSrc = flake.defaultNix.inputs.nixpkgs; - cargo2nix = flake.defaultNix.inputs.cargo2nix; - cargo2nixOverlay = cargo2nix.overlays.default; - devShells = builtins.getAttr builtins.currentSystem flake.defaultNix.devShells; + +{ + flake = flake.defaultNix; + nixpkgs = flake.defaultNix.inputs.nixpkgs; + devShells = flake.defaultNix.devShells.${builtins.currentSystem}; } diff --git a/nix/compile.nix b/nix/compile.nix index 9630b5dd..4e3fdd7a 100644 --- a/nix/compile.nix +++ b/nix/compile.nix @@ -1,83 +1,64 @@ -{ system, target ? null, pkgsSrc, cargo2nixOverlay -, release ? false, git_version ? null, features ? null, }: +{ + /* build inputs */ + nixpkgs, + crane, + rust-overlay, + + /* parameters */ + system, + git_version ? null, + target ? null, + release ? false, + features ? null, + extraTestEnv ? {} +}: let log = v: builtins.trace v v; + # NixOS and Rust/Cargo triples do not match for ARM, fix it here. + rustTarget = if target == "armv6l-unknown-linux-musleabihf" then + "arm-unknown-linux-musleabihf" + else + target; + + rustTargetEnvMap = { + "x86_64-unknown-linux-musl" = "X86_64_UNKNOWN_LINUX_MUSL"; + "aarch64-unknown-linux-musl" = "AARCH64_UNKNOWN_LINUX_MUSL"; + "i686-unknown-linux-musl" = "I686_UNKNOWN_LINUX_MUSL"; + "arm-unknown-linux-musleabihf" = "ARM_UNKNOWN_LINUX_MUSLEABIHF"; + }; + + pkgsNative = import nixpkgs { + inherit system; + overlays = [ (import rust-overlay) ]; + }; + pkgs = if target != null then - import pkgsSrc { + import nixpkgs { inherit system; crossSystem = { config = target; isStatic = true; }; - overlays = [ cargo2nixOverlay ]; + overlays = [ (import rust-overlay) ]; } else - import pkgsSrc { - inherit system; - overlays = [ cargo2nixOverlay ]; - }; + pkgsNative; - toolchainOptions = { - rustVersion = "1.78.0"; - extraRustComponents = [ "clippy" ]; - }; + inherit (pkgs) lib stdenv; - /* Cargo2nix provides many overrides by default, you can take inspiration from them: - https://github.com/cargo2nix/cargo2nix/blob/master/overlay/overrides.nix - - You can have a complete list of the available options by looking at the overriden object, mkcrate: - https://github.com/cargo2nix/cargo2nix/blob/master/overlay/mkcrate.nix - */ - packageOverrides = pkgs: - pkgs.rustBuilder.overrides.all ++ [ - /* [1] We need to alter Nix hardening to make static binaries: PIE, - Position Independent Executables seems to be supported only on amd64. Having - this flag set either 1. make our executables crash or 2. compile as dynamic on some platforms. - Here, we deactivate it. Later (find `codegenOpts`), we reactivate it for supported targets - (only amd64 curently) through the `-static-pie` flag. - PIE is a feature used by ASLR, which helps mitigate security issues. - Learn more about Nix Hardening at: https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/cc-wrapper/add-hardening.sh - - [2] We want to inject the git version while keeping the build deterministic. - As we do not want to consider the .git folder as part of the input source, - we ask the user (the CI often) to pass the value to Nix. - - [3] We don't want libsodium-sys and zstd-sys to try to use pkgconfig to build against a system library. - However the features to do so get activated for some reason (due to a bug in cargo2nix?), - so disable them manually here. - */ - (pkgs.rustBuilder.rustLib.makeOverride { - name = "garage"; - overrideAttrs = drv: - (if git_version != null then { - # [2] - preConfigure = '' - ${drv.preConfigure or ""} - export GIT_VERSION="${git_version}" - ''; - } else - { }) // { - # [1] - hardeningDisable = [ "pie" ]; - }; - }) - - (pkgs.rustBuilder.rustLib.makeOverride { - name = "libsodium-sys"; - overrideArgs = old: { - features = [ ]; # [3] - }; - }) - - (pkgs.rustBuilder.rustLib.makeOverride { - name = "zstd-sys"; - overrideArgs = old: { - features = [ ]; # [3] - }; - }) + toolchainFn = (p: p.rust-bin.stable."1.78.0".default.override { + targets = lib.optionals (target != null) [ rustTarget ]; + extensions = [ + "rust-src" + "rustfmt" ]; + }); + + craneLib = (crane.mkLib pkgs).overrideToolchain toolchainFn; + + src = craneLib.cleanCargoSource ../.; /* We ship some parts of the code disabled by default by putting them behind a flag. It speeds up the compilation (when the feature is not required) and released crates have less dependency by default (less attack surface, disk space, etc.). @@ -87,16 +68,15 @@ let rootFeatures = if features != null then features else - ([ "garage/bundled-libs" "garage/lmdb" "garage/sqlite" "garage/k2v" ] ++ (if release then [ - "garage/consul-discovery" - "garage/kubernetes-discovery" - "garage/metrics" - "garage/telemetry-otlp" - "garage/syslog" - ] else - [ ])); + ([ "bundled-libs" "lmdb" "sqlite" "k2v" ] ++ (lib.optionals release [ + "consul-discovery" + "kubernetes-discovery" + "metrics" + "telemetry-otlp" + "syslog" + ])); - packageFun = import ../Cargo.nix; + featuresStr = lib.concatStringsSep "," rootFeatures; /* We compile fully static binaries with musl to simplify deployment on most systems. When possible, we reactivate PIE hardening (see above). @@ -107,12 +87,9 @@ let For more information on static builds, please refer to Rust's RFC 1721. https://rust-lang.github.io/rfcs/1721-crt-static.html#specifying-dynamicstatic-c-runtime-linkage */ - - codegenOpts = { - "armv6l-unknown-linux-musleabihf" = [ - "target-feature=+crt-static" - "link-arg=-static" - ]; # compile as dynamic with static-pie + codegenOptsMap = { + "x86_64-unknown-linux-musl" = + [ "target-feature=+crt-static" "link-arg=-static-pie" ]; "aarch64-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static" @@ -121,18 +98,95 @@ let "target-feature=+crt-static" "link-arg=-static" ]; # segfault with static-pie - "x86_64-unknown-linux-musl" = - [ "target-feature=+crt-static" "link-arg=-static-pie" ]; + "armv6l-unknown-linux-musleabihf" = [ + "target-feature=+crt-static" + "link-arg=-static" + ]; # compile as dynamic with static-pie }; - # NixOS and Rust/Cargo triples do not match for ARM, fix it here. - rustTarget = if target == "armv6l-unknown-linux-musleabihf" then - "arm-unknown-linux-musleabihf" - else - target; + codegenOpts = if target != null then codegenOptsMap.${target} else [ + "link-arg=-fuse-ld=mold" + ]; + + commonArgs = + { + inherit src; + pname = "garage"; + version = "dev"; + + strictDeps = true; + cargoExtraArgs = "--locked --features ${featuresStr}"; + cargoTestExtraArgs = "--workspace"; + + nativeBuildInputs = [ + pkgsNative.protobuf + pkgs.stdenv.cc + ] ++ lib.optionals (target == null) [ + pkgs.clang + pkgs.mold + ]; + + CARGO_PROFILE = if release then "release" else "dev"; + CARGO_BUILD_RUSTFLAGS = + lib.concatStringsSep + " " + (builtins.map (flag: "-C ${flag}") codegenOpts); + } + // + (if rustTarget != null then { + CARGO_BUILD_TARGET = rustTarget; + + "CARGO_TARGET_${rustTargetEnvMap.${rustTarget}}_LINKER" = "${stdenv.cc.targetPrefix}cc"; + + HOST_CC = "${stdenv.cc.nativePrefix}cc"; + TARGET_CC = "${stdenv.cc.targetPrefix}cc"; + } else { + CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER = "clang"; + }); + +in rec { + toolchain = toolchainFn pkgs; + + devShell = pkgs.mkShell { + buildInputs = [ + toolchain + ] ++ (with pkgs; [ + protobuf + clang + mold + ]); + }; + + # ---- building garage ---- + + garage-deps = craneLib.buildDepsOnly commonArgs; + + garage = craneLib.buildPackage (commonArgs // { + cargoArtifacts = garage-deps; + + doCheck = false; + } // + (if git_version != null then { + version = git_version; + GIT_VERSION = git_version; + } else {})); -in pkgs.rustBuilder.makePackageSet ({ - inherit release packageFun packageOverrides codegenOpts rootFeatures; - target = rustTarget; - workspaceSrc = pkgs.lib.cleanSource ../.; -} // toolchainOptions) + # ---- testing garage ---- + + garage-test-bin = craneLib.cargoBuild (commonArgs // { + cargoArtifacts = garage-deps; + + pname = "garage-tests"; + + CARGO_PROFILE = "test"; + cargoExtraArgs = "${commonArgs.cargoExtraArgs} --tests --workspace"; + doCheck = false; + }); + + garage-test = craneLib.cargoTest (commonArgs // { + cargoArtifacts = garage-test-bin; + nativeBuildInputs = commonArgs.nativeBuildInputs ++ [ + pkgs.cacert + ]; + } // extraTestEnv); +} |