-
-
Notifications
You must be signed in to change notification settings - Fork 14.2k
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
source combinators: extend, trace, cutAt, focusAt and more #112083
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 |
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.filterSource
call:getSubpath /my/project//some/subpath -> some/subpath
-
.origSrc
(not really exposed): Returns the root anchor. This is thesrc
argument to the underlyingbuiltins.filterSource
call:(/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/entries
to the source path, essentially path manipulation. We just can't usedirOf
and+
because those don't propagate the filtering function. This is the same as the previousfocusAt
. Note that agetSubpath
is 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:extend
to construct sources from pre-existing sources in related directoriesRefactor
cleanSourceWith
to operate on a simple internal representation, in preparation for new combinators to be added later.I've discussed a union-like combinator here before.
extend
combines naturally with thesubDir
feature in thehaskell.nix
library. This becamesubpath
.includeSiblings
was replaced bycutAt
and/orextend
.Things done
sandbox
innix.conf
on 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 -S
before and after)