Skip to content

Commit f266a5a

Browse files
authored
[Backport release-26.05] ci/eval: Allow disallowed attrs to depend on other disallowed attrs (#537612)
2 parents f23654d + ea21df9 commit f266a5a

3 files changed

Lines changed: 80 additions & 47 deletions

File tree

ci/eval/default.nix

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ let
100100
myChunk=$2
101101
system=$3
102102
outputDir=$4
103+
preEvalFile=$5
103104
104105
# Default is 5, higher values effectively disable the warning.
105106
# This randomly breaks Eval.
@@ -121,12 +122,12 @@ let
121122
--show-trace \
122123
--arg chunkSize "$chunkSize" \
123124
--arg myChunk "$myChunk" \
124-
--arg preEvalFile "${preEvalFile}" \
125+
--arg preEvalFile "$preEvalFile" \
125126
--arg systems "[ \"$system\" ]" \
126127
--arg includeBroken ${lib.boolToString includeBroken} \
127128
--argstr extraNixpkgsConfigJson ${lib.escapeShellArg (builtins.toJSON extraNixpkgsConfig)} \
128129
-I ${nixpkgs} \
129-
-I ${preEvalFile} \
130+
-I "$preEvalFile" \
130131
> "$outputDir/result/$myChunk" \
131132
2> "$outputDir/stderr/$myChunk"
132133
exitCode=$?
@@ -164,12 +165,6 @@ let
164165
echo "System: $evalSystem"
165166
cores=$NIX_BUILD_CORES
166167
echo "Cores: $cores"
167-
attrCount=$(jq '.paths | length' "${preEvalFile}")
168-
echo "Attribute count: $attrCount"
169-
echo "Chunk size: $chunkSize"
170-
# Same as `attrCount / chunkSize` but rounded up
171-
chunkCount=$(( (attrCount - 1) / chunkSize + 1 ))
172-
echo "Chunk count: $chunkCount"
173168
174169
mkdir -p $out/${evalSystem}
175170
@@ -190,29 +185,61 @@ let
190185
done
191186
) &
192187
193-
seq_end=$(( chunkCount - 1 ))
188+
chunkedEval() {
189+
local chunkOutputDir=$1
190+
local preEvalFile=$2
194191
195-
${lib.optionalString quickTest ''
196-
seq_end=0
197-
''}
192+
local attrCount=$(jq '.paths | length' "$preEvalFile")
193+
echo "Attribute count: $attrCount"
194+
echo "Chunk size: $chunkSize"
195+
# Same as `attrCount / chunkSize` but rounded up
196+
local chunkCount=$(( (attrCount - 1) / chunkSize + 1 ))
197+
echo "Chunk count: $chunkCount"
198198
199-
chunkOutputDir=$(mktemp -d)
200-
mkdir "$chunkOutputDir"/{result,stats,timestats,stderr}
199+
local seq_end=$(( chunkCount - 1 ))
200+
${lib.optionalString quickTest ''
201+
seq_end=0
202+
''}
201203
202-
seq -w 0 "$seq_end" |
203-
command time -f "%e" -o "$out/${evalSystem}/total-time" \
204-
xargs -I{} -P"$cores" \
205-
${singleChunk} "$chunkSize" {} "$evalSystem" "$chunkOutputDir"
204+
mkdir -p "$chunkOutputDir"/{result,stats,timestats,stderr}
206205
207-
cp -r "$chunkOutputDir"/stats $out/${evalSystem}/stats-by-chunk
206+
seq -w 0 "$seq_end" |
207+
xargs -I{} -P"$cores" \
208+
${singleChunk} "$chunkSize" {} "$evalSystem" "$chunkOutputDir" "$preEvalFile"
208209
209-
if (( chunkSize * chunkCount != attrCount )); then
210-
# A final incomplete chunk would mess up the stats, don't include it
211-
rm "$chunkOutputDir"/stats/"$seq_end"
212-
fi
210+
if (( chunkSize * chunkCount != attrCount )); then
211+
# A final incomplete chunk would mess up the stats, don't include it
212+
rm "$chunkOutputDir"/stats/"$seq_end"
213+
fi
214+
}
215+
216+
chunkOutputDirs=$(mktemp -d)
217+
218+
# Preparation for the second eval
219+
disallowedAttributesPreEvalFile=$(mktemp)
220+
jq '{
221+
paths: (.attrPathsDisallowedForInternalUse | map(.attrPath)),
222+
attrPathsDisallowedForInternalUse: []
223+
}' ${preEvalFile} > "$disallowedAttributesPreEvalFile"
224+
225+
startEpoch=$(date +%s)
226+
227+
# The first eval evaluates only attributes that are not disallowed for internal Nixpkgs use, ensuring that they don't depend on disallowed attributes
228+
# Because the first eval doesn't evaluate the disallowed attributes themselves, but we still want to check that they don't fail evaluation, we evaluate them separately in a second eval
229+
# The reason we need two evals is because we want disallowed attributes to be able to depend on other disallowed attributes, which inherently needs a separate Nixpkgs instantiation
230+
# And while we could interleave that instantiation into a single eval, that would ~double memory usage for all chunks, while doing it separately doesn't
231+
echo "Evaluating the internally allowed attributes"
232+
chunkedEval "$chunkOutputDirs"/allowed ${preEvalFile}
233+
echo "Evaluating the internally disallowed attributes"
234+
chunkedEval "$chunkOutputDirs"/disallowed "$disallowedAttributesPreEvalFile"
235+
236+
echo $(( $(date +%s) - startEpoch )) > "$out/${evalSystem}/total-time"
237+
238+
# We only use the stats from the allowed attrs eval, because the disallowed attrs are generally not even a full chunk
239+
cp -r "$chunkOutputDirs"/allowed/stats $out/${evalSystem}/stats-by-chunk
213240
214-
cat "$chunkOutputDir"/result/* | jq -s 'add | map_values(.outputs)' > $out/${evalSystem}/paths.json
215-
cat "$chunkOutputDir"/result/* | jq -s 'add | map_values(.meta)' > $out/${evalSystem}/meta.json
241+
cat "$chunkOutputDirs"/*/result/* | jq -s 'add | map_values(.outputs)' > $out/${evalSystem}/paths.json
242+
cat "$chunkOutputDirs"/*/result/* | jq -s 'add | map_values(.meta)' > $out/${evalSystem}/meta.json
216243
'';
217244

218245
diff = callPackage ./diff.nix { };

pkgs/top-level/default.nix

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -216,27 +216,37 @@ let
216216

217217
fixedPoint = boot stages;
218218

219+
removeInternallyDisallowedAttrPaths =
220+
let
221+
# Same as `lib.removeAttrs`, but can remove nested attributes (and order of arguments is fixed)
222+
# TODO: Consider moving to lib.attrpaths.removeAttrPaths
223+
removeAttrPaths =
224+
attrPathsToRemove: set:
225+
let
226+
split = lib.partition (
227+
attrPath:
228+
assert attrPath != [ ];
229+
lib.length attrPath == 1
230+
) attrPathsToRemove;
231+
nestedApplied =
232+
set
233+
// lib.mapAttrs (name: attrPaths: removeAttrPaths (lib.map lib.tail attrPaths) set.${name}) (
234+
lib.groupBy (attrPath: lib.head attrPath) split.wrong
235+
);
236+
in
237+
lib.removeAttrs nestedApplied (lib.map lib.head split.right);
238+
in
239+
removeAttrPaths (map (x: x.attrPath) config.attrPathsDisallowedForInternalUse);
240+
219241
pkgs =
220242
# Generally only set by CI, don't want to cause a performance hit for users
221243
if config.attrPathsDisallowedForInternalUse == [ ] then
222244
fixedPoint
223245
else
224246
# See ./stage.nix, which replaced config.attrPathsDisallowedForInternalUse with aborts.
225-
# We replace these attribute paths with their original derivations again,
226-
# because CI would just error out from the aborting attributes themselves.
227-
# Internally all packages still see the aborting attributes if used as dependencies,
228-
# because we do this here after the fixed-point is calculated.
229-
# Note that we don't want to remove the attributes entirely like what aliases.nix does,
230-
# because unlike aliases, CI still needs to check the packages to evaluate at all,
231-
# which it wouldn't if they're removed entirely.
232-
lib.updateManyAttrsByPath
233-
(map (attrs: {
234-
path = attrs.attrPath;
235-
update =
236-
_:
237-
lib.getAttrFromPath attrs.attrPath fixedPoint.__internalBeforeInternallyDisallowedAttrPathsOverlay;
238-
}) config.attrPathsDisallowedForInternalUse)
239-
(removeAttrs fixedPoint [ "__internalBeforeInternallyDisallowedAttrPathsOverlay" ]);
247+
# To prevent these attributes from causing CI failures we remove them entirely.
248+
# These attrs are still evaluated but in a different way, see ci/eval/default.nix
249+
removeInternallyDisallowedAttrPaths fixedPoint;
240250

241251
in
242252
checked pkgs

pkgs/top-level/stage.nix

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -317,20 +317,16 @@ let
317317
};
318318

319319
# Replaces the attributes in config.attrPathsDisallowedForInternalUse with aborts.
320+
# Not warnings because those wouldn't give a backtrace, which is important for debugging
320321
# Not throws because those would be ignored by nix-env, which is what CI uses to evaluate everything
321-
# See also ./default.nix, where these attributes are added back again so they're still checked by CI
322+
# See also ./default.nix, which removes these attributes entirely from the end result
322323
internallyDisallowedAttrPathsOverlay =
323324
final: prev:
324325
# Generally only set by CI, don't want to cause a performance hit for users
325326
if config.attrPathsDisallowedForInternalUse == [ ] then
326327
{ }
327328
else
328-
{
329-
# So that ./default.nix can add them back again outside the fixed point
330-
# Don't use this in packages!
331-
__internalBeforeInternallyDisallowedAttrPathsOverlay = prev;
332-
}
333-
// lib.updateManyAttrsByPath (map (
329+
lib.updateManyAttrsByPath (map (
334330
{ attrPath, reason }:
335331
{
336332
path = attrPath;

0 commit comments

Comments
 (0)