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

filterSource support #34

Merged
merged 3 commits into from Apr 21, 2021
Merged

filterSource support #34

merged 3 commits into from Apr 21, 2021

Conversation

symphorien
Copy link
Contributor

Motivation: use nix-gitignore and not have to rebuild on each git fetch
I decided accurately respecting the filter in filterSource was too complex, so this implements an approximation.

  • adds a way to watch only directory not recursively
  • when using filtersource on a path, the path is watch non-recursively, and the children satisfying the filter are watched recursively.
    This way, .git it ignored.
  • Amended the changelog in release.nix (see release.nix for instructions)

@Profpatsch
Copy link
Collaborator

Can you add some more motivation and a problem description to the commit messages? I don’t understand why we have to re-implement nixpkgs functions.

@symphorien
Copy link
Contributor Author

better?

@Profpatsch
Copy link
Collaborator

The first commit is especially what I’m unsure about, it looks like it changes the watcher logic somewhat.

@symphorien
Copy link
Contributor Author

Added a motivation to the first commit.

@sternenseemann
Copy link
Contributor

Currently if a nix file depends on builtins.readDir path, path is
recursively watched. This is wasteful because if "path/foo/bar" is modified,
builtins.readDir path does not change. We should reevaluate
only if the list of direct children of path changed.

The result of that may not change, but the content of the watched path may still change and there's really nothing preventing you from doing a builtins.map (s: ./ + s) (builtins.readDir foo) in a nix expression which would then require a rebuild, right?

@symphorien
Copy link
Contributor Author

The dependency will still be found when nix coerces ./ + s to a string with context and logs it, just like when we do the traditional src = ./.:

/tmp/test 1 job
$  tree
.
├── foo
└── shell.nix

0 directories, 2 files
                                                                                                                                                                                              
/tmp/test 1 job
$  cat shell.nix
with import <nixpkgs> {};
mkShell {
  envvar = lib.concatMapStrings (name: (./. + ("/"+name))) (lib.attrNames (builtins.readDir ./.));
}
                                                                                                                                                                                              
/tmp/test 1 job
$  nix-instantiate shell.nix -vv |& grep "copied source '/tmp"
copied source '/tmp/test/foo' -> '/nix/store/avva3pijyyj4bgrj1vcl3sj6hiadjxlj-foo'
copied source '/tmp/test/shell.nix' -> '/nix/store/mgn25m4drjdw2j9pay17jv4ln1bkd3k8-shell.nix'

…readDir

Currently if a nix file depends on `builtins.readDir path`, `path` is
recursively watched. This is wasteful because if "path/foo/bar" is modified,
`builtins.readDir path` does not change. We should reevaluate
only if the list of direct children of `path` changed.

This patch separates paths to be watched in paths to be watched
recursively and patches to be watched non recursively. The latter is
used for builtins.readDir.
nix does not log anything when evaluating builtins.filterSource,
contrary to normal paths. We have to wrap builtins.filterSource with
builtins.trace like builtins.readFile.

The easy solution would be to trace "lorri read: $path", but then we
also watch filtered out files. Reimplementing the full logic of
filterSource in nix would be cumbersome, but we can find a treadeoff.
This implementation is designed to ignore .git when using nix-gitignore:
the top-level directory is watched as if it was readDir'ed, and only the
children which are kept by filterSource (ie, not .git) are watched
recursively. This way you can run git fetch in the filterSource'd
directory and not reevaluate. I suppose nix-gitignore accounts for a
large part of filterSource usage, as filterSource is kind of painful
to use.
@symphorien
Copy link
Contributor Author

I fixed the merge conflict.

Copy link
Collaborator

@Profpatsch Profpatsch left a comment

Choose a reason for hiding this comment

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

This looks amazing!

Once upon a time I didn’t have a good mental state of how readDir works, so we were watching over-zealously, but this should be just fine.

I can imagine the filterSource improvement to make a quite big difference on e.g. javascript source code, where node_modules can be easily 100k files, which will run out of fds immediately.

# if all files were kept, let's log only ${path}
logTopLevel = builtins.trace "lorri read: '${toString path}'" result;
# otherwise log all kept files individually, and the listing of the toplevel dir
log1stLevel = builtins.trace "lorri readdir: '${toString path}'" (builtins.foldl' (acc: name: builtins.trace "lorri read: '${toString path}/${name}'" acc) result kept);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is the tradeoff right? If a recursive directory is kept, it will be watched recursively.

Still, in combination with the previous commit a strict improvement!

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah, not quite. input sources were still watched 100%, so both commits are an improvement to the filtering logic. Awesome.

release.nix Outdated
{
version = 872;
changes = ''
Add support for builtins.filterSource
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is also missing the first commit, not watching readDir recursively by default is a huge win.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added it

@symphorien
Copy link
Contributor Author

hum the failing test passes locally...

@Profpatsch
Copy link
Collaborator

Gosh our file watching tests are flaky

@Profpatsch Profpatsch merged commit ef57d51 into nix-community:canon Apr 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants