From 91a4e55c066b32249b4e1ed5949aeb06b0dff6ef Mon Sep 17 00:00:00 2001 From: Philipp Mildenberger Date: Fri, 12 May 2023 00:00:21 +0200 Subject: [PATCH 1/7] Symlink cargo build artifacts instead of copying them --- lib/cargoTarpaulin.nix | 2 +- lib/setupHooks/inheritCargoArtifacts.nix | 2 + lib/setupHooks/inheritCargoArtifactsHook.sh | 42 +++++++++++++++------ 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/lib/cargoTarpaulin.nix b/lib/cargoTarpaulin.nix index 5ac007d9..1b094f82 100644 --- a/lib/cargoTarpaulin.nix +++ b/lib/cargoTarpaulin.nix @@ -3,7 +3,7 @@ }: { cargoExtraArgs ? "" -, cargoTarpaulinExtraArgs ? "--skip-clean --out Xml --output-dir $out" +, cargoTarpaulinExtraArgs ? "--out Xml --output-dir $out" , ... }@origArgs: let diff --git a/lib/setupHooks/inheritCargoArtifacts.nix b/lib/setupHooks/inheritCargoArtifacts.nix index afb3d67e..aebdfa86 100644 --- a/lib/setupHooks/inheritCargoArtifacts.nix +++ b/lib/setupHooks/inheritCargoArtifacts.nix @@ -1,8 +1,10 @@ { makeSetupHook +, rsync }: makeSetupHook { name = "inheritCargoArtifactsHook"; + propagatedBuildInputs = [ rsync ]; } ./inheritCargoArtifactsHook.sh diff --git a/lib/setupHooks/inheritCargoArtifactsHook.sh b/lib/setupHooks/inheritCargoArtifactsHook.sh index 2456ef1c..fce3e3ea 100644 --- a/lib/setupHooks/inheritCargoArtifactsHook.sh +++ b/lib/setupHooks/inheritCargoArtifactsHook.sh @@ -24,18 +24,36 @@ inheritCargoArtifacts() { elif [ -d "${preparedArtifacts}" ]; then echo "copying cargo artifacts from ${preparedArtifacts} to ${cargoTargetDir}" - # NB: rustc doesn't like it when artifacts are either symlinks or hardlinks to the store - # (it tries to truncate files instead of unlinking and recreating them) - # so we're forced to do a full copy here :( - # - # Notes: - # - --no-target-directory to avoid nesting (i.e. `./target/target`) - # - preserve timestamps to avoid rebuilding - # - no-preserve ownership (root) so we can make the files writable - cp -r "${preparedArtifacts}" \ - --no-target-directory "${cargoTargetDir}" \ - --preserve=timestamps \ - --no-preserve=ownership + # copy target dir but ignore crate build artifacts + rsync -r --chmod=Du=rwx,Dg=rx,Do=rx --exclude "release/build" --exclude "release/deps" --exclude "*/release/build" --exclude "*/release/deps" "${preparedArtifacts}/" "${cargoTargetDir}/" + + link_build_artifacts() { + local artifacts="$1" + local target="$2" + + if [ -d "${artifacts}/release/deps" ]; then + mkdir -p "${target}/release/deps" + for dep in $(ls "${artifacts}/release/deps"); do + ln -fs "${artifacts}/release/deps/$dep" "${target}/release/deps/$dep" + done + fi + + if [ -d "${artifacts}/release/build" ]; then + mkdir -p "${target}/release/build" + for build in $(ls "${artifacts}/release/build"); do + ln -fs "${artifacts}/release/build/$build" "${target}/release/build/$build" + done + fi + } + + # symlink crate build artifacts + link_build_artifacts "${preparedArtifacts}" "${cargoTargetDir}" + + # for each build target as well + # all other directories are ignored in `link_build_artifacts` + for target in $(ls "${preparedArtifacts}"); do + link_build_artifacts "${preparedArtifacts}/$target" "${cargoTargetDir}/$target" + done # Keep existing permissions (e.g. exectuable), but also make things writable # since the store is read-only and cargo would otherwise choke From c41599656db25662504c561f5c2b381e29621095 Mon Sep 17 00:00:00 2001 From: Philipp Mildenberger Date: Thu, 18 May 2023 23:41:37 +0200 Subject: [PATCH 2/7] Simplify build-artifacts symlinking and support all build profiles instead of just 'release' --- lib/setupHooks/inheritCargoArtifactsHook.sh | 39 +++++---------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/lib/setupHooks/inheritCargoArtifactsHook.sh b/lib/setupHooks/inheritCargoArtifactsHook.sh index fce3e3ea..a55a3109 100644 --- a/lib/setupHooks/inheritCargoArtifactsHook.sh +++ b/lib/setupHooks/inheritCargoArtifactsHook.sh @@ -24,36 +24,15 @@ inheritCargoArtifacts() { elif [ -d "${preparedArtifacts}" ]; then echo "copying cargo artifacts from ${preparedArtifacts} to ${cargoTargetDir}" - # copy target dir but ignore crate build artifacts - rsync -r --chmod=Du=rwx,Dg=rx,Do=rx --exclude "release/build" --exclude "release/deps" --exclude "*/release/build" --exclude "*/release/deps" "${preparedArtifacts}/" "${cargoTargetDir}/" - - link_build_artifacts() { - local artifacts="$1" - local target="$2" - - if [ -d "${artifacts}/release/deps" ]; then - mkdir -p "${target}/release/deps" - for dep in $(ls "${artifacts}/release/deps"); do - ln -fs "${artifacts}/release/deps/$dep" "${target}/release/deps/$dep" - done - fi - - if [ -d "${artifacts}/release/build" ]; then - mkdir -p "${target}/release/build" - for build in $(ls "${artifacts}/release/build"); do - ln -fs "${artifacts}/release/build/$build" "${target}/release/build/$build" - done - fi - } - - # symlink crate build artifacts - link_build_artifacts "${preparedArtifacts}" "${cargoTargetDir}" - - # for each build target as well - # all other directories are ignored in `link_build_artifacts` - for target in $(ls "${preparedArtifacts}"); do - link_build_artifacts "${preparedArtifacts}/$target" "${cargoTargetDir}/$target" - done + # copy target dir but ignore content-addressed build artifacts + rsync -r --chmod=Du=rwx --exclude "*/build/*" --exclude "*/deps/*" --exclude "*/*/build/*" --exclude "*/*/deps/*" "${preparedArtifacts}/" "${cargoTargetDir}/" + + # symlink all remaining content-addressed artifacts + pushd "${cargoTargetDir}" + for d in $(ls -d */{deps,build} */*/{deps,build}); do + ls "${preparedArtifacts}/${d}" | xargs -P 100 -I '##{}##' ln -fs "${preparedArtifacts}/${d}/##{}##" "${d}/##{}##" + done + popd # Keep existing permissions (e.g. exectuable), but also make things writable # since the store is read-only and cargo would otherwise choke From 638ab25a5247dfe1f25b87b6a9ef87b62344a019 Mon Sep 17 00:00:00 2001 From: Ivan Petkov Date: Tue, 6 Jun 2023 20:45:01 -0700 Subject: [PATCH 3/7] Fix build --- lib/mkCargoDerivation.nix | 2 ++ lib/setupHooks/inheritCargoArtifacts.nix | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/mkCargoDerivation.nix b/lib/mkCargoDerivation.nix index 76e35cb6..929acb45 100644 --- a/lib/mkCargoDerivation.nix +++ b/lib/mkCargoDerivation.nix @@ -5,6 +5,7 @@ , crateNameFromCargoToml , inheritCargoArtifactsHook , installCargoArtifactsHook +, rsync , stdenv , vendorCargoDeps , zstd @@ -59,6 +60,7 @@ chosenStdenv.mkDerivation (cleanedArgs // { configureCargoVendoredDepsHook inheritCargoArtifactsHook installCargoArtifactsHook + rsync zstd ]; diff --git a/lib/setupHooks/inheritCargoArtifacts.nix b/lib/setupHooks/inheritCargoArtifacts.nix index aebdfa86..d4a8a8fe 100644 --- a/lib/setupHooks/inheritCargoArtifacts.nix +++ b/lib/setupHooks/inheritCargoArtifacts.nix @@ -5,6 +5,5 @@ makeSetupHook { name = "inheritCargoArtifactsHook"; - propagatedBuildInputs = [ rsync ]; } ./inheritCargoArtifactsHook.sh From 799d60f4bffdb2747ae12848abd20985c73ff8c4 Mon Sep 17 00:00:00 2001 From: Ivan Petkov Date: Mon, 19 Jun 2023 13:06:18 -0700 Subject: [PATCH 4/7] inheritCargoArtifactsHook: symlink dependency rlib and rmeta files --- lib/setupHooks/inheritCargoArtifactsHook.sh | 52 +++++++++++++++------ 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/lib/setupHooks/inheritCargoArtifactsHook.sh b/lib/setupHooks/inheritCargoArtifactsHook.sh index a55a3109..bc9e57a8 100644 --- a/lib/setupHooks/inheritCargoArtifactsHook.sh +++ b/lib/setupHooks/inheritCargoArtifactsHook.sh @@ -24,23 +24,47 @@ inheritCargoArtifacts() { elif [ -d "${preparedArtifacts}" ]; then echo "copying cargo artifacts from ${preparedArtifacts} to ${cargoTargetDir}" - # copy target dir but ignore content-addressed build artifacts - rsync -r --chmod=Du=rwx --exclude "*/build/*" --exclude "*/deps/*" --exclude "*/*/build/*" --exclude "*/*/deps/*" "${preparedArtifacts}/" "${cargoTargetDir}/" + # Dependency .rlib and .rmeta files are content addressed and thus are not written to after + # being built (since changing `Cargo.lock` would rebuild everything anyway), which makes them + # good candidates for symlinking (esp. since they can make up 60-70% of the artifact directory + # on most projects). Thus we ignore them when copying all other artifacts below as we will + # symlink them afterwards. Note that we scope these checks to the `/deps` subdirectory; the + # workspace's own .rlib and .rmeta files appear one directory up (and these may require being + # writable depending on how the actual workspace build is being invoked, so we'll leave them + # alone). + # + # NB: keep the executable bit only if set on the original file + # but make all files writable as sometimes read-only files will make the build choke + # + # NB: cargo also doesn't like it if `.cargo-lock` files remain with a + # timestamp in the distant past so we avoid copying them here + rsync \ + --recursive \ + --links \ + --times \ + --chmod=u+w \ + --executability \ + --exclude 'deps/*.rlib' \ + --exclude 'deps/*.rmeta' \ + --exclude '.cargo-lock' \ + "${preparedArtifacts}/" \ + "${cargoTargetDir}/" - # symlink all remaining content-addressed artifacts - pushd "${cargoTargetDir}" - for d in $(ls -d */{deps,build} */*/{deps,build}); do - ls "${preparedArtifacts}/${d}" | xargs -P 100 -I '##{}##' ln -fs "${preparedArtifacts}/${d}/##{}##" "${d}/##{}##" - done - popd + local linkCandidates=$(mktemp linkCandidatesXXXX.txt) + find "${preparedArtifacts}" \ + '(' -path '*/deps/*.rlib' -or -path '*/deps/*.rmeta' ')' \ + -printf "%P\n" \ + >"${linkCandidates}" - # Keep existing permissions (e.g. exectuable), but also make things writable - # since the store is read-only and cargo would otherwise choke - chmod -R u+w "${cargoTargetDir}" + # Next create any missing directories up front so we can avoid redundant checks later + cat "${linkCandidates}" \ + | xargs --no-run-if-empty -n1 dirname \ + | sort -u \ + | (cd "${cargoTargetDir}"; xargs --no-run-if-empty mkdir -p) - # NB: cargo also doesn't like it if `.cargo-lock` files remain with a - # timestamp in the distant past so we need to delete them here - find "${cargoTargetDir}" -name '.cargo-lock' -delete + # Lastly do the actual symlinking + cat "${linkCandidates}" \ + | xargs -P ${NIX_BUILD_CORES} -I '##{}##' ln -s "${preparedArtifacts}/##{}##" "${cargoTargetDir}/##{}##" else echo unable to copy cargo artifacts, \"${preparedArtifacts}\" looks invalid false From 7be810ce8a972afa9a22ab8c2ee737aaabe0dcb0 Mon Sep 17 00:00:00 2001 From: Ivan Petkov Date: Mon, 19 Jun 2023 13:08:06 -0700 Subject: [PATCH 5/7] Update docs and CHANGELOG --- CHANGELOG.md | 5 +++++ docs/API.md | 1 + 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e421390..d19fd454 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added * Added support for the [Trunk](https://trunkrs.dev) wasm app build tool +### Changed +* `inheritCargoArtifactsHook` will now symlink dependency `.rlib` and `.rmeta` + files. This means that derivations which reuse existing cargo artifacts will + run faster as fewer files (and bytes!) need to be copied around + ### [0.12.1] - 2023-04-10 ### Changed diff --git a/docs/API.md b/docs/API.md index f54f17ec..f343e69a 100644 --- a/docs/API.md +++ b/docs/API.md @@ -927,6 +927,7 @@ hooks: * `configureCargoVendoredDepsHook` * `inheritCargoArtifactsHook` * `installCargoArtifactsHook` +* `rsync` * `zstd` ### `craneLib.mkDummySrc` From 184f52f8d91a65e0bf2f1aa7a9a2194fa9be3e26 Mon Sep 17 00:00:00 2001 From: Ivan Petkov Date: Mon, 19 Jun 2023 13:08:30 -0700 Subject: [PATCH 6/7] Revert unrelated change --- lib/cargoTarpaulin.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cargoTarpaulin.nix b/lib/cargoTarpaulin.nix index 1b094f82..5ac007d9 100644 --- a/lib/cargoTarpaulin.nix +++ b/lib/cargoTarpaulin.nix @@ -3,7 +3,7 @@ }: { cargoExtraArgs ? "" -, cargoTarpaulinExtraArgs ? "--out Xml --output-dir $out" +, cargoTarpaulinExtraArgs ? "--skip-clean --out Xml --output-dir $out" , ... }@origArgs: let From 81a467c1b8c0a32c0f3e284577a76d42580204c8 Mon Sep 17 00:00:00 2001 From: Ivan Petkov Date: Mon, 19 Jun 2023 14:43:50 -0700 Subject: [PATCH 7/7] cargoTarpaulin: do not clean by default --- CHANGELOG.md | 2 ++ docs/API.md | 2 +- lib/cargoTarpaulin.nix | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b77b462..d3857867 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. * `inheritCargoArtifactsHook` will now symlink dependency `.rlib` and `.rmeta` files. This means that derivations which reuse existing cargo artifacts will run faster as fewer files (and bytes!) need to be copied around +* `cargoTarpaulin`'s default `cargoTarpaulinExtraArgs` no longer include + `--skip-clean` ### Changed * **Breaking**: dropped compatibility for Nix versions below 2.13.3 diff --git a/docs/API.md b/docs/API.md index 50504652..c5877d05 100644 --- a/docs/API.md +++ b/docs/API.md @@ -622,7 +622,7 @@ Except where noted below, all derivation attributes are delegated to - Default value: `""` * `cargoTarpaulinExtraArgs`: additional flags to be passed in the cargo tarpaulin invocation - - Default value: `"--skip-clean --out Xml --output-dir $out"` + - Default value: `"--out Xml --output-dir $out"` #### Native build dependencies The `cargo-tarpaulin` package is automatically appended as a native build input to any diff --git a/lib/cargoTarpaulin.nix b/lib/cargoTarpaulin.nix index 5ac007d9..1b094f82 100644 --- a/lib/cargoTarpaulin.nix +++ b/lib/cargoTarpaulin.nix @@ -3,7 +3,7 @@ }: { cargoExtraArgs ? "" -, cargoTarpaulinExtraArgs ? "--skip-clean --out Xml --output-dir $out" +, cargoTarpaulinExtraArgs ? "--out Xml --output-dir $out" , ... }@origArgs: let