Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
1# Evaluates all the accessible paths in nixpkgs. 2# *This only builds on Linux* since it requires the Linux sandbox isolation to 3# be able to write in various places while evaluating inside the sandbox. 4# 5# This file is used by nixpkgs CI (see .github/workflows/eval.yml) as well as 6# being used directly as an entry point in Lix's CI (in `flake.nix` in the Lix 7# repo). 8# 9# If you know you are doing a breaking API change, please ping the nixpkgs CI 10# maintainers and the Lix maintainers (`nix eval -f . lib.teams.lix`). 11{ 12 callPackage, 13 lib, 14 runCommand, 15 writeShellScript, 16 symlinkJoin, 17 busybox, 18 jq, 19 nix, 20}: 21 22let 23 nixpkgs = 24 with lib.fileset; 25 toSource { 26 root = ../..; 27 fileset = unions ( 28 map (lib.path.append ../..) [ 29 "default.nix" 30 "doc" 31 "lib" 32 "maintainers" 33 "nixos" 34 "pkgs" 35 ".version" 36 "ci/supportedSystems.json" 37 ] 38 ); 39 }; 40 41 supportedSystems = builtins.fromJSON (builtins.readFile ../supportedSystems.json); 42 43 attrpathsSuperset = 44 { 45 evalSystem, 46 }: 47 runCommand "attrpaths-superset.json" 48 { 49 src = nixpkgs; 50 # Don't depend on -dev outputs to reduce closure size for CI. 51 nativeBuildInputs = map lib.getBin [ 52 busybox 53 nix 54 ]; 55 } 56 '' 57 export NIX_STATE_DIR=$(mktemp -d) 58 mkdir $out 59 export GC_INITIAL_HEAP_SIZE=4g 60 command time -f "Attribute eval done [%MKB max resident, %Es elapsed] %C" \ 61 nix-instantiate --eval --strict --json --show-trace \ 62 "$src/pkgs/top-level/release-attrpaths-superset.nix" \ 63 -A paths \ 64 -I "$src" \ 65 --option restrict-eval true \ 66 --option allow-import-from-derivation false \ 67 --option eval-system "${evalSystem}" > $out/paths.json 68 ''; 69 70 singleSystem = 71 { 72 # The system to evaluate. 73 # Note that this is intentionally not called `system`, 74 # because `--argstr system` would only be passed to the ci/default.nix file! 75 evalSystem ? builtins.currentSystem, 76 # The path to the `paths.json` file from `attrpathsSuperset` 77 attrpathFile ? "${attrpathsSuperset { inherit evalSystem; }}/paths.json", 78 # The number of attributes per chunk, see ./README.md for more info. 79 chunkSize ? 5000, 80 checkMeta ? true, 81 82 # Don't try to eval packages marked as broken. 83 includeBroken ? false, 84 # Whether to just evaluate a single chunk for quick testing 85 quickTest ? false, 86 }: 87 let 88 singleChunk = writeShellScript "single-chunk" '' 89 set -euo pipefail 90 chunkSize=$1 91 myChunk=$2 92 system=$3 93 outputDir=$4 94 95 export NIX_SHOW_STATS=1 96 export NIX_SHOW_STATS_PATH="$outputDir/stats/$myChunk" 97 echo "Chunk $myChunk on $system start" 98 set +e 99 command time -o "$outputDir/timestats/$myChunk" \ 100 -f "Chunk $myChunk on $system done [%MKB max resident, %Es elapsed] %C" \ 101 nix-env -f "${nixpkgs}/pkgs/top-level/release-outpaths-parallel.nix" \ 102 --eval-system "$system" \ 103 --option restrict-eval true \ 104 --option allow-import-from-derivation false \ 105 --query --available \ 106 --out-path --json \ 107 --show-trace \ 108 --arg chunkSize "$chunkSize" \ 109 --arg myChunk "$myChunk" \ 110 --arg attrpathFile "${attrpathFile}" \ 111 --arg systems "[ \"$system\" ]" \ 112 --arg checkMeta ${lib.boolToString checkMeta} \ 113 --arg includeBroken ${lib.boolToString includeBroken} \ 114 -I ${nixpkgs} \ 115 -I ${attrpathFile} \ 116 > "$outputDir/result/$myChunk" \ 117 2> "$outputDir/stderr/$myChunk" 118 exitCode=$? 119 set -e 120 cat "$outputDir/stderr/$myChunk" 121 cat "$outputDir/timestats/$myChunk" 122 if (( exitCode != 0 )); then 123 echo "Evaluation failed with exit code $exitCode" 124 # This immediately halts all xargs processes 125 kill $PPID 126 elif [[ -s "$outputDir/stderr/$myChunk" ]]; then 127 echo "Nixpkgs on $system evaluated with warnings, aborting" 128 kill $PPID 129 fi 130 ''; 131 in 132 runCommand "nixpkgs-eval-${evalSystem}" 133 { 134 # Don't depend on -dev outputs to reduce closure size for CI. 135 nativeBuildInputs = map lib.getBin [ 136 busybox 137 jq 138 nix 139 ]; 140 env = { 141 inherit evalSystem chunkSize; 142 }; 143 } 144 '' 145 export NIX_STATE_DIR=$(mktemp -d) 146 nix-store --init 147 148 echo "System: $evalSystem" 149 cores=$NIX_BUILD_CORES 150 echo "Cores: $cores" 151 attrCount=$(jq length "${attrpathFile}") 152 echo "Attribute count: $attrCount" 153 echo "Chunk size: $chunkSize" 154 # Same as `attrCount / chunkSize` but rounded up 155 chunkCount=$(( (attrCount - 1) / chunkSize + 1 )) 156 echo "Chunk count: $chunkCount" 157 158 mkdir -p $out/${evalSystem} 159 160 # Record and print stats on free memory and swap in the background 161 ( 162 while true; do 163 availMemory=$(free -m | grep Mem | awk '{print $7}') 164 freeSwap=$(free -m | grep Swap | awk '{print $4}') 165 echo "Available memory: $(( availMemory )) MiB, free swap: $(( freeSwap )) MiB" 166 167 if [[ ! -f "$out/${evalSystem}/min-avail-memory" ]] || (( availMemory < $(<$out/${evalSystem}/min-avail-memory) )); then 168 echo "$availMemory" > $out/${evalSystem}/min-avail-memory 169 fi 170 if [[ ! -f $out/${evalSystem}/min-free-swap ]] || (( freeSwap < $(<$out/${evalSystem}/min-free-swap) )); then 171 echo "$freeSwap" > $out/${evalSystem}/min-free-swap 172 fi 173 sleep 4 174 done 175 ) & 176 177 seq_end=$(( chunkCount - 1 )) 178 179 ${lib.optionalString quickTest '' 180 seq_end=0 181 ''} 182 183 chunkOutputDir=$(mktemp -d) 184 mkdir "$chunkOutputDir"/{result,stats,timestats,stderr} 185 186 seq -w 0 "$seq_end" | 187 command time -f "%e" -o "$out/${evalSystem}/total-time" \ 188 xargs -I{} -P"$cores" \ 189 ${singleChunk} "$chunkSize" {} "$evalSystem" "$chunkOutputDir" 190 191 cp -r "$chunkOutputDir"/stats $out/${evalSystem}/stats-by-chunk 192 193 if (( chunkSize * chunkCount != attrCount )); then 194 # A final incomplete chunk would mess up the stats, don't include it 195 rm "$chunkOutputDir"/stats/"$seq_end" 196 fi 197 198 cat "$chunkOutputDir"/result/* | jq -s 'add | map_values(.outputs)' > $out/${evalSystem}/paths.json 199 ''; 200 201 diff = callPackage ./diff.nix { }; 202 203 combine = 204 { 205 diffDir, 206 }: 207 runCommand "combined-eval" 208 { 209 # Don't depend on -dev outputs to reduce closure size for CI. 210 nativeBuildInputs = map lib.getBin [ 211 jq 212 ]; 213 } 214 '' 215 mkdir -p $out 216 217 # Combine output paths from all systems 218 cat ${diffDir}/*/diff.json | jq -s ' 219 reduce .[] as $item ({}; { 220 added: (.added + $item.added), 221 changed: (.changed + $item.changed), 222 removed: (.removed + $item.removed), 223 rebuilds: (.rebuilds + $item.rebuilds) 224 }) 225 ' > $out/combined-diff.json 226 227 mkdir -p $out/before/stats 228 for d in ${diffDir}/before/*; do 229 cp -r "$d"/stats-by-chunk $out/before/stats/$(basename "$d") 230 done 231 232 mkdir -p $out/after/stats 233 for d in ${diffDir}/after/*; do 234 cp -r "$d"/stats-by-chunk $out/after/stats/$(basename "$d") 235 done 236 ''; 237 238 compare = callPackage ./compare { }; 239 240 full = 241 { 242 # Whether to evaluate on a specific set of systems, by default all are evaluated 243 evalSystems ? if quickTest then [ "x86_64-linux" ] else supportedSystems, 244 # The number of attributes per chunk, see ./README.md for more info. 245 chunkSize, 246 quickTest ? false, 247 }: 248 let 249 diffs = symlinkJoin { 250 name = "diffs"; 251 paths = map ( 252 evalSystem: 253 let 254 eval = singleSystem { 255 inherit quickTest evalSystem chunkSize; 256 }; 257 in 258 diff { 259 inherit evalSystem; 260 # Local "full" evaluation doesn't do a real diff. 261 beforeDir = eval; 262 afterDir = eval; 263 } 264 ) evalSystems; 265 }; 266 in 267 combine { 268 diffDir = diffs; 269 }; 270 271in 272{ 273 inherit 274 attrpathsSuperset 275 singleSystem 276 diff 277 combine 278 compare 279 # The above three are used by separate VMs in a GitHub workflow, 280 # while the below is intended for testing on a single local machine 281 full 282 ; 283}