From 71d4cf42f1971d18c4142a6bbc8fd05f484ca090 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Mon, 25 Jul 2022 14:58:47 +0200 Subject: [PATCH] Refactor default.nix to follow Nix Flakes patterns --- .drone.yml | 87 +++++++++++--------------- default.nix | 159 +++++++----------------------------------------- nix/compile.nix | 140 ++++++++++++++++++++++++++++++++++++++++++ nix/nix.conf | 2 + 4 files changed, 200 insertions(+), 188 deletions(-) create mode 100644 nix/compile.nix diff --git a/.drone.yml b/.drone.yml index 726387a4..f76e162d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -36,7 +36,7 @@ steps: - name: nix_config path: /etc/nix commands: - - nix-build --no-build-output --no-out-link shell.nix -A rust.inputDerivation -A integration.inputDerivation -A release.inputDerivation + - nix-build --no-build-output --no-out-link shell.nix -A rust.inputDerivation -A integration.inputDerivation - name: code quality image: nixpkgs/nix:nixos-22.05 @@ -57,7 +57,7 @@ steps: - name: nix_config path: /etc/nix commands: - - nix-build --no-build-output --argstr target x86_64-unknown-linux-musl --arg release false --argstr git_version $DRONE_COMMIT + - nix-build --no-build-output --attr pkgs.amd64.debug --argstr git_version $DRONE_COMMIT - nix-shell --attr rust --run "./script/not-dynamic.sh result/bin/garage" - name: unit + func tests @@ -70,12 +70,7 @@ steps: - name: nix_config path: /etc/nix commands: - - | - nix-build \ - --no-build-output \ - --option log-lines 100 \ - --argstr target x86_64-unknown-linux-musl \ - --argstr compileMode test + - nix-build --no-build-output --attr test.amd64 - ./result/bin/garage_api-* - ./result/bin/garage_model-* - ./result/bin/garage_rpc-* @@ -93,7 +88,7 @@ steps: - name: nix_config path: /etc/nix commands: - - nix-build --no-build-output --argstr target x86_64-unknown-linux-musl --arg release false --argstr git_version $DRONE_COMMIT + - nix-build --no-build-output --attr pkgs.amd64.debug --argstr git_version $DRONE_COMMIT - nix-shell --attr integration --run ./script/test-smoke.sh || (cat /tmp/garage.log; false) trigger: @@ -107,7 +102,7 @@ trigger: --- kind: pipeline type: docker -name: release-linux-x86_64 +name: release-linux-amd64 volumes: - name: nix_store @@ -116,20 +111,18 @@ volumes: - name: nix_config temp: {} -environment: - TARGET: x86_64-unknown-linux-musl - steps: - - name: setup nix + - name: nix maintainance image: nixpkgs/nix:nixos-22.05 volumes: - name: nix_store - path: /nix + path: /mnt - name: nix_config path: /etc/nix commands: - - cp nix/nix.conf /etc/nix/nix.conf - - nix-build --no-build-output --no-out-link shell.nix -A rust.inputDerivation -A integration.inputDerivation -A release.inputDerivation + - "[ -d /mnt/store/3vpyn2qz5ay057nq9x68sh0r328d77ng-nix-2.8.1/ ] || (mkdir -p /mnt/store && cp -r /nix/store/* /mnt/store/)" + - "[ -d /mnt/var/ ] || cp -r /nix/var /mnt/" + - cp nix/nix.conf /etc/nix/nix.conf - name: build image: nixpkgs/nix:nixos-22.05 @@ -139,7 +132,7 @@ steps: - name: nix_config path: /etc/nix commands: - - nix-build --no-build-output --argstr target $TARGET --arg release true --argstr git_version $DRONE_COMMIT + - nix-build --no-build-output --attr pkgs.amd64.release --argstr git_version $DRONE_COMMIT - nix-shell --attr rust --run "./script/not-dynamic.sh result/bin/garage" - name: integration @@ -195,7 +188,7 @@ trigger: --- kind: pipeline type: docker -name: release-linux-i686 +name: release-linux-i386 volumes: - name: nix_store @@ -204,20 +197,18 @@ volumes: - name: nix_config temp: {} -environment: - TARGET: i686-unknown-linux-musl - steps: - - name: setup nix + - name: nix maintainance image: nixpkgs/nix:nixos-22.05 volumes: - name: nix_store - path: /nix + path: /mnt - name: nix_config path: /etc/nix commands: - - cp nix/nix.conf /etc/nix/nix.conf - - nix-build --no-build-output --no-out-link shell.nix -A rust.inputDerivation -A integration.inputDerivation -A release.inputDerivation + - "[ -d /mnt/store/3vpyn2qz5ay057nq9x68sh0r328d77ng-nix-2.8.1/ ] || (mkdir -p /mnt/store && cp -r /nix/store/* /mnt/store/)" + - "[ -d /mnt/var/ ] || cp -r /nix/var /mnt/" + - cp nix/nix.conf /etc/nix/nix.conf - name: build image: nixpkgs/nix:nixos-22.05 @@ -227,7 +218,7 @@ steps: - name: nix_config path: /etc/nix commands: - - nix-build --no-build-output --argstr target $TARGET --arg release true --argstr git_version $DRONE_COMMIT + - nix-build --no-build-output --attr pkgs.i386.release --argstr git_version $DRONE_COMMIT - nix-shell --attr rust --run "./script/not-dynamic.sh result/bin/garage" - name: integration @@ -282,7 +273,7 @@ trigger: --- kind: pipeline type: docker -name: release-linux-aarch64 +name: release-linux-arm64 volumes: - name: nix_store @@ -291,20 +282,18 @@ volumes: - name: nix_config temp: {} -environment: - TARGET: aarch64-unknown-linux-musl - steps: - - name: setup nix + - name: nix maintainance image: nixpkgs/nix:nixos-22.05 volumes: - name: nix_store - path: /nix + path: /mnt - name: nix_config path: /etc/nix commands: - - cp nix/nix.conf /etc/nix/nix.conf - - nix-build --no-build-output --no-out-link shell.nix -A rust.inputDerivation -A integration.inputDerivation -A release.inputDerivation + - "[ -d /mnt/store/3vpyn2qz5ay057nq9x68sh0r328d77ng-nix-2.8.1/ ] || (mkdir -p /mnt/store && cp -r /nix/store/* /mnt/store/)" + - "[ -d /mnt/var/ ] || cp -r /nix/var /mnt/" + - cp nix/nix.conf /etc/nix/nix.conf - name: build image: nixpkgs/nix:nixos-22.05 @@ -314,7 +303,7 @@ steps: - name: nix_config path: /etc/nix commands: - - nix-build --no-build-output --argstr target $TARGET --arg release true --argstr git_version $DRONE_COMMIT + - nix-build --no-build-output --attr pkgs.arm64.release --argstr git_version $DRONE_COMMIT - nix-shell --attr rust --run "./script/not-dynamic.sh result/bin/garage" - name: push static binary @@ -359,7 +348,7 @@ trigger: --- kind: pipeline type: docker -name: release-linux-armv6l +name: release-linux-arm volumes: - name: nix_store @@ -368,20 +357,18 @@ volumes: - name: nix_config temp: {} -environment: - TARGET: armv6l-unknown-linux-musleabihf - steps: - - name: setup nix + - name: nix maintainance image: nixpkgs/nix:nixos-22.05 volumes: - name: nix_store - path: /nix + path: /mnt - name: nix_config path: /etc/nix commands: - - cp nix/nix.conf /etc/nix/nix.conf - - nix-build --no-build-output --no-out-link shell.nix -A rust.inputDerivation -A integration.inputDerivation -A release.inputDerivation + - "[ -d /mnt/store/3vpyn2qz5ay057nq9x68sh0r328d77ng-nix-2.8.1/ ] || (mkdir -p /mnt/store && cp -r /nix/store/* /mnt/store/)" + - "[ -d /mnt/var/ ] || cp -r /nix/var /mnt/" + - cp nix/nix.conf /etc/nix/nix.conf - name: build image: nixpkgs/nix:nixos-22.05 @@ -391,7 +378,7 @@ steps: - name: nix_config path: /etc/nix commands: - - nix-build --no-build-output --argstr target $TARGET --arg release true --argstr git_version $DRONE_COMMIT + - nix-build --no-build-output --attr pkgs.arm.release --argstr git_version $DRONE_COMMIT - nix-shell --attr rust --run "./script/not-dynamic.sh result/bin/garage" - name: push static binary @@ -459,10 +446,10 @@ steps: - nix-shell --attr release --run "refresh_index" depends_on: - - release-linux-x86_64 - - release-linux-i686 - - release-linux-aarch64 - - release-linux-armv6l + - release-linux-amd64 + - release-linux-i386 + - release-linux-arm64 + - release-linux-arm trigger: event: @@ -471,6 +458,6 @@ trigger: --- kind: signature -hmac: 12b06094741a9b6da448e3a176d2fc37b2c261ab87acefa60a070e67a55352b0 +hmac: 9789d5fd470fc4273adcfd05946833268d1e466462c5f36abeb8f607d62fdb4b ... diff --git a/default.nix b/default.nix index 296c7592..5634f0af 100644 --- a/default.nix +++ b/default.nix @@ -1,149 +1,32 @@ { system ? builtins.currentSystem, - release ? false, - target ? "x86_64-unknown-linux-musl", - compileMode ? null, git_version ? null, }: with import ./nix/common.nix; -let - crossSystem = { config = target; }; -in let - log = v: builtins.trace v v; +let + compile = import ./nix/compile.nix; + build_debug_and_release = (target: { + debug = (compile { inherit target; release = false; }).workspace.garage { compileMode = "build"; }; + release = (compile { inherit target; release = true; }).workspace.garage { compileMode = "build"; }; + }); - pkgs = import pkgsSrc { - inherit system crossSystem; - overlays = [ cargo2nixOverlay ]; +in { + pkgs = { + amd64 = build_debug_and_release "x86_64-unknown-linux-musl"; + i386 = build_debug_and_release "i686-unknown-linux-musl"; + arm64 = build_debug_and_release "aarch64-unknown-linux-musl"; + arm = build_debug_and_release "armv6l-unknown-linux-musleabihf"; }; - - - /* - Rust and Nix triples are not the same. Cargo2nix has a dedicated library - to convert Nix triples to Rust ones. We need this conversion as we want to - set later options linked to our (rust) target in a generic way. Not only - the triple terminology is different, but also the "roles" are named differently. - Nix uses a build/host/target terminology where Nix's "host" maps to Cargo's "target". - */ - rustTarget = log (pkgs.rustBuilder.rustLib.rustTriple pkgs.stdenv.hostPlatform); - - /* - Cargo2nix is built for rustOverlay which installs Rust from Mozilla releases. - We want our own Rust to avoid incompatibilities, like we had with musl 1.2.0. - rustc was built with musl < 1.2.0 and nix shipped musl >= 1.2.0 which lead to compilation breakage. - So we want a Rust release that is bound to our Nix repository to avoid these problems. - See here for more info: https://musl.libc.org/time64.html - Because Cargo2nix does not support the Rust environment shipped by NixOS, - we emulate the structure of the Rust object created by rustOverlay. - In practise, rustOverlay ships rustc+cargo in a single derivation while - NixOS ships them in separate ones. We reunite them with symlinkJoin. - */ - rustChannel = pkgs.symlinkJoin { - name ="rust-channel"; - paths = [ - pkgs.rustPlatform.rust.rustc - pkgs.rustPlatform.rust.cargo - ]; - }; - - /* - 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 - */ - overrides = 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 - */ - (pkgs.rustBuilder.rustLib.makeOverride { - name = "garage"; - overrideAttrs = drv: { hardeningDisable = [ "pie" ]; }; - }) - - (pkgs.rustBuilder.rustLib.makeOverride { - name = "garage_rpc"; - - /* - [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. - */ - overrideAttrs = drv: - (if git_version != null then { - preConfigure = '' - ${drv.preConfigure or ""} - export GIT_VERSION="${git_version}" - ''; - } else {}); - - /* - [3] 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.). - But we want to ship these additional features when we release Garage. - In the end, we chose to exclude all features from debug builds while putting (all of) them in the release builds. - Currently, the only feature of Garage is kubernetes-discovery from the garage_rpc crate. - */ - overrideArgs = old: { - features = if release then [ "kubernetes-discovery" ] else []; + test = { + amd64 = let + pkgs = import pkgsSrc { }; + rustPkgs = compile { target = "x86_64-unknown-linux-musl"; }; + in + pkgs.symlinkJoin { + name ="garage-tests"; + paths = builtins.map (key: rustPkgs.workspace.${key} { compileMode = "test"; }) (builtins.attrNames rustPkgs.workspace); }; - }) - - ]; - - packageFun = import ./Cargo.nix; - - /* - We compile fully static binaries with musl to simplify deployment on most systems. - When possible, we reactivate PIE hardening (see above). - - Also, if you set the RUSTFLAGS environment variable, the following parameters will - be ignored. - - 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 */ - "aarch64-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static" ]; /* segfault with static-pie */ - "i686-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static" ]; /* segfault with static-pie */ - "x86_64-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static-pie" ]; }; - - /* - The following definition is not elegant as we use a low level function of Cargo2nix - that enables us to pass our custom rustChannel object. We need this low level definition - to pass Nix's Rust toolchains instead of Mozilla's one. - - target is mandatory but must be kept to null to allow cargo2nix to set it to the appropriate value - for each crate. - */ - rustPkgs = pkgs.rustBuilder.makePackageSet { - inherit packageFun rustChannel release codegenOpts; - packageOverrides = overrides; - target = null; - - buildRustPackages = pkgs.buildPackages.rustBuilder.makePackageSet { - inherit rustChannel packageFun codegenOpts; - packageOverrides = overrides; - target = null; - }; - }; - - -in - if compileMode == "test" - then pkgs.symlinkJoin { - name ="garage-tests"; - paths = builtins.map (key: rustPkgs.workspace.${key} { inherit compileMode; }) (builtins.attrNames rustPkgs.workspace); - } - else rustPkgs.workspace.garage { inherit compileMode; } +} diff --git a/nix/compile.nix b/nix/compile.nix new file mode 100644 index 00000000..972e2f2e --- /dev/null +++ b/nix/compile.nix @@ -0,0 +1,140 @@ +{ + system ? builtins.currentSystem, + target ? "x86_64-unknown-linux-musl", + release ? false, + git_version ? null, +}: + +with import ./common.nix; + +let + crossSystem = { config = target; }; + + log = v: builtins.trace v v; + + pkgs = import pkgsSrc { + inherit system crossSystem; + overlays = [ cargo2nixOverlay ]; + }; + + + /* + Rust and Nix triples are not the same. Cargo2nix has a dedicated library + to convert Nix triples to Rust ones. We need this conversion as we want to + set later options linked to our (rust) target in a generic way. Not only + the triple terminology is different, but also the "roles" are named differently. + Nix uses a build/host/target terminology where Nix's "host" maps to Cargo's "target". + */ + rustTarget = log (pkgs.rustBuilder.rustLib.rustTriple pkgs.stdenv.hostPlatform); + + /* + Cargo2nix is built for rustOverlay which installs Rust from Mozilla releases. + We want our own Rust to avoid incompatibilities, like we had with musl 1.2.0. + rustc was built with musl < 1.2.0 and nix shipped musl >= 1.2.0 which lead to compilation breakage. + So we want a Rust release that is bound to our Nix repository to avoid these problems. + See here for more info: https://musl.libc.org/time64.html + Because Cargo2nix does not support the Rust environment shipped by NixOS, + we emulate the structure of the Rust object created by rustOverlay. + In practise, rustOverlay ships rustc+cargo in a single derivation while + NixOS ships them in separate ones. We reunite them with symlinkJoin. + */ + rustChannel = pkgs.symlinkJoin { + name ="rust-channel"; + paths = [ + pkgs.rustPlatform.rust.rustc + pkgs.rustPlatform.rust.cargo + ]; + }; + + /* + 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 + */ + overrides = 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 + */ + (pkgs.rustBuilder.rustLib.makeOverride { + name = "garage"; + overrideAttrs = drv: { hardeningDisable = [ "pie" ]; }; + }) + + (pkgs.rustBuilder.rustLib.makeOverride { + name = "garage_rpc"; + + /* + [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. + */ + overrideAttrs = drv: + (if git_version != null then { + preConfigure = '' + ${drv.preConfigure or ""} + export GIT_VERSION="${git_version}" + ''; + } else {}); + + /* + [3] 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.). + But we want to ship these additional features when we release Garage. + In the end, we chose to exclude all features from debug builds while putting (all of) them in the release builds. + Currently, the only feature of Garage is kubernetes-discovery from the garage_rpc crate. + */ + overrideArgs = old: { + features = if release then [ "kubernetes-discovery" ] else []; + }; + }) + + ]; + + packageFun = import ../Cargo.nix; + + /* + We compile fully static binaries with musl to simplify deployment on most systems. + When possible, we reactivate PIE hardening (see above). + + Also, if you set the RUSTFLAGS environment variable, the following parameters will + be ignored. + + 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 */ + "aarch64-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static" ]; /* segfault with static-pie */ + "i686-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static" ]; /* segfault with static-pie */ + "x86_64-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static-pie" ]; + }; + +in + /* + The following definition is not elegant as we use a low level function of Cargo2nix + that enables us to pass our custom rustChannel object. We need this low level definition + to pass Nix's Rust toolchains instead of Mozilla's one. + + target is mandatory but must be kept to null to allow cargo2nix to set it to the appropriate value + for each crate. + */ + pkgs.rustBuilder.makePackageSet { + inherit packageFun rustChannel release codegenOpts; + packageOverrides = overrides; + target = null; + + buildRustPackages = pkgs.buildPackages.rustBuilder.makePackageSet { + inherit rustChannel packageFun codegenOpts; + packageOverrides = overrides; + target = null; + }; + } diff --git a/nix/nix.conf b/nix/nix.conf index f3defe69..de2ede71 100644 --- a/nix/nix.conf +++ b/nix/nix.conf @@ -5,3 +5,5 @@ cores = 4 log-lines = 200 filter-syscalls = false sandbox = false +keep-outputs = true +keep-derivations = true