-
-
Notifications
You must be signed in to change notification settings - Fork 161
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
[RFC 0032] Phase running changes for better nix-shell use #32
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
1479cad
[RFC0032] Phase running changes for better nix-shell use
dezgeg a16dadf
Add syntax highlighting to code blocks
dezgeg 928b72b
[RFC0032] Reword summary to mention the 2nd goal
dezgeg 3da99ac
[0032] Add shepherd team to metadata
lheckemann 6e48c1e
[0032] Make @globin primary author
lheckemann 4a109d5
RFC32: no more unresolved questions
lheckemann File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
180 changes: 180 additions & 0 deletions
180
rfcs/0032-run-phase-changes-for-better-nix-shell-use.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
--- | ||
feature: run-phase-changes-for-better-nix-shell-use | ||
start-date: 2018-08-26 | ||
author: @globin | ||
co-authors: @dezgeg | ||
related-issues: https://github.com/dezgeg/nixpkgs/tree/better-run-phases | ||
shepherd-team: Samuel Dionne-Reil, John Ericson, Linus Heckemann | ||
shepherd-leader: Linus Heckemann | ||
--- | ||
|
||
# Summary | ||
[summary]: #summary | ||
|
||
The intent of this proposal is to tweak a bit how the stdenv runs phases during a build to achieve these two goals: | ||
1. Improve the UX of nix-shell by making it easier to manually run phases inside a nix-shell | ||
2. Improve the consistency of pre/post hooks by always running such hooks instead of only running them in non-overridden phases | ||
|
||
# Motivation | ||
[motivation]: #motivation | ||
|
||
The primary goal of this RFC is to make it easier to run build phases manually inside a nix-shell. | ||
Currently, it's a bit annoying because to run say, `buildPhase` the only invocation that works correctly in all cases is: | ||
|
||
````sh | ||
eval "${buildPhase:-buildPhase}" | ||
```` | ||
|
||
The goal is to be able to replace the above with the obvious: | ||
````sh | ||
buildPhase | ||
```` | ||
|
||
The secondary goal is to change how hooks (like `preBuild`, `postBuild` etc.) interact with overridden phases. | ||
For example a derivation `foo-package` doing, | ||
````nix | ||
stdenv.mkDerivation { | ||
# ... | ||
|
||
buildPhase = '' | ||
./my-funky-build-script.sh | ||
''; | ||
|
||
# ... | ||
} | ||
```` | ||
causes `preBuild` and `postBuild` not to be called anymore. | ||
In 99% of the cases this isn't a problem, but it can cause hidden annoyances when using `.overrideAttrs`, for instance: | ||
```` | ||
foo-package.overrideAttrs (attrs: { | ||
postBuild = (attrs.postBuild or "") + '' | ||
# whatever | ||
''; | ||
}) | ||
```` | ||
Which has led to some people adding explicit `runHook preFoo` and `runHook postFoo` calls to a (small) number of packages. | ||
|
||
Thus, to counter this inconsistency, this RFC proposes that those hooks will be run even when the phase is overridden. | ||
|
||
# Detailed design | ||
[design]: #detailed-design | ||
|
||
All the 'phase' functions in Nixpkgs need to be reworked a bit. Conceptually, the following diff will be applied to each of them: | ||
````diff | ||
-buildPhase() { | ||
+defaultBuildPhase() { | ||
- runHook preBuild | ||
- | ||
# set to empty if unset | ||
: ${makeFlags=} | ||
|
||
@@ -1008,14 +1104,14 @@ buildPhase() { | ||
make ${makefile:+-f $makefile} "${flagsArray[@]}" | ||
unset flagsArray | ||
fi | ||
- | ||
- runHook postBuild | ||
} | ||
+ | ||
+buildPhase() { | ||
+ runHook preBuild | ||
+ | ||
+ if [ -n "$buildPhase" ]; then | ||
+ eval "$buildPhase" | ||
+ else | ||
+ defaultBuildPhase | ||
+ fi | ||
+ | ||
+ runHook postBuild | ||
+} | ||
|
||
```` | ||
That is, the logic of 'if variable `$buildPhase` is set, then eval the contents of `$buildPhase`, otherwise call the function `buildPhase` which contains the default implementation' is pulled down from `genericBuild` to the `buildPhase` function itself | ||
and the function responsible for the default implementation is now renamed to `defaultBuildPhase`. | ||
Then, the `runHook` calls are pulled up from the default phase implementation to the new `buildPhase` function itself. | ||
|
||
The actual logic is abstracted to helper function I've named `commonPhaseImpl` (bikeshedding on the name welcome). Thus the implementation of `buildPhase` presented above will be this one-liner: | ||
````sh | ||
buildPhase() { | ||
commonPhaseImpl buildPhase --default defaultBuildPhase --pre-hook preBuild --post-hook postBuild | ||
} | ||
```` | ||
|
||
## Backwards compatibility | ||
|
||
The changed semantics proposed here might break some out-of-tree packages. | ||
Fortunately most of it should be avoidable by writing some backwards-compatibility code that will allow extra time for out-of-tree code to migrate. | ||
In the order of how frequent the problem will happen (in my view), the following things are problematic: | ||
|
||
1. Out-of-tree packages which have explicit `runHook preFoo` and `runHook postFoo` in their overridden `fooPhase`. | ||
2. Out-of-tree custom phases. | ||
3. Out-of-tree packages that expect calls like `buildPhase` to call the default implementation. | ||
|
||
For 1., the problem is that the `preFoo` and `postFoo` hooks would get executed twice. | ||
We can avoid this by having `commonPhaseImpl` 'poison' the hooks for the duration of the overridden phase | ||
by temporarily setting `preFoo` and `postFoo` to some function that just prints a warning message. | ||
|
||
For 2., the problem is what should `genericBuild` do if both `$fooPhase` the variable and `fooPhase` the function exists. | ||
- For "new-style" phases (i.e. ones migrated to use `commonPhaseImpl`) the only right thing to do is to call the function `fooPhase`. | ||
- For "old-style" phases (i.e. ones that have not migrated to `commonPhaseImpl` yet) the only right thing to do is `eval "$fooPhase"`. | ||
Thus, a way to detect between the two cases needs to be made. I currently have a check among the lines of `declare -f "$curPhase" | grep -q commonPhaseImpl`. | ||
That is, grep the definition of the function to see if the word `commonPhaseImpl` appears there, which is a quite crude hack but will probably work in practice. | ||
An alternative would be to have a associative array where the phases could declare that they are 'new-style' (e.g. stdenv's setup.sh would have `isNewStylePhase[buildPhase]=1` somewhere and so on). | ||
|
||
For 3., the specific problem is that some (very few) packages do something like this: | ||
```` | ||
stdenv.mkDerivation { | ||
# ... | ||
|
||
buildPhase = '' | ||
buildPhase | ||
(cd foo; buildPhase) | ||
(cd bar; buildPhase) | ||
''; | ||
|
||
# ... | ||
} | ||
```` | ||
Which will now call the overridden `buildPhase` and recurse infinitely until Bash crashes with a segfault. | ||
To counter this, `commonPhaseImpl` will detect recursion from a phase to itself and fail the build with an error message, | ||
instructing that the code here needs to be changed to `defaultBuildPhase`. | ||
|
||
Of these three, only 3. needs immediate changes to out-of-tree code. The other two can be kept as a notice/warning for some time, | ||
enabling users to write Nix expressions that are compatible with both old and new Nixpkgs versions simply by not migrating immediately. | ||
|
||
# Drawbacks | ||
[drawbacks]: #drawbacks | ||
|
||
This proposal will (eventually) force some users to change their code as previously listed in the 'Backwards compatibility' section. | ||
|
||
Nixpkgs developers will have to learn this new way of implementing phases. | ||
|
||
# Alternatives | ||
[alternatives]: #alternatives | ||
|
||
An alternative which has been discussed at some point is to have a function like: | ||
````sh | ||
runPhase() { | ||
local phase="$1" | ||
eval "${!phase:-$phase}" | ||
} | ||
```` | ||
which would mean a nix-shell user would write e.g. `runPhase buildPhase` to run `buildPhase` and have it always work correctly. | ||
|
||
While that would be an improvement to status quo, I don't feel that is a sufficient solution, | ||
because there still would be a function `buildPhase` in scope, where running just `buildPhase` would work *sometimes*, | ||
but silently do the wrong thing sometimes. | ||
|
||
Not doing this RFC at all would also be an option, given that the issue being fixed is a pure UX issue, not fixing it wouldn't prevent any other work from happening. | ||
|
||
# Unresolved questions | ||
[unresolved]: #unresolved-questions | ||
|
||
All resolved. | ||
|
||
# Future work | ||
[future]: #future-work | ||
|
||
There's an open ticket (at https://github.com/NixOS/nixpkgs/issues/5483) titled 'Does it make sense for propagatedBuildInputs to get written out in fixupPhase?' | ||
which point out that `propagatedBuildInputs` file doesn't get written out if `fixupPhase` is disabled. | ||
This RFC opens the door for having some parts of a phase not user-overridable, which could be used to avoid the `propagatedBuildInputs`-not-written problem. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Has this not been implemented yet, or implemented differently?
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.
Haven't seen @dezgeg in a while, don't think this ever got implemented.
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.
Fwiw, NixOS/nixpkgs#175605 suggests a perhaps less breaking solution that's opt-in. Opting in is in itself messy though, so that's a reason to prefer this RFC if feasible.