2024-11-14 23:14:20 +01:00
|
|
|
{
|
|
|
|
lib,
|
|
|
|
runCommand,
|
|
|
|
writeShellScript,
|
2024-12-07 16:31:33 +01:00
|
|
|
writeText,
|
2024-11-14 23:14:20 +01:00
|
|
|
linkFarm,
|
|
|
|
time,
|
|
|
|
procps,
|
2024-11-21 09:08:25 +01:00
|
|
|
nixVersions,
|
2024-11-14 23:14:20 +01:00
|
|
|
jq,
|
|
|
|
sta,
|
|
|
|
}:
|
|
|
|
|
|
|
|
let
|
|
|
|
nixpkgs =
|
|
|
|
with lib.fileset;
|
|
|
|
toSource {
|
|
|
|
root = ../..;
|
|
|
|
fileset = unions (
|
|
|
|
map (lib.path.append ../..) [
|
|
|
|
"default.nix"
|
|
|
|
"doc"
|
|
|
|
"lib"
|
|
|
|
"maintainers"
|
|
|
|
"nixos"
|
|
|
|
"pkgs"
|
|
|
|
".version"
|
|
|
|
"ci/supportedSystems.nix"
|
|
|
|
]
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2024-11-21 09:08:25 +01:00
|
|
|
nix = nixVersions.nix_2_24;
|
|
|
|
|
2024-11-14 23:14:20 +01:00
|
|
|
supportedSystems = import ../supportedSystems.nix;
|
|
|
|
|
|
|
|
attrpathsSuperset =
|
|
|
|
runCommand "attrpaths-superset.json"
|
|
|
|
{
|
|
|
|
src = nixpkgs;
|
|
|
|
nativeBuildInputs = [
|
|
|
|
nix
|
|
|
|
time
|
|
|
|
];
|
|
|
|
env.supportedSystems = builtins.toJSON supportedSystems;
|
|
|
|
passAsFile = [ "supportedSystems" ];
|
|
|
|
}
|
|
|
|
''
|
|
|
|
export NIX_STATE_DIR=$(mktemp -d)
|
|
|
|
mkdir $out
|
|
|
|
export GC_INITIAL_HEAP_SIZE=4g
|
2024-12-12 12:53:37 +01:00
|
|
|
command time -f "Attribute eval done [%MKB max resident, %Es elapsed] %C" \
|
2024-11-14 23:14:20 +01:00
|
|
|
nix-instantiate --eval --strict --json --show-trace \
|
2024-11-29 21:31:24 +01:00
|
|
|
"$src/pkgs/top-level/release-attrpaths-superset.nix" \
|
|
|
|
-A paths \
|
|
|
|
-I "$src" \
|
|
|
|
--option restrict-eval true \
|
|
|
|
--option allow-import-from-derivation false \
|
|
|
|
--arg enableWarnings false > $out/paths.json
|
2024-11-14 23:14:20 +01:00
|
|
|
mv "$supportedSystemsPath" $out/systems.json
|
|
|
|
'';
|
|
|
|
|
|
|
|
singleSystem =
|
|
|
|
{
|
|
|
|
# The system to evaluate.
|
|
|
|
# Note that this is intentionally not called `system`,
|
|
|
|
# because `--argstr system` would only be passed to the ci/default.nix file!
|
|
|
|
evalSystem,
|
|
|
|
# The path to the `paths.json` file from `attrpathsSuperset`
|
|
|
|
attrpathFile,
|
|
|
|
# The number of attributes per chunk, see ./README.md for more info.
|
|
|
|
chunkSize,
|
|
|
|
checkMeta ? true,
|
|
|
|
includeBroken ? true,
|
|
|
|
# Whether to just evaluate a single chunk for quick testing
|
|
|
|
quickTest ? false,
|
|
|
|
}:
|
|
|
|
let
|
|
|
|
singleChunk = writeShellScript "single-chunk" ''
|
|
|
|
set -euo pipefail
|
|
|
|
chunkSize=$1
|
|
|
|
myChunk=$2
|
|
|
|
system=$3
|
|
|
|
outputDir=$4
|
|
|
|
|
|
|
|
export NIX_SHOW_STATS=1
|
|
|
|
export NIX_SHOW_STATS_PATH="$outputDir/stats/$myChunk"
|
|
|
|
echo "Chunk $myChunk on $system start"
|
|
|
|
set +e
|
2025-02-11 21:12:31 +01:00
|
|
|
command time -o "$outputDir/timestats/$myChunk" \
|
|
|
|
-f "Chunk $myChunk on $system done [%MKB max resident, %Es elapsed] %C" \
|
2024-11-14 23:14:20 +01:00
|
|
|
nix-env -f "${nixpkgs}/pkgs/top-level/release-attrpaths-parallel.nix" \
|
ci/eval: make eval for non-native platforms less incorrect
We commonly use platform-dependent conditional patterns like
`lib.meta.availableOn stdenv.hostPlatform` and `stdenv.hostPlatform.isLinux`
to enable different features in a given derivation or to evaluate
completely different derivations based on the platform.
For example, source builds of a given derivation may only be available
on linux but not on darwin. The use of such conditionals allow us to
fall back to patched binaries on darwin instead.
In `chromedriver` (pkgs/development/tools/selenium/chromedriver/default.nix), we use
~~~nix
if lib.meta.availableOn stdenv.hostPlatform chromium then
callPackage ./source.nix { }
else
callPackage ./binary.nix { }
~~~
To provide some context, `chromedriver` source builds are based on `chromium.mkDerivation`
and `chromium` is limited to `lib.platforms.linux`.
Based on the same `chromium.mkDerivation`, we also do source builds for
`electron` (pkgs/top-level/all-packages.nix):
~~~nix
electron_33 = if lib.meta.availableOn stdenv.hostPlatform electron-source.electron_33 then electron-source.electron_33 else electron_33-bin;
electron_34 = electron_34-bin;
electron = electron_34;
~~~
And finally, the top-level `jdk` (Java) attribute has a lot of
indirection, but eventually also boils down to `stdenv.hostPlatform.isLinux`
for source builds and binaries for x86_64-darwin and aarch64-darwin.
A surprising amount of electron and jdk consumers use variations of
`meta.platforms = electron.meta.platforms` in their own meta block.
Due to internal implementation details, the conditionals in those
top-level attributes like `chromedriver`, `electron` and `jdk` are
evaluated based on the value from `builtins.currentSystem` and not the
system passed to `import <nixpkgs> { }`.
This then causes `chromedriver`, `electron`, `jdk` and all dependents
that inherit those `meta.platforms` to appear only available on linux
despite also being available on darwin. Hydra is affected similarly, but
it's a lot more nuanced and in practice not actually *that* bad.
The addition of `--eval-system` ensures that `builtins.currentSystem`
matches the requested platform.
As a bonus, this also fixes the store paths of an impure test that
should probably be made pure:
~~~diff
@@ -885069,13 +886119,13 @@
"out": "/nix/store/lb2500hc69czy4sfga9mbh2k679cr1rp-test-compressDrv"
},
"tests.config.allowPkgsInPermittedInsecurePackages.aarch64-darwin": {
- "out": "/nix/store/0l5h8svrpzwymq35mnpvx82gyc7nf8s4-hello-2.12.1"
+ "out": "/nix/store/v1zjb688mp4y2132b6chii43d5kkxnpa-hello-2.12.1"
},
"tests.config.allowPkgsInPermittedInsecurePackages.aarch64-linux": {
- "out": "/nix/store/0l5h8svrpzwymq35mnpvx82gyc7nf8s4-hello-2.12.1"
+ "out": "/nix/store/hb21z2zdk03dwygsw5lvpa8zc3fbr500-hello-2.12.1"
},
"tests.config.allowPkgsInPermittedInsecurePackages.x86_64-darwin": {
- "out": "/nix/store/0l5h8svrpzwymq35mnpvx82gyc7nf8s4-hello-2.12.1"
+ "out": "/nix/store/gljdqsf0mxv1j8zb04phx9ws09pp7z3l-hello-2.12.1"
},
"tests.config.allowPkgsInPermittedInsecurePackages.x86_64-linux": {
"out": "/nix/store/0l5h8svrpzwymq35mnpvx82gyc7nf8s4-hello-2.12.1"
~~~
Diff stats between two full evals based on 75c8548d8118c31509b89ffd7df6c322b94017dd
with and without this fix on x86_64-linux:
~~~bash
# git diff --no-index --stat /nix/store/659l3xp78255wx7abbahggsnrlj3a1la-combined-result/outpaths.json /nix/store/4fhlq4g5qa65cxbibskq9pma40zigrx7-combined-result/outpaths.json
/nix/store/{659l3xp78255wx7abbahggsnrlj3a1la-combined-result => 4fhlq4g5qa65cxbibskq9pma40zigrx7-combined-result}/outpaths.json | 1416 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 1405 insertions(+), 11 deletions(-)
~~~
The full diff is available as a gist at <https://gist.github.com/emilylange/d40c50031fc332bbcca133ad56d224f6>.
When we added `electron_34` only as binary instead of the usual source
on linux with binary fallback in cfed9a19cbc98d12c0167f69238673f4ed89f798
and made the unversioned `electron` top-level point to the newly added
`electron_34` instead of `electron_33`, the GitHub workflow suddenly
reported 20 new packages. Of those 20 reported packages, 17 where
false-positives caused by dropping the wrongly evaluated conditional.
2025-02-02 21:04:08 +01:00
|
|
|
--eval-system "$system" \
|
2024-11-29 21:31:24 +01:00
|
|
|
--option restrict-eval true \
|
|
|
|
--option allow-import-from-derivation false \
|
2024-11-14 23:14:20 +01:00
|
|
|
--query --available \
|
|
|
|
--no-name --attr-path --out-path \
|
|
|
|
--show-trace \
|
|
|
|
--arg chunkSize "$chunkSize" \
|
|
|
|
--arg myChunk "$myChunk" \
|
|
|
|
--arg attrpathFile "${attrpathFile}" \
|
|
|
|
--arg systems "[ \"$system\" ]" \
|
|
|
|
--arg checkMeta ${lib.boolToString checkMeta} \
|
|
|
|
--arg includeBroken ${lib.boolToString includeBroken} \
|
2024-11-29 21:31:24 +01:00
|
|
|
-I ${nixpkgs} \
|
|
|
|
-I ${attrpathFile} \
|
2025-02-11 21:12:31 +01:00
|
|
|
> "$outputDir/result/$myChunk" \
|
|
|
|
2> "$outputDir/stderr/$myChunk"
|
2024-11-14 23:14:20 +01:00
|
|
|
exitCode=$?
|
|
|
|
set -e
|
2025-02-11 21:12:31 +01:00
|
|
|
cat "$outputDir/stderr/$myChunk"
|
|
|
|
cat "$outputDir/timestats/$myChunk"
|
2024-11-14 23:14:20 +01:00
|
|
|
if (( exitCode != 0 )); then
|
|
|
|
echo "Evaluation failed with exit code $exitCode"
|
|
|
|
# This immediately halts all xargs processes
|
|
|
|
kill $PPID
|
2025-02-11 21:16:58 +01:00
|
|
|
elif [[ -s "$outputDir/stderr/$myChunk" ]]; then
|
|
|
|
echo "Nixpkgs on $system evaluated with warnings, aborting"
|
|
|
|
kill $PPID
|
2024-11-14 23:14:20 +01:00
|
|
|
fi
|
|
|
|
'';
|
|
|
|
in
|
|
|
|
runCommand "nixpkgs-eval-${evalSystem}"
|
|
|
|
{
|
|
|
|
nativeBuildInputs = [
|
|
|
|
nix
|
|
|
|
time
|
|
|
|
procps
|
|
|
|
jq
|
|
|
|
];
|
|
|
|
env = {
|
|
|
|
inherit evalSystem chunkSize;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
''
|
|
|
|
export NIX_STATE_DIR=$(mktemp -d)
|
|
|
|
nix-store --init
|
|
|
|
|
|
|
|
echo "System: $evalSystem"
|
|
|
|
cores=$NIX_BUILD_CORES
|
|
|
|
echo "Cores: $cores"
|
|
|
|
attrCount=$(jq length "${attrpathFile}")
|
|
|
|
echo "Attribute count: $attrCount"
|
|
|
|
echo "Chunk size: $chunkSize"
|
|
|
|
# Same as `attrCount / chunkSize` but rounded up
|
|
|
|
chunkCount=$(( (attrCount - 1) / chunkSize + 1 ))
|
|
|
|
echo "Chunk count: $chunkCount"
|
|
|
|
|
|
|
|
mkdir $out
|
|
|
|
|
|
|
|
# Record and print stats on free memory and swap in the background
|
|
|
|
(
|
|
|
|
while true; do
|
|
|
|
availMemory=$(free -b | grep Mem | awk '{print $7}')
|
|
|
|
freeSwap=$(free -b | grep Swap | awk '{print $4}')
|
|
|
|
echo "Available memory: $(( availMemory / 1024 / 1024 )) MiB, free swap: $(( freeSwap / 1024 / 1024 )) MiB"
|
|
|
|
|
|
|
|
if [[ ! -f "$out/min-avail-memory" ]] || (( availMemory < $(<$out/min-avail-memory) )); then
|
|
|
|
echo "$availMemory" > $out/min-avail-memory
|
|
|
|
fi
|
|
|
|
if [[ ! -f $out/min-free-swap ]] || (( availMemory < $(<$out/min-free-swap) )); then
|
|
|
|
echo "$freeSwap" > $out/min-free-swap
|
|
|
|
fi
|
|
|
|
sleep 4
|
|
|
|
done
|
|
|
|
) &
|
|
|
|
|
|
|
|
seq_end=$(( chunkCount - 1 ))
|
|
|
|
|
|
|
|
${lib.optionalString quickTest ''
|
|
|
|
seq_end=0
|
|
|
|
''}
|
|
|
|
|
|
|
|
chunkOutputDir=$(mktemp -d)
|
2025-02-11 21:12:31 +01:00
|
|
|
mkdir "$chunkOutputDir"/{result,stats,timestats,stderr}
|
2024-11-14 23:14:20 +01:00
|
|
|
|
|
|
|
seq -w 0 "$seq_end" |
|
|
|
|
command time -f "%e" -o "$out/total-time" \
|
|
|
|
xargs -I{} -P"$cores" \
|
|
|
|
${singleChunk} "$chunkSize" {} "$evalSystem" "$chunkOutputDir"
|
|
|
|
|
2025-04-18 13:22:09 +02:00
|
|
|
cp -r "$chunkOutputDir"/stats $out/stats-by-chunk
|
|
|
|
|
2024-11-14 23:14:20 +01:00
|
|
|
if (( chunkSize * chunkCount != attrCount )); then
|
|
|
|
# A final incomplete chunk would mess up the stats, don't include it
|
|
|
|
rm "$chunkOutputDir"/stats/"$seq_end"
|
|
|
|
fi
|
|
|
|
|
|
|
|
# Make sure the glob doesn't break when there's no files
|
|
|
|
shopt -s nullglob
|
|
|
|
cat "$chunkOutputDir"/result/* > $out/paths
|
|
|
|
cat "$chunkOutputDir"/stats/* > $out/stats.jsonstream
|
|
|
|
'';
|
|
|
|
|
|
|
|
combine =
|
|
|
|
{
|
|
|
|
resultsDir,
|
|
|
|
}:
|
|
|
|
runCommand "combined-result"
|
|
|
|
{
|
|
|
|
nativeBuildInputs = [
|
|
|
|
jq
|
|
|
|
sta
|
|
|
|
];
|
|
|
|
}
|
|
|
|
''
|
|
|
|
mkdir -p $out
|
|
|
|
|
|
|
|
# Transform output paths to JSON
|
|
|
|
cat ${resultsDir}/*/paths |
|
|
|
|
jq --sort-keys --raw-input --slurp '
|
|
|
|
split("\n") |
|
|
|
|
map(select(. != "") | split(" ") | map(select(. != ""))) |
|
|
|
|
map(
|
|
|
|
{
|
|
|
|
key: .[0],
|
|
|
|
value: .[1] | split(";") | map(split("=") |
|
|
|
|
if length == 1 then
|
|
|
|
{ key: "out", value: .[0] }
|
|
|
|
else
|
|
|
|
{ key: .[0], value: .[1] }
|
|
|
|
end) | from_entries}
|
|
|
|
) | from_entries
|
|
|
|
' > $out/outpaths.json
|
|
|
|
|
|
|
|
# Computes min, mean, error, etc. for a list of values and outputs a JSON from that
|
|
|
|
statistics() {
|
|
|
|
local stat=$1
|
|
|
|
sta --transpose |
|
|
|
|
jq --raw-input --argjson stat "$stat" -n '
|
|
|
|
[
|
|
|
|
inputs |
|
|
|
|
split("\t") |
|
|
|
|
{ key: .[0], value: (.[1] | fromjson) }
|
|
|
|
] |
|
|
|
|
from_entries |
|
|
|
|
{
|
|
|
|
key: ($stat | join(".")),
|
|
|
|
value: .
|
|
|
|
}'
|
|
|
|
}
|
|
|
|
|
|
|
|
# Gets all available number stats (without .sizes because those are constant and not interesting)
|
|
|
|
readarray -t stats < <(jq -cs '.[0] | del(.sizes) | paths(type == "number")' ${resultsDir}/*/stats.jsonstream)
|
|
|
|
|
|
|
|
# Combines the statistics from all evaluations
|
|
|
|
{
|
|
|
|
echo "{ \"key\": \"minAvailMemory\", \"value\": $(cat ${resultsDir}/*/min-avail-memory | sta --brief --min) }"
|
|
|
|
echo "{ \"key\": \"minFreeSwap\", \"value\": $(cat ${resultsDir}/*/min-free-swap | sta --brief --min) }"
|
|
|
|
cat ${resultsDir}/*/total-time | statistics '["totalTime"]'
|
|
|
|
for stat in "''${stats[@]}"; do
|
|
|
|
cat ${resultsDir}/*/stats.jsonstream |
|
|
|
|
jq --argjson stat "$stat" 'getpath($stat)' |
|
|
|
|
statistics "$stat"
|
|
|
|
done
|
|
|
|
} |
|
|
|
|
jq -s from_entries > $out/stats.json
|
2025-04-18 13:22:09 +02:00
|
|
|
|
|
|
|
mkdir -p $out/stats
|
|
|
|
|
|
|
|
for d in ${resultsDir}/*; do
|
|
|
|
cp -r "$d"/stats-by-chunk $out/stats/$(basename "$d")
|
|
|
|
done
|
2024-11-14 23:14:20 +01:00
|
|
|
'';
|
|
|
|
|
2024-12-07 16:31:33 +01:00
|
|
|
compare = import ./compare {
|
|
|
|
inherit
|
|
|
|
lib
|
|
|
|
jq
|
|
|
|
runCommand
|
|
|
|
writeText
|
|
|
|
supportedSystems
|
|
|
|
;
|
|
|
|
};
|
2024-11-28 00:04:17 +01:00
|
|
|
|
2024-11-14 23:14:20 +01:00
|
|
|
full =
|
|
|
|
{
|
2024-12-14 21:32:34 +01:00
|
|
|
# Whether to evaluate on a specific set of systems, by default all are evaluated
|
|
|
|
evalSystems ? if quickTest then [ "x86_64-linux" ] else supportedSystems,
|
2024-11-14 23:14:20 +01:00
|
|
|
# The number of attributes per chunk, see ./README.md for more info.
|
|
|
|
chunkSize,
|
|
|
|
quickTest ? false,
|
|
|
|
}:
|
|
|
|
let
|
|
|
|
results = linkFarm "results" (
|
|
|
|
map (evalSystem: {
|
|
|
|
name = evalSystem;
|
|
|
|
path = singleSystem {
|
|
|
|
inherit quickTest evalSystem chunkSize;
|
|
|
|
attrpathFile = attrpathsSuperset + "/paths.json";
|
|
|
|
};
|
2024-12-14 21:32:34 +01:00
|
|
|
}) evalSystems
|
2024-11-14 23:14:20 +01:00
|
|
|
);
|
|
|
|
in
|
|
|
|
combine {
|
|
|
|
resultsDir = results;
|
|
|
|
};
|
|
|
|
|
|
|
|
in
|
|
|
|
{
|
|
|
|
inherit
|
|
|
|
attrpathsSuperset
|
|
|
|
singleSystem
|
|
|
|
combine
|
2024-11-28 00:04:17 +01:00
|
|
|
compare
|
2024-11-14 23:14:20 +01:00
|
|
|
# The above three are used by separate VMs in a GitHub workflow,
|
|
|
|
# while the below is intended for testing on a single local machine
|
|
|
|
full
|
|
|
|
;
|
|
|
|
}
|