mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-06-09 17:46:29 +09:00
lib.fileset: Internal representation v2, ~12x faster unions!
$ ./benchmark.sh HEAD [...] Mean CPU time 0.04006 (σ = 0.0040146) for 10 runs is 8.193619775953792% (σ = 0.9584251052704821%) of the old value 0.488917 (σ = 0.0294955) [...]
This commit is contained in:
parent
45bf2c7617
commit
fe6c1539cc
3 changed files with 32 additions and 48 deletions
|
@ -41,7 +41,7 @@ An attribute set with these values:
|
||||||
- `_type` (constant string `"fileset"`):
|
- `_type` (constant string `"fileset"`):
|
||||||
Tag to indicate this value is a file set.
|
Tag to indicate this value is a file set.
|
||||||
|
|
||||||
- `_internalVersion` (constant `1`, the current version):
|
- `_internalVersion` (constant `2`, the current version):
|
||||||
Version of the representation.
|
Version of the representation.
|
||||||
|
|
||||||
- `_internalBase` (path):
|
- `_internalBase` (path):
|
||||||
|
@ -67,8 +67,8 @@ An attribute set with these values:
|
||||||
One of the following:
|
One of the following:
|
||||||
|
|
||||||
- `{ <name> = filesetTree; }`:
|
- `{ <name> = filesetTree; }`:
|
||||||
A directory with a nested `filesetTree` value for every directory entry.
|
A directory with a nested `filesetTree` value for directory entries.
|
||||||
Even entries that aren't included are present as `null` because it improves laziness and allows using this as a sort of `builtins.readDir` cache.
|
Entries not included may either be omitted or set to `null`, as necessary to improve efficiency or laziness.
|
||||||
|
|
||||||
- `"directory"`:
|
- `"directory"`:
|
||||||
A directory with all its files included recursively, allowing early cutoff for some operations.
|
A directory with all its files included recursively, allowing early cutoff for some operations.
|
||||||
|
|
|
@ -14,6 +14,7 @@ let
|
||||||
inherit (lib.attrsets)
|
inherit (lib.attrsets)
|
||||||
attrValues
|
attrValues
|
||||||
mapAttrs
|
mapAttrs
|
||||||
|
setAttrByPath
|
||||||
zipAttrsWith
|
zipAttrsWith
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -63,7 +64,7 @@ rec {
|
||||||
# - Increment this version
|
# - Increment this version
|
||||||
# - Add an additional migration function below
|
# - Add an additional migration function below
|
||||||
# - Update the description of the internal representation in ./README.md
|
# - Update the description of the internal representation in ./README.md
|
||||||
_currentVersion = 1;
|
_currentVersion = 2;
|
||||||
|
|
||||||
# Migrations between versions. The 0th element converts from v0 to v1, and so on
|
# Migrations between versions. The 0th element converts from v0 to v1, and so on
|
||||||
migrations = [
|
migrations = [
|
||||||
|
@ -79,6 +80,15 @@ rec {
|
||||||
_internalBaseComponents = components parts.subpath;
|
_internalBaseComponents = components parts.subpath;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Convert v1 into v2: filesetTree's can now also omit attributes to signal paths not being included
|
||||||
|
(
|
||||||
|
filesetV1:
|
||||||
|
# This change is backwards compatible (but not forwards compatible, so we still need a new version)
|
||||||
|
filesetV1 // {
|
||||||
|
_internalVersion = 2;
|
||||||
|
}
|
||||||
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
# Create a fileset, see ./README.md#fileset
|
# Create a fileset, see ./README.md#fileset
|
||||||
|
@ -174,50 +184,23 @@ rec {
|
||||||
# - _internalBase: ./.
|
# - _internalBase: ./.
|
||||||
# - _internalTree: {
|
# - _internalTree: {
|
||||||
# "default.nix" = <type>;
|
# "default.nix" = <type>;
|
||||||
# # Other directory entries
|
|
||||||
# <name> = null;
|
|
||||||
# }
|
# }
|
||||||
# See ./README.md#single-files
|
# See ./README.md#single-files
|
||||||
_create (dirOf path)
|
_create (dirOf path)
|
||||||
(_nestTree
|
{
|
||||||
(dirOf path)
|
${baseNameOf path} = type;
|
||||||
[ (baseNameOf path) ]
|
};
|
||||||
type
|
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
# Expand a directory representation to an equivalent one in attribute set form.
|
||||||
Nest a filesetTree under some extra components, while filling out all the other directory entries that aren't included with null
|
# All directory entries are included in the result.
|
||||||
|
|
||||||
_nestTree ./. [ "foo" "bar" ] tree == {
|
|
||||||
foo = {
|
|
||||||
bar = tree;
|
|
||||||
<other-entries> = null;
|
|
||||||
}
|
|
||||||
<other-entries> = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Type: Path -> [ String ] -> filesetTree -> filesetTree
|
|
||||||
*/
|
|
||||||
_nestTree = targetBase: extraComponents: tree:
|
|
||||||
let
|
|
||||||
recurse = index: focusPath:
|
|
||||||
if index == length extraComponents then
|
|
||||||
tree
|
|
||||||
else
|
|
||||||
mapAttrs (_: _: null) (readDir focusPath)
|
|
||||||
// {
|
|
||||||
${elemAt extraComponents index} = recurse (index + 1) (append focusPath (elemAt extraComponents index));
|
|
||||||
};
|
|
||||||
in
|
|
||||||
recurse 0 targetBase;
|
|
||||||
|
|
||||||
# Expand "directory" filesetTree representation to the equivalent { <name> = filesetTree; }
|
|
||||||
# Type: Path -> filesetTree -> { <name> = filesetTree; }
|
# Type: Path -> filesetTree -> { <name> = filesetTree; }
|
||||||
_directoryEntries = path: value:
|
_directoryEntries = path: value:
|
||||||
if isAttrs value then
|
if value == "directory" then
|
||||||
value
|
readDir path
|
||||||
else
|
else
|
||||||
readDir path;
|
# Set all entries not present to null
|
||||||
|
mapAttrs (name: value: null) (readDir path)
|
||||||
|
// value;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Simplify a filesetTree recursively:
|
Simplify a filesetTree recursively:
|
||||||
|
@ -368,8 +351,7 @@ rec {
|
||||||
# while the tree under `/foo/baz` gets nested under `{ baz = ...; ... }`
|
# while the tree under `/foo/baz` gets nested under `{ baz = ...; ... }`
|
||||||
# Therefore allowing combined operations over them.
|
# Therefore allowing combined operations over them.
|
||||||
trees = map (fileset:
|
trees = map (fileset:
|
||||||
_nestTree
|
setAttrByPath
|
||||||
commonBase
|
|
||||||
(drop commonBaseComponentsCount fileset._internalBaseComponents)
|
(drop commonBaseComponentsCount fileset._internalBaseComponents)
|
||||||
fileset._internalTree
|
fileset._internalTree
|
||||||
) filesets;
|
) filesets;
|
||||||
|
|
|
@ -285,19 +285,21 @@ expectFailure 'union ./. ./.' 'lib.fileset: Directly evaluating a file set is no
|
||||||
|
|
||||||
# Past versions of the internal representation are supported
|
# Past versions of the internal representation are supported
|
||||||
expectEqual '_coerce "<tests>: value" { _type = "fileset"; _internalVersion = 0; _internalBase = ./.; }' \
|
expectEqual '_coerce "<tests>: value" { _type = "fileset"; _internalVersion = 0; _internalBase = ./.; }' \
|
||||||
'{ _internalBase = ./.; _internalBaseComponents = path.subpath.components (path.splitRoot ./.).subpath; _internalBaseRoot = /.; _internalVersion = 1; _type = "fileset"; }'
|
'{ _internalBase = ./.; _internalBaseComponents = path.subpath.components (path.splitRoot ./.).subpath; _internalBaseRoot = /.; _internalVersion = 2; _type = "fileset"; }'
|
||||||
|
expectEqual '_coerce "<tests>: value" { _type = "fileset"; _internalVersion = 1; }' \
|
||||||
|
'{ _type = "fileset"; _internalVersion = 2; }'
|
||||||
|
|
||||||
# Future versions of the internal representation are unsupported
|
# Future versions of the internal representation are unsupported
|
||||||
expectFailure '_coerce "<tests>: value" { _type = "fileset"; _internalVersion = 2; }' '<tests>: value is a file set created from a future version of the file set library with a different internal representation:
|
expectFailure '_coerce "<tests>: value" { _type = "fileset"; _internalVersion = 3; }' '<tests>: value is a file set created from a future version of the file set library with a different internal representation:
|
||||||
\s*- Internal version of the file set: 2
|
\s*- Internal version of the file set: 3
|
||||||
\s*- Internal version of the library: 1
|
\s*- Internal version of the library: 2
|
||||||
\s*Make sure to update your Nixpkgs to have a newer version of `lib.fileset`.'
|
\s*Make sure to update your Nixpkgs to have a newer version of `lib.fileset`.'
|
||||||
|
|
||||||
# _create followed by _coerce should give the inputs back without any validation
|
# _create followed by _coerce should give the inputs back without any validation
|
||||||
expectEqual '{
|
expectEqual '{
|
||||||
inherit (_coerce "<test>" (_create ./. "directory"))
|
inherit (_coerce "<test>" (_create ./. "directory"))
|
||||||
_internalVersion _internalBase _internalTree;
|
_internalVersion _internalBase _internalTree;
|
||||||
}' '{ _internalBase = ./.; _internalTree = "directory"; _internalVersion = 1; }'
|
}' '{ _internalBase = ./.; _internalTree = "directory"; _internalVersion = 2; }'
|
||||||
|
|
||||||
#### Resulting store path ####
|
#### Resulting store path ####
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue