-
-
Notifications
You must be signed in to change notification settings - Fork 17.8k
source combinators: extend, trace, cutAt, focusAt and more #112083
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
Conversation
|
Sounds exciting! I guess you already have an idea for how to design combinators? I'm sure @Profpatsch is going to be interested too. Also pinging @nh2 as the author of #56985 |
|
@infinisil thanks. I've added my ideas to the description for context, but this PR can be reviewed independently from those. |
irrelevant comment
|
considerations about the short namesI have an idea that may improve the feel of this interface. The new functions have a new style of interface that is a bit distinct from
cleanSourceWith { name = "myproject-src"; src = unionSource ./pom.xml (cleanSource ./src); }
with sources;
rename "myproject-src" (union ./pom.xml (clean ./src))with souces;
rename "myproject-src" (
union
./pom.xml
(filter (path: _type: /* ... */) ./src)
) |
dc77fe1 to
e66249d
Compare
e66249d to
f759761
Compare
f759761 to
4624bc7
Compare
|
I went ahead and implemented the subpath feature. You can now write a derivation that uses files from parent directories. For example, you may want to write a Nix expression in a project's
It's a bit of a contrived example, but situations like these happen all the time with various auxiliary files aren't directly in the subpath where you need them. With this PR, we don't have to touch the existing makefiles and we don't have to base our source on the parent directory and filter away almost everything. { stdenv, pandoc, lib }:
stdenv.mkDerivation {
name = "README.html";
nativeBuildInputs = [ pandoc ];
src = with lib.sources; extend (cleanSource ./.) [(cleanSource ../makelib) ../README.md];
} |
4624bc7 to
dcdaa6c
Compare
dcdaa6c to
bbf46e6
Compare
|
This is finally ready for review. Took a bunch of iterations to get it to this point, some of which I haven't rebased away, so I recommend to review the end result instead of the commits. |
bbf46e6 to
7c3d7dc
Compare
|
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/nix-developer-role-antithesis-startup-on-site-in-dc-area/18234/1 |
|
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/how-to-generate-documentation-from-arbitrary-nix-code/22292/8 |
infinisil
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Taking a closer look at the PR, the biggest problem is how hard it is to understand what the functions extend, empty, focusAt, cutAt, getOriginalFocusPath, getSubpath and reparent do. It's neither obvious from the name or their documentation. I'm also not understanding use cases for these, when would I want to use one or the other? I'm giving this a better try here:
An overview of the functions
The core concept of this PR is that it works with two anchor points of filtered sources:
- root anchor: The root of the store path import, only files under this anchor can be included in the filter
- subpath anchor: The subpath of the root anchor where the filtered source is pointing to. This is what the filtered source evaluates to with
"${source}"orsource.outPath
I'm going to use a new notation for a filtered source, where // indicates the root anchor:
/my/project//some/subpath/
^ ^
root anchor subpath anchor
# When imported into the store:
/nix/store/<hash>-<name>/some/subpath/Using this format, we can explain better what individual functions do to these two anchors and the filtering. Note that these examples are only illustrative, this is not an exact description.
-
focusAt: Sets the subpath anchor without changing the filtering:focusAt ./some /my/project//some/subpath -> /my/project//some focusAt ./some/subpath/foo /my/project//some/subpath -> /my/project//some/subpath/foo
-
cutAt: Moves the root anchor down, preventing imports of files outside of the new root:cutAt ./some /my/project//some/subpath -> /my/project/some//subpath
-
reparent: Moves the root anchor up, without adding any new files:reparent /my /my/project//some/subpath -> /my//project/some/subpath
-
extend: Adds new files, moves the root anchor up if necessary:extend /my/project//some/subpath [ /my/otherproject/some/file ] -> /my//project/some/subpath
-
empty: Returns a filtered source with a specific root anchor, but with all files filtered out:empty /my/project -> /my/project//
-
getOriginalFocusPath: Returns the subpath anchor:getOriginalFocusPath /my/project//some/subpath -> /my/project/some/subpath
-
getSubpath: Returns the subpath anchor relative to the root anchor. This is what gets appended to the store path result of the underlyingbuiltins.filterSourcecall:getSubpath /my/project//some/subpath -> some/subpath
-
.origSrc(not really exposed): Returns the root anchor. This is thesrcargument to the underlyingbuiltins.filterSourcecall:(/my/project//some/subpath).origSrc -> /my/project
An alternative model
While I think above gives a nice overview of these functions and potentially makes it easier to give them better names, I'd also like to propose a hopefully simpler to understand alternative set of functions. Notably omitting:
-
A way to move the root anchor up (
reparent, part ofextend). Users should declare the root anchor manually. This is what everybody is used to already and should only be necessary once per project repository. -
An identity source (
empty), because it's not necessary if the API is designed to take lists of sources. -
union :: Path -> ListOf SourceLike -> SourceLike: Filters a path to only include the directories/files from the given list. It throws an error if any items have a subpath set. This is a simplerextend. -
setSubpath :: Path -> SourceLike -> SourceLike: Sets the subpath, like doing appending or removing/path/entriesto the source path, essentially path manipulation. We just can't usedirOfand+because those don't propagate the filtering function. This is the same as the previousfocusAt. Note that agetSubpathis needed too. -
limitToSubpath :: SourceLike -> SourceLike: Limits the files that get imported into the store to the the ones under the subpath set withsetSubpath, the result has an emptysubpath. This is like reimporting the path the source is pointing at. This is a simplercutAt. -
Functions to get the `
Notably I think these are easier to separate: union can be introduced in one PR, setSubpath along with the mkDerivation change in another, and limitToSubpath in another one (though it depends on setSubpath)
| if pre == "/" # root is the only path that already ends in "/" | ||
| then true | ||
| else | ||
| hasPrefix (pre + "/") (other + "/"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a bit obscure why + "/" is needed. From what I can tell, it's so that /some/path doesn't match as a prefix of /some/pathpath. While hasPrefix (pre + "/") other would fix that, this would then break for when pre == other. So adding the "/" to other as well makes it work when the same path is a prefix of itself.
| # Function to memoize | ||
| f: | ||
| # What to return when a path does not exist, as a function of the path | ||
| missing: | ||
| # Filesystem location below which the returned function is defined. `/.` may be acceptable, but a path closer to the data of interest is better. | ||
| root: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a rather special function, 3 arguments and no clear order to them, I'd say this benefits from making it an attribute set argument
| # Function to memoize | |
| f: | |
| # What to return when a path does not exist, as a function of the path | |
| missing: | |
| # Filesystem location below which the returned function is defined. `/.` may be acceptable, but a path closer to the data of interest is better. | |
| root: | |
| { | |
| # Function to memoize | |
| f, | |
| # What to return when a path does not exist, as a function of the path | |
| missing, | |
| # Filesystem location below which the returned function is defined. `/.` may be acceptable, but a path closer to the data of interest is better. | |
| root, | |
| } |
|
Thanks for the review. The overview looks good. Anyway, these are my thoughts now. I'll make some of the changes later.
Wait, is this still for omission? I think we're switching to addition now. Note to self: I do still mention Throwing is a usability and design smell. These were intended to be combinators, so functions to operate on any source input. What is the
Correct. I'm ok with this name.
Already added.
👍
Looks like you accidentally your comment 😕
The subpath features don't rely on |
I was thinking of removing
As far as I can see, If somebody needs a subpath of the result, they can do
The path argument of
Ah whoops, was gonna say "Functions to get the |
|
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/advice-wanted-writing-a-build-system-in-nix/23460/4 |
This prevents unnecessary rebuilds of packages that haven't really changed, except for a hash that included irrelevant sources. By applying a trivial source filter, we create a new store path that only contains the subdirectory that the build cares about. This isn't 100% compatible with all projects, as some packages might "legitimately" depend on files outside of their own directory. We could support this by adding an option for sources to be unioned, but a source union function isn't available, _yet_. For progress, check NixOS/nixpkgs#112083 As a workaround we may allow the filtering to be disabled entirely. This could be implemented as a `packages.<name>.filteredSource` option with a default, but for now that seems over-engineered.
|
Recently I worked on an improved set of combinators inspired by this PR. It's currently a draft Nixpkgs PR, but it needs more eyes! So if you're interested in this, please take a look, try it out, and give feedback! The best overview is here: https://discourse.nixos.org/t/easy-source-filtering-with-file-sets/29117 |
|
The |
Make root, subpath internal Adapted from NixOS#112083 Co-Authored-By: Robert Hensing <robert@roberthensing.nl>
Motivation for this change
Improve usability of
lib.sources, specificallylib.sources, primarily:extendto construct sources from pre-existing sources in related directoriesRefactor
cleanSourceWithto operate on a simple internal representation, in preparation for new combinators to be added later.I've discussed a union-like combinator here before.
extendcombines naturally with thesubDirfeature in thehaskell.nixlibrary. This becamesubpath.includeSiblingswas replaced bycutAtand/orextend.Things done
sandboxinnix.confon non-NixOS linux)Tested via one or more NixOS test(s) if existing and applicable for the change (look inside nixos/tests)nix-shell -p nixpkgs-review --run "nixpkgs-review wip"./result/bin/)nix path-info -Sbefore and after)