Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy trees #6530

Draft
wants to merge 390 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
390 commits
Select commit Hold shift + click to select a range
37d859b
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Aug 19, 2022
a115c4f
GitHub fetcher: Restore the lastModified field
edolstra Aug 22, 2022
91aea15
Fix macOS build, where le32toh is not available
edolstra Aug 22, 2022
13c0db4
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Aug 25, 2022
def7b25
readLittleEndian(): Fix 64-bit integer truncation
edolstra Aug 26, 2022
034340a
Fix potential duplicate activity IDs in forked child processes
edolstra Aug 26, 2022
f95c425
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Aug 29, 2022
30be644
Make EvalState::inputAccessors keyed by the accessor number
edolstra Aug 29, 2022
7da3a30
Remove no_pos_tag
edolstra Aug 29, 2022
301f388
Remove FIXME
edolstra Aug 29, 2022
440214f
ZipInputAccessor: Fix invalid read
edolstra Aug 30, 2022
89f1021
Improve display of Git inputs
edolstra Aug 31, 2022
120bec5
GitInputScheme: Do not record 'ref' for dirty trees
edolstra Aug 31, 2022
8a43eaa
GitInputScheme: Add some progress indication
edolstra Aug 31, 2022
7c1bdff
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Sep 2, 2022
c0dd35a
ZipInputAccessor: Improve error messages
edolstra Sep 2, 2022
2d5cfca
Fix accessing 'toString path'
edolstra Sep 2, 2022
3667cf5
Whitespace
edolstra Sep 12, 2022
b293b33
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Sep 12, 2022
48a5879
Decode virtual paths in user-thrown errors
edolstra Sep 12, 2022
1b8065f
posToXML(): Fix displaying paths
edolstra Sep 12, 2022
85c1959
Remove some FIXMEs
edolstra Sep 12, 2022
432a3a1
Move isUri() and resolveUri() out of filetransfer.cc
edolstra Sep 12, 2022
2a1c63c
Support flake references in the old CLI
edolstra Sep 13, 2022
28b62dd
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Sep 28, 2022
a291e37
Improve error messages from call-flake.nix
edolstra Sep 28, 2022
c3c0682
Don't show "from call site" when we don't know the call site
edolstra Sep 28, 2022
cbade16
Handle unlocked overriden inputs
edolstra Sep 29, 2022
241dd54
warnOnce(): Fix boost exception when the message contains a format ch…
edolstra Sep 30, 2022
5c8d67d
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Oct 10, 2022
0286edb
Format GitHub paths as URLs
edolstra Oct 10, 2022
1483c56
Patch libzip to return timestamps in the Unix epoch
edolstra Oct 10, 2022
7317196
Input::getAccessor(): Get the fingerprint from the final accessor
edolstra Oct 10, 2022
5115909
Fix handling of relative paths
edolstra Oct 10, 2022
12dd8d4
Fix 'nix-instantiate --find-file' and add a test
edolstra Oct 12, 2022
e2353b9
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Oct 26, 2022
0402dd0
Interpret absolute paths relative to the root FS rather than the curr…
edolstra Oct 26, 2022
1683872
Use __nix_virtual__ instead of __virtual__
edolstra Oct 26, 2022
e424a8b
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Oct 27, 2022
a653e98
Encode virtual paths as /nix/store/virtual000<N>
edolstra Oct 27, 2022
f02da62
builtins.trace: Decode virtual paths
edolstra Oct 28, 2022
b275aa4
Don't use warnOnce() for the toString warning
edolstra Oct 28, 2022
8342317
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Nov 2, 2022
4072024
GitArchiveInputScheme: Bring back the narHash attribute
edolstra Nov 3, 2022
64a69b4
Fix dirOf on the root of a flake
edolstra Nov 16, 2022
561440b
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Nov 16, 2022
b27cd88
Partially revert "Format GitHub paths as URLs"
edolstra Nov 25, 2022
515b908
Fix decoding virtual paths that are at the root of the tree
edolstra Nov 25, 2022
39a783f
Require flakes for the -I flake:... feature
edolstra Dec 1, 2022
116acc1
Fix readDir for accessors whose readDirectory doesn't return types
edolstra Dec 2, 2022
fcdca3d
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Dec 5, 2022
730f6bf
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Dec 7, 2022
57397a3
Formatting
edolstra Dec 7, 2022
d162222
Remove test for .path in nix flake metadata
edolstra Dec 7, 2022
6d104bb
Don't allow appending a non-absolute path to the root of a source tree
edolstra Dec 8, 2022
f620184
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Dec 9, 2022
d950e3a
Improve error message when flake.nix exists but is not under Git control
edolstra Dec 9, 2022
12c554a
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Dec 12, 2022
a322306
For backward compatibility, allow appending non-root paths to the roo…
edolstra Dec 14, 2022
d8620d7
Append a slash in ./${"foo"}
edolstra Dec 14, 2022
8becbb0
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Dec 14, 2022
3bcaa7d
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Dec 15, 2022
210cd8c
Merge toDerivations() into toDerivedPaths()
edolstra Dec 15, 2022
81a4516
InstallableFlake::toDerivedPaths(): Support paths and store paths
edolstra Dec 15, 2022
880a72b
nix build --json: Only show non-zero startTime / stopTime
edolstra Dec 16, 2022
15d2e0e
Merge remote-tracking branch 'origin/master' into tmp
edolstra Dec 20, 2022
b48e641
Add builtins.filterPath
edolstra Dec 19, 2022
5e3cd3e
Fix FilteringInputAccessor::pathExists()
edolstra Dec 21, 2022
29dff7e
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Jan 11, 2023
20a0a74
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Jan 12, 2023
fa5af1e
Fix support for relative non-flake inputs (path:./bla)
edolstra Jan 12, 2023
9286b1a
Split GitInputScheme::getAccessor() more
edolstra Jan 20, 2023
9512afa
Typo
edolstra Jan 20, 2023
3621d07
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Jan 26, 2023
3522978
Fix tests
edolstra Jan 26, 2023
f4f0f8a
Remove toString deprecation warning
edolstra Jan 26, 2023
37b4a9e
Shut up a gcc warning
edolstra Jan 26, 2023
31bb875
Use libgit2 to provide direct access to Git repositories
edolstra Jan 25, 2023
aaf8b1b
Use C++20
edolstra Jan 31, 2023
b14830b
TarArchive: Remove a duplicate constant and increase the buffer size
edolstra Jan 31, 2023
5c29abc
GitArchiveInputScheme: Revert to downloading tarballs
edolstra Jan 31, 2023
4142982
Remove ZipInputAccessor
edolstra Jan 31, 2023
ca26ce9
Check tarball cache validity
edolstra Feb 3, 2023
219510b
GitArchiveInputScheme: Verify the locked tree hash
edolstra Feb 3, 2023
7b1cda9
GitArchiveInputScheme: Restore the lastModified attribute
edolstra Feb 3, 2023
00b746d
Warn if the computed tree hash differs from the one reported by GitHub
edolstra Feb 3, 2023
bb421ac
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Feb 8, 2023
12f1413
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Feb 17, 2023
02d5c54
Fix clang build
edolstra Feb 17, 2023
26ff9c1
Revert "Shut up a gcc warning"
edolstra Feb 17, 2023
f4009fd
Don't use std::span just yet
edolstra Feb 17, 2023
ece20d5
GitInputAccessor: Support symlinks
edolstra Feb 17, 2023
5d1e5a0
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Feb 21, 2023
c8f7ca2
Fix addErrorContext
edolstra Feb 21, 2023
273df09
Remove unused file
edolstra Feb 21, 2023
21f0a98
Remove unnecessary -lgit2
edolstra Feb 21, 2023
03618bb
Fix GitHub test
edolstra Feb 21, 2023
56a845d
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Feb 22, 2023
98a90cc
Use libgit2 to get the revCount
edolstra Feb 24, 2023
430bfcf
Use libgit2 to get lastModified
edolstra Feb 24, 2023
13e8d70
Use libgit2 for getting isShallow
edolstra Feb 24, 2023
23806db
Use libgit2 to resolve references to commit hashes
edolstra Feb 24, 2023
ab2b0fb
Add a helper class to avoid leaking libgit2 objects
edolstra Feb 24, 2023
8c8f242
Handle revisions used as refs for compatibility
edolstra Feb 24, 2023
579ecd3
Remove a test for the non-presence of git
edolstra Feb 24, 2023
1b0b3b1
Add git_oid -> Hash conversion
edolstra Feb 24, 2023
834d878
Use libgit2 to get workdir info
edolstra Mar 1, 2023
1017b11
Remove dead code
edolstra Mar 1, 2023
86ca2c5
Use libgit2 to create the local cache repos
edolstra Mar 1, 2023
e952d36
Refactor
edolstra Mar 1, 2023
d6cdb07
Rename
edolstra Mar 1, 2023
334348f
Remove use of git cat-file
edolstra Mar 1, 2023
733861d
Support allRefs in git URLs
edolstra Mar 2, 2023
3a1a33d
Use libgit2 for fetching repos
edolstra Mar 2, 2023
b19534a
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Mar 6, 2023
854a311
Fix appending an empty string to a path
edolstra Mar 6, 2023
b8218b5
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Mar 7, 2023
f21e1cf
Fix outPath in flakes
edolstra Mar 8, 2023
aa823c2
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Mar 8, 2023
d7d49fd
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Mar 9, 2023
bb72d1b
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Mar 9, 2023
6192fd0
Fix some clang errors/warnings
edolstra Mar 9, 2023
4f1b0d7
Fix clang compilation
edolstra Mar 10, 2023
cdb946e
Fix test regression
edolstra Mar 13, 2023
7052e19
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Mar 15, 2023
2f1a90b
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Mar 16, 2023
0671807
Fix makefile
edolstra Mar 16, 2023
b2a205e
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Mar 16, 2023
ccd2ad2
Remove git caching
edolstra Mar 17, 2023
2a4462e
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Mar 17, 2023
f432967
Use libgit2 for getting the HEAD ref of local repos
edolstra Mar 20, 2023
f23b969
Check git_remote_fetch() return value
edolstra Mar 20, 2023
2ffa909
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Mar 24, 2023
4b9215c
Handle patches that have a timestamp after the filename
edolstra Mar 28, 2023
9a15ec8
Indentation
edolstra Mar 30, 2023
870e750
Fix root/overriden flakes with dir attribute
edolstra Mar 29, 2023
64b9e50
Add CanonPath::makeRelative()
edolstra Mar 30, 2023
2154084
Allow patches to be applied to flake inputs
edolstra Mar 28, 2023
a0b0cac
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Mar 30, 2023
a1501cf
EvalState::rootPath(): Take a CanonPath
edolstra Apr 3, 2023
8ee59d7
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Apr 3, 2023
791e222
Fix tests
edolstra Apr 6, 2023
5b2be3a
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Apr 25, 2023
0dfc214
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Apr 26, 2023
91aefbd
Factor out FilteringInputAccessor
edolstra Apr 26, 2023
d102273
Fix clang compilation
edolstra May 1, 2023
39079dd
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra May 9, 2023
2b9ded5
Fix parsing of relative flake inputs without 'path:'
edolstra May 10, 2023
a9d4780
Revert to using git for fetching remote git repos
edolstra May 11, 2023
2dc2f58
Log git fetch errors
edolstra May 11, 2023
ac73702
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra May 12, 2023
3c4d678
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Jun 2, 2023
e59caad
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Jun 19, 2023
2f18e78
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Jun 26, 2023
f355c34
Fix handling of relative path flakes (and add some tests)
edolstra Jun 26, 2023
56272db
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Aug 3, 2023
3494c29
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Sep 9, 2023
4e16d5e
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Sep 20, 2023
6b70509
Remove dead code
edolstra Sep 21, 2023
eaa785e
Improve error message
edolstra Sep 22, 2023
ea5c2e3
Tarball fetcher: Use the tarball cache
edolstra Oct 11, 2023
b219d76
Tarball cache: Add cache entries for all URLs in the redirect chain
edolstra Oct 11, 2023
0e48afb
Remove the "facts" cache
edolstra Oct 11, 2023
0b72b31
Remove debug statement
edolstra Oct 12, 2023
6513f69
Fix lastModified handling
edolstra Oct 13, 2023
94c028f
Fix file:https:// URLs
edolstra Oct 13, 2023
e350f84
Remove bad.tar.xz check, since libarchive doesn't care
edolstra Oct 13, 2023
fa8618f
Eliminate old downloadTarball()
edolstra Oct 13, 2023
144987e
Revert cache version
edolstra Oct 13, 2023
6a95ff9
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Oct 13, 2023
b4df104
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Oct 17, 2023
601de7f
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Oct 23, 2023
c677ea7
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Oct 23, 2023
aa85359
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Nov 3, 2023
ddca787
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Nov 20, 2023
ec272de
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Nov 22, 2023
b39148e
Post-merge cleanup
edolstra Nov 22, 2023
3099b51
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Nov 28, 2023
0aa13b6
Cleanup
edolstra Nov 30, 2023
be1543e
Merge remote-tracking branch 'upstream/master' into lazy-trees
Ericson2314 Dec 1, 2023
653c100
Merge remote-tracking branch 'upstream/master' into lazy-trees
Ericson2314 Dec 1, 2023
575902b
Merge remote-tracking branch 'upstream/master' into lazy-trees
Ericson2314 Dec 7, 2023
d38f4d9
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Dec 13, 2023
dab2be3
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Dec 13, 2023
2055e28
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Dec 13, 2023
c0b1df7
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Feb 15, 2024
748567b
Doxygen
edolstra Feb 15, 2024
5558d65
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Feb 19, 2024
d022bce
FileInputScheme: Display path properly
edolstra Feb 19, 2024
7eb3ba0
PosixSourceAccessor: Support roots that are not directories
edolstra Feb 19, 2024
d9cfae2
Cleanup
edolstra Feb 21, 2024
43de535
Remove unnecessary pointer
edolstra Feb 21, 2024
0d02a63
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Feb 21, 2024
d2292a1
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Feb 21, 2024
edcd6ce
Merge remote-tracking branch 'origin/master' into lazy-trees
9999years Mar 1, 2024
793a838
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Mar 4, 2024
1c56bdd
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Mar 7, 2024
49f9099
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Apr 2, 2024
ce23764
Remove duplicate comment
edolstra Apr 2, 2024
3369a81
EvalState::findFile(): Resolve symlinks
edolstra Apr 3, 2024
3f70f20
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Apr 19, 2024
1abf058
Merge remote-tracking branch 'upstream/master' into lazy-trees
roberth Apr 21, 2024
147593d
tests/libexpr: Re-enable C api tests that build
roberth Apr 21, 2024
c6b4f01
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Apr 22, 2024
cf02c6a
Fix test
edolstra Apr 22, 2024
4a9b7c8
Fix pre-commit check
edolstra Apr 22, 2024
0efcbbf
Run the flake-regressions test suite
edolstra Apr 24, 2024
c4cd4cd
Hack
edolstra Apr 24, 2024
93cf285
PathInputScheme: Improve path display
edolstra Apr 25, 2024
2fec506
BasicDerivation: Add applyRewrites() method
edolstra Apr 25, 2024
5ddd8e4
Introduce a new string context for representing paths
edolstra Apr 25, 2024
1302bfa
Re-enable some tests
edolstra Apr 25, 2024
9c10fcf
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Apr 30, 2024
71575a2
Debug
edolstra Apr 30, 2024
333d35a
callPathFilter(): Pass a lazy store path
edolstra Apr 30, 2024
0950094
Fix fileset compatibility
edolstra Apr 30, 2024
2101916
Test baseNameOf behaviour on the root of a flake
edolstra Apr 30, 2024
fc4d948
Cleanup
edolstra Apr 30, 2024
db6c219
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Apr 30, 2024
a65c8a9
Debug
edolstra Apr 30, 2024
c5ae41d
Copy roots to the store as /nix/store/<hash1>-<hash2>-source
edolstra Apr 30, 2024
ea54be0
filterPath: Ensure /nix/store/<hash>-<hash>-source
edolstra Apr 30, 2024
a435a40
Disable some GitHub narHash related tests for now
edolstra Apr 30, 2024
7332321
Run more flake regression tests
edolstra Apr 30, 2024
299d0b7
TarballInputScheme: Fix fetchToStore() caching
edolstra May 3, 2024
3a96ba0
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra May 6, 2024
25635e5
EvalState: Rename decodePaths() -> prettyPrintPaths()
edolstra May 6, 2024
b5be3b0
Pretty-print virtual paths in more contexts
edolstra May 6, 2024
2409e6b
Add tests for tree laziness
edolstra May 6, 2024
44d20c3
Warn against use of `toString ./path` as a derivation attribute
edolstra May 6, 2024
4133514
Remove unused
edolstra May 6, 2024
2cb003c
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra May 6, 2024
8fc36e2
Fix concatenating to an attrset
edolstra May 6, 2024
bcb1be4
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra May 6, 2024
f0cf5d8
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra May 7, 2024
a123f87
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra May 8, 2024
26f1431
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra May 10, 2024
b7255f8
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra May 13, 2024
584fece
flake-regressions.sh: Make the sort order deterministic
edolstra May 14, 2024
4d39908
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra May 14, 2024
e255be6
builtins.toPath: Rewrite virtual paths
edolstra May 14, 2024
a33aad3
Use fetchToStore() instead of computeStorePath()
edolstra May 14, 2024
35bbd85
Warn about double copies
edolstra May 14, 2024
b6be10d
Skip a broken test
edolstra May 15, 2024
57e1692
Backwards compatibility hack to handle `/. + path`
edolstra May 15, 2024
57b2cc2
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Jul 2, 2024
1b20945
Formatting
edolstra Jul 2, 2024
53fabdc
Busywork
edolstra Jul 2, 2024
34e4a61
Merge branch 'master' into lazy-trees
Ericson2314 Jul 5, 2024
0d3a157
Merge remote-tracking branch 'origin/master' into lazy-trees
edolstra Jul 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add builtins.filterPath
This is like builtins.{filterSource,path}, but returns a virtual path
that applies the filter lazily, rather than copying the result to the
Nix store. Thus filterPath can be composed.
  • Loading branch information
edolstra committed Dec 21, 2022
commit b48e64162a77f09345c94826d9799ff578b2981b
7 changes: 7 additions & 0 deletions src/libexpr/eval.hh
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,13 @@ public:
*/
[[nodiscard]] StringMap realiseContext(const PathSet & context);

/* Call the binary path filter predicate used builtins.path etc. */
bool callPathFilter(
Value * filterFun,
const SourcePath & path,
std::string_view pathArg,
PosIdx pos);

private:

unsigned long nrEnvs = 0;
Expand Down
52 changes: 31 additions & 21 deletions src/libexpr/primops.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1974,6 +1974,30 @@ static RegisterPrimOp primop_toFile({
.fun = prim_toFile,
});

bool EvalState::callPathFilter(
Value * filterFun,
const SourcePath & path,
std::string_view pathArg,
PosIdx pos)
{
auto st = path.lstat();

/* Call the filter function. The first argument is the path, the
second is a string indicating the type of the file. */
Value arg1;
arg1.mkString(pathArg);

Value arg2;
// assert that type is not "unknown"
arg2.mkString(fileTypeToString(st.type));

Value * args []{&arg1, &arg2};
Value res;
callFunction(*filterFun, 2, args, res, pos);

return forceBool(res, pos);
}

static void addPath(
EvalState & state,
const PosIdx pos,
Expand Down Expand Up @@ -2010,26 +2034,11 @@ static void addPath(
#endif

std::unique_ptr<PathFilter> filter;
if (filterFun) filter = std::make_unique<PathFilter>([&](const Path & p) {
SourcePath path2{path.accessor, CanonPath(p)};

auto st = path2.lstat();

/* Call the filter function. The first argument is the path,
the second is a string indicating the type of the file. */
Value arg1;
arg1.mkString(path2.path.abs());

Value arg2;
// assert that type is not "unknown"
arg2.mkString(fileTypeToString(st.type));

Value * args []{&arg1, &arg2};
Value res;
state.callFunction(*filterFun, 2, args, res, pos);

return state.forceBool(res, pos);
});
if (filterFun)
filter = std::make_unique<PathFilter>([&](const Path & p) {
auto p2 = CanonPath(p);
return state.callPathFilter(filterFun, {path.accessor, p2}, p2.abs(), pos);
});

std::optional<StorePath> expectedStorePath;
if (expectedHash)
Expand Down Expand Up @@ -2130,14 +2139,15 @@ static RegisterPrimOp primop_filterSource({

static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceAttrs(*args[0], pos);
std::optional<SourcePath> path;
std::string name;
Value * filterFun = nullptr;
auto method = FileIngestionMethod::Recursive;
std::optional<Hash> expectedHash;
PathSet context;

state.forceAttrs(*args[0], pos);

for (auto & attr : *args[0]->attrs) {
auto n = state.symbols[attr.name];
if (n == "path")
Expand Down
174 changes: 174 additions & 0 deletions src/libexpr/primops/filterPath.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#include "primops.hh"

namespace nix {

struct FilteringInputAccessor : InputAccessor
{
EvalState & state;
PosIdx pos;
ref<InputAccessor> next;
CanonPath prefix;
Value * filterFun;

std::map<CanonPath, bool> cache;

FilteringInputAccessor(EvalState & state, PosIdx pos, const SourcePath & src, Value * filterFun)
: state(state)
, pos(pos)
, next(src.accessor)
, prefix(src.path)
, filterFun(filterFun)
{
}

std::string readFile(const CanonPath & path) override
{
checkAccess(path);
return next->readFile(prefix + path);
}

bool pathExists(const CanonPath & path) override
{
checkAccess(path);
return next->pathExists(prefix + path);
edolstra marked this conversation as resolved.
Show resolved Hide resolved
}

Stat lstat(const CanonPath & path) override
{
checkAccess(path);
return next->lstat(prefix + path);
}

DirEntries readDirectory(const CanonPath & path) override
{
checkAccess(path);
DirEntries entries;
for (auto & entry : next->readDirectory(prefix + path)) {
if (isAllowed(path + entry.first))
entries.insert(std::move(entry));
}
return entries;
}

std::string readLink(const CanonPath & path) override
{
checkAccess(path);
return next->readLink(prefix + path);
}

void checkAccess(const CanonPath & path)
{
if (!isAllowed(path))
throw Error("access to path '%s' has been filtered out", showPath(path));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!isAllowed(path))
throw Error("access to path '%s' has been filtered out", showPath(path));
if (!isAllowed(path))
throw Error("access to path '%s' has been filtered out by filterPath", showPath(path));

Also this will lead to surprising errors like access to path 'defualt.nix' has been filtered out by filterPath, leading someone to debug their filter function rather than fix the typo.

I've tried to improve the error message in a suggestion here, but to get enough information, we need a pathExistsUnfiltered function.

}

bool isAllowed(const CanonPath & path)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bool isAllowed(const CanonPath & path)
bool isIncluded(const CanonPath & path)

It's not really about permission.

{
auto i = cache.find(path);
if (i != cache.end()) return i->second;
auto res = isAllowedUncached(path);
cache.emplace(path, res);
return res;
}

bool isAllowedUncached(const CanonPath & path)
{
if (!path.isRoot() && !isAllowed(*path.parent())) return false;
// Note that unlike 'builtins.{path,filterSource}', we don't
// pass the prefix to the filter function.
return state.callPathFilter(filterFun, {next, prefix + path}, path.abs(), pos);
}

std::string showPath(const CanonPath & path) override
{
return next->showPath(prefix + path);
}
};

static void prim_filterPath(EvalState & state, PosIdx pos, Value * * args, Value & v)
{
std::optional<SourcePath> path;
Value * filterFun = nullptr;
PathSet context;

state.forceAttrs(*args[0], pos);

for (auto & attr : *args[0]->attrs) {
auto n = state.symbols[attr.name];
if (n == "path")
path.emplace(state.coerceToPath(attr.pos, *attr.value, context));
else if (n == "filter") {
state.forceValue(*attr.value, pos);
filterFun = attr.value;
}
else
state.debugThrowLastTrace(EvalError({
.msg = hintfmt("unsupported argument '%1%' to 'filterPath'", state.symbols[attr.name]),
.errPos = state.positions[attr.pos]
}));
}

if (!path)
state.debugThrowLastTrace(EvalError({
.msg = hintfmt("'path' required"),
.errPos = state.positions[pos]
}));

if (!filterFun)
state.debugThrowLastTrace(EvalError({
.msg = hintfmt("'filter' required"),
.errPos = state.positions[pos]
}));

if (!context.empty())
state.debugThrowLastTrace(EvalError({
.msg = hintfmt("'path' argument to 'filterPath' cannot have a context"),
.errPos = state.positions[pos]
}));

auto accessor = make_ref<FilteringInputAccessor>(state, pos, *path, filterFun);

state.registerAccessor(accessor);

v.mkPath(accessor->root());
}

static RegisterPrimOp primop_filterPath({
.name = "__filterPath",
.args = {"args"},
.doc = R"(
This function lets you filter out files from a path. It takes a
path and a predicate function, and returns a new path from which
every file has been removed for which the predicate function
returns `false`.

For example, the following filters out all regular files in
`./doc` that don't end with the extension `.md`:

```nix
builtins.filterPath {
path = ./doc;
filter =
path: type:
(type != "regular" || hasSuffix ".md" path);
}
```

The filter function is called for all files in `path`. It takes
two arguments. The first is a string that represents the path of
the file to be filtered, relative to `path` (i.e. it does *not*
contain `./doc` in the example above). The second is the file
type, which can be one of `regular`, `directory` or `symlink`.

Note that unlike `builtins.filterSource` and `builtins.path`,
this function does not copy the result to the Nix store. Rather,
the result is a virtual path that lazily applies the filter
predicate. The result will only be copied to the Nix store if
needed (e.g. if used in a derivation attribute like `src =
builtins.filterPath { ... }`).
)",
.fun = prim_filterPath,
.experimentalFeature = Xp::Flakes,
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filterPath as implemented allows a new species of path to be constructed; a lot like a file, but with an odd dirOf.

nix-repl> p = builtins.filterPath { path = ./default.nix; filter = _: _: true; }

nix-repl> dirOf p == p
true

nix-repl> p2 = ./default.nix

nix-repl> dirOf p2 == p2
false

This seems undesirable, especially while comparing dirOf is the only way to find the tree root, but also if that was provided for.

The "outPath" behavior is also different.

nix-repl> "${./default.nix}"
"/nix/store/i09iwz8gh5qcrhmgfiz8g381b5qky4v1-default.nix"

nix-repl> "${p}"             
"/nix/store/n3afs2np12qjb7lgj8n54rpjs2hzar8j-source"

While these design choices are by no means easy to compare, I think the least surprising choice is the one where the complexity can be understood locally - even when relevant events occur in code written by different people.
"filterPath returns a file path instead of a tree when the path is a file" seems like a rule that's easier to understand and troubleshoot than "a value that behaves like a path may behave differently in rare cases", as a user would have no reason to suspect that this might happen, and there's no ad hoc repl test that explains the behavior.
So I think filterPath should make an exception for a path argument. When it's a single file (or symlink I suppose), return that as a regular path instead of a lazy tree.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, could returning the file as part of a lazy directory with just that file in it work? Returning a regular path would still cause the file to be copied to the store, which might still be an issue especially in case of large files, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right. Returning a single directory with just the file is better than returning the file in its original root, as it would be reasonable to assume that no path outside the path argument can be recovered from the returned path.

Returning a regular path would still cause the file to be copied to the store

That wouldn't be necessary until used as a string.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filterPath is not referentially transparent under == equality.

nix-repl> d1 = builtins.filterPath { path = ./.; filter = always; }     

nix-repl> d2 = builtins.filterPath { path = ./.; filter = always; } 

nix-repl> d1 == d2
false  # wrong

The default equality should be implemented by traversing the tree and making sure that the trees behave equally. That is still quite lazy, as no copying is necessary. It should compare the directory structure and outcome of the filter applications need to be computed, and if that all works out, it should check that either the files are in the same physical location, or have equal contents. The latter would rarely be necessary.


}
48 changes: 48 additions & 0 deletions tests/flakes/tree-operators.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
source ./common.sh

flake1Dir=$TEST_ROOT/flake1

mkdir -p $flake1Dir

pwd=$(pwd)

cat > $flake1Dir/flake.nix <<EOF
{
inputs.docs = {
url = "path:https://$pwd/../../doc";
flake = false;
};

outputs = { self, docs }: with import ./lib.nix; {

mdOnly = builtins.filterPath {
path = docs;
filter =
path: type:
(type != "regular" || hasSuffix ".md" path);
};

noReleaseNotes = builtins.filterPath {
path = self.mdOnly + "/manual";
filter =
path: type:
assert !hasPrefix "/manual" path;
(builtins.baseNameOf path != "release-notes");
};

};
}
EOF

cp ../lang/lib.nix $flake1Dir/

nix build --out-link $TEST_ROOT/result $flake1Dir#mdOnly
[[ -e $TEST_ROOT/result/manual/src/quick-start.md ]]
[[ -e $TEST_ROOT/result/manual/src/release-notes ]]
(! find $TEST_ROOT/result/ -type f | grep -v '.md$')
find $TEST_ROOT/result/ -type f | grep release-notes

nix build --out-link $TEST_ROOT/result $flake1Dir#noReleaseNotes
[[ -e $TEST_ROOT/result/src/quick-start.md ]]
(! [[ -e $TEST_ROOT/result/src/release-notes ]])
(! find $TEST_ROOT/result/ -type f | grep release-notes)
2 changes: 2 additions & 0 deletions tests/lang/lib.nix
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ rec {

sum = foldl' (x: y: add x y) 0;

hasPrefix = pref: str: substring 0 (stringLength pref) str == pref;

hasSuffix = ext: fileName:
let lenFileName = stringLength fileName;
lenExt = stringLength ext;
Expand Down
1 change: 1 addition & 0 deletions tests/local.mk
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ nix_tests = \
flakes/unlocked-override.sh \
flakes/absolute-paths.sh \
flakes/build-paths.sh \
flakes/tree-operators.sh \
ca/gc.sh \
gc.sh \
remote-store.sh \
Expand Down