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

Added composable version of filterSource to lib #33312

Merged
merged 1 commit into from Jan 10, 2018

Conversation

ElvishJerricco
Copy link
Contributor

Addresses NixOS/nix#1767

@ElvishJerricco
Copy link
Contributor Author

@shlevy, how does this look?

@ElvishJerricco
Copy link
Contributor Author

ElvishJerricco commented Jan 2, 2018

@GrahamcOfBorg what constitutes rebuild-darwin/rebuild-linux? AFAICT, this should not require rebuilding any existing uses of the functions in sources.nix.

EDIT: Oh that's a bot.

@ElvishJerricco ElvishJerricco changed the title Added composable version of filterSource to lib WIP: Added composable version of filterSource to lib Jan 2, 2018
@ElvishJerricco
Copy link
Contributor Author

It'd be nice if we could transparently use lib.filterSource on all kinds of sources inputs, including builtins.filterSource, fetchFromGitHub, and of course paths. Would eliminate the complexity in callCabal2nix. Of course it would only be beneficial on paths, but it would be convenient nonetheless.

@ElvishJerricco
Copy link
Contributor Author

I suppose it's not actually possible to apply builtins.filterSource to a derivation output no matter what, so I've settled for a convenience function that checks if a source can be filtered (and added an assertion to make sure we're being given an argument we can use)

@ElvishJerricco ElvishJerricco force-pushed the composable-filter-source branch 2 times, most recently from f85e792 to a60642a Compare January 2, 2018 06:21
@ElvishJerricco ElvishJerricco changed the title WIP: Added composable version of filterSource to lib Added composable version of filterSource to lib Jan 2, 2018
@shlevy
Copy link
Member

shlevy commented Jan 2, 2018

@ElvishJerricco The OfBorg tags say there are 0 linux rebuilds and 0 darwin rebuilds.

Copy link
Member

@shlevy shlevy left a comment

Choose a reason for hiding this comment

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

A few small changes. Have you tested with callCabal2nix?

lib/sources.nix Outdated
# intermediate copies being put in the nix store.
filterSource = f: src:
let isPath = builtins.typeOf src == "path";
in assert isLocalSource src; rec {
Copy link
Member

Choose a reason for hiding this comment

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

Instead of asserting here, why don't we just do builtins.storePath src if it's in the store already? You can't filter in that case but it's already in the store so there's no need anyway.

Copy link
Member

Choose a reason for hiding this comment

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

Or maybe not even do storePath, just pass src as-is.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think it's a good idea to allow filterSource to accept inputs that it won't filter. That would be very misleading

Copy link
Member

Choose a reason for hiding this comment

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

Filtering is an optimization, though...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think so. Filtering can be semantically relevant. e.g. If they want to filter out a particular source file.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm, OK. Better to fail now and be permissive later if we need it I suppose.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe this should take its arguments in an attrset so that we can extend it with new arguments (like, fallBackNoFilter) in the future?

Copy link
Member

Choose a reason for hiding this comment

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

👍

lib/sources.nix Outdated
# allowing you to chain multiple calls together without any
# intermediate copies being put in the nix store.
filterSource = f: src:
let isPath = builtins.typeOf src == "path";
Copy link
Member

Choose a reason for hiding this comment

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

Seems like it would be better to switch on src ? __is-lib-filterSource than isPath. E.g. hydra will pass in paths as { outPath = the-actual-path; etc. } so this will spuriously fail here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wouldn't builtins.filterSource fail on such inputs as well? AFAIK, this will succeed on anything builtins.filterSource succeeds on

Copy link
Member

Choose a reason for hiding this comment

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

filterSource calls coerceToPath, which respects outPath etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just pushed a change. Is that better?

lib/sources.nix Outdated
filter = if isPath then f else name: type: f name type && src.filter name type;
origSrc = if isPath then src else src.origSrc;
outPath = builtins.filterSource filter origSrc;
__is-lib-filterSource = true;
Copy link
Member

Choose a reason for hiding this comment

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

There's nothing standardized here but IMO double underscore should be reserved by nix itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Single underscore then?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah.

lib/sources.nix Outdated
# intermediate copies being put in the nix store.
filterSource = f: src:
let isFiltered = src ? _is-lib-filterSource;
in assert isLocalSource src; rec {
Copy link
Member

Choose a reason for hiding this comment

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

Hmm what does this assertion actually gain us? builtins.filterSource itself will throw here. And note that typeOf src == "path" is not quite exhaustive, as you can call builtins.filterSource on, say toString ./. (though I'm not sure if there are actual cases where that matters)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm. Yea I suppose the only way to exhaustively check that builtins.filterSource can take the argument would be to actually try it and catch any thrown error; but that would defeat the point since it would evaluate the intermediate filtered sources.

I was using the assertion to avoid an error message that's difficult to debug, but you're right; I don't think it will work. But the question remains: How do we have callCabal2nix determine when it's appropriate to try to filter?

Copy link
Member

Choose a reason for hiding this comment

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

callCabal2nix can do something like check (src.type or "") != "derivation"

Copy link
Member

Choose a reason for hiding this comment

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

That's not exhaustive though... I think either you have to do something like check if src is a string and if so if it's in the nix store, or just take an arg to callCabal2nix.

Copy link
Member

Choose a reason for hiding this comment

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

@edolstra Would you object to a hasContext primop? That would solve this...

@ElvishJerricco
Copy link
Contributor Author

@shlevy Alright I think I've addressed all your points. Is this what you had in mind?

lib/sources.nix Outdated
# intermediate copies being put in the nix store.
filterSource = { filter, src }:
let isFiltered = src ? _is-lib-filterSource;
in assert isLocalSource src; rec {
Copy link
Member

Choose a reason for hiding this comment

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

Does isLocalSource exist any more?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Derp. Fixed

lib/sources.nix Outdated
filter = if isFiltered then name: type: filter name type && src.filter name type else filter;
origSrc = if isFiltered then src.origSrc else src;
outPath = builtins.filterSource filter origSrc;
_is-lib-filterSource = true;
Copy link
Member

Choose a reason for hiding this comment

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

Please don't use dashes in variable names.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@ElvishJerricco
Copy link
Contributor Author

@shlevy One other thing we should probably think about is how callCabal2nix will make use of this. You suggested using src.type or "" == "derivation" to select which sources can't be filtered, but as you said, it's non-exhaustive. In particular, the only case that matters IMO is when src is itself a builtins.filterSource. I'd be ok with just failing in this case if callCabal2nix were a new feature, since users could just use lib.filterSource. But that's not acceptable since we have to be backward compatible.

lib/sources.nix Outdated
# Like `builtins.filterSource`, except it will compose with itself,
# allowing you to chain multiple calls together without any
# intermediate copies being put in the nix store.
filterSource = { filter, src }:
Copy link
Member

Choose a reason for hiding this comment

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

Please don't use the same name as a builtin function. Call it composableFilterSource or something like that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

EDIT: Wrong thread

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was doing as fetchTarball/fetchurl do. I'm hesitant to call it something like composableFilterSource, since I think that this should be the recommended way to filter sources instead of the builtin function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@peti @edolstra How about cleanSourceWith? Avoids the naming clash but remains generic.

Copy link
Member

Choose a reason for hiding this comment

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

cleanSourceWith sounds good to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@shlevy
Copy link
Member

shlevy commented Jan 2, 2018

@ElvishJerricco IMO we shouldn't hold ourselves to especially strong compatability guarantees across NixOS releases, but I'll defer to @peti on that one.

@shlevy
Copy link
Member

shlevy commented Jan 2, 2018

@ElvishJerricco I think NixOS/nix#1772 would be a general solution though.

Copy link
Member

@peti peti left a comment

Choose a reason for hiding this comment

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

@ElvishJerricco
Copy link
Contributor Author

@shlevy Forgive me; I don't know much about Nix internals. Would NixOS/nix#1772 require evaluating builtins.filterSource to do the check? Wouldn't that put the source into the nix store prematurely, defeating the purpose?

@ElvishJerricco ElvishJerricco force-pushed the composable-filter-source branch 3 times, most recently from 4db2546 to 9048189 Compare January 2, 2018 18:13
@ElvishJerricco
Copy link
Contributor Author

@peti Do you think it's ok to break existing uses of haskellPackages.callCabal2nix "foo" (builtins.filterSource ...) {} and instruct users to switch to cleanSourceWith? If not, we may want to hold off on merging this, as it doesn't really solve what I was originally hoping to solve.

filter = filter';
outPath = builtins.filterSource filter' origSrc;
_isLibCleanSourceWith = true;
};

# Filter sources by a list of regular expressions.
#
# E.g. `src = sourceByRegex ./my-subproject [".*\.py$" "^database.sql$"]`
Copy link
Contributor

Choose a reason for hiding this comment

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

IIRC this is actually a misleading example. I believe this will not recurse into directories unless they match these regexes. So in many cases this example would actually filter out everything and leave you with an empty folder. I need to double-check that this is the case but that's how it has appeared in my experience.

Copy link
Contributor

Choose a reason for hiding this comment

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

I realize this is tangential to your actual change. Feel free to ignore while I look into opening a separate PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

I've confirmed this. I'll open a separate PR.


# Like `builtins.filterSource`, except it will compose with itself,
# allowing you to chain multiple calls together without any
# intermediate copies being put in the nix store.
Copy link
Contributor

Choose a reason for hiding this comment

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

An example would be nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

lib/sources.nix Outdated
# Like `builtins.filterSource`, except it will compose with itself,
# allowing you to chain multiple calls together without any
# intermediate copies being put in the nix store.
cleanSourceWith = { filter, src }:
Copy link
Contributor

Choose a reason for hiding this comment

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

An additional/alternative design that I've used is to write composeFilter and just use that to build up bigger filters.

@peti
Copy link
Member

peti commented Jan 3, 2018

Do you think it's ok to break existing uses of haskellPackages.callCabal2nix "foo" (builtins.filterSource ...) {} and instruct users to switch to cleanSourceWith?

I don't know ... I have no idea how many people are actually using this idiom and would therefore be affected by this change. Personally, I think that it's okay'ish to break backwards-compatibility if there is a sufficiently self-explanatory error message.

@ElvishJerricco
Copy link
Contributor Author

Can this be merged?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants