Skip to content

Conversation

ShamrockLee
Copy link
Contributor

@ShamrockLee ShamrockLee commented May 28, 2023

Description of changes

This PR introduces a unified approach to implementing build helpers that support fixed-point arguments and bring such support to existing build helpers.

The fixed-point arguments support in stdenv.mkDerivation is introduced in #119942, and there are ongoing attempts to make other build helpers support such syntax (buildRustPackage refactor #194475, buildGoModule refactor #225051). The overlay style overrideAttrs brought by the stdenv.mkDerivation change can be used to implement the functionality, which is adopted by the buildRustPackage refactor and the previous version of the buildGoModule refactor. The challenge of such an approach is that the whole set pattern matching the input arguments are degenerated into a single identifier, making it hard to see from the source code which attributes to the build helper accepts.

The new Nixpkgs Library function, lib.extendMkDerivation accepts a base build helper and an attribute overlay (an overlay in the form finalAttrs: args: <attrsToUpdate>), and returns a new build helper by extending the base build helper with the attribute overlay via its <pkg>.overrideAttrs.

The following is the definition of an example build helper, mkLocalDerivation:

lib.extendMkDerivation {
  constructDrv = stdenv.mkDerivation;
  extendDrvArgs =
    finalAttrs:
    {
      preferLocalBuild ? true,
      allowSubstitute ? false,
      ...
    }@args:

    # No need of `// args` here.
    # The whole set of input arguments is passed in advance.
    {
      # Attributes to update
      inherit preferLocalBuild allowSubstitute;
    };
}

For existing build helpers with arguments that cannot be passed to the base build helper, lib.extendMkDerivation provides an attribute removedAttrNames to specify arguments not to pass down to the base build helper.

lib.extendMkDerivation {
  constructDrv = stdenv.mkDerivation;
  extendDrvArgs =
    finalAttrs:
    {
      preferLocalBuild ? true,
      allowSubstitute ? false,
      specialArg ? (_: false),
      ...
    }@args:

    {
      # Arguments to pass
      inherit preferLocalBuild allowSubstitute;
      # Some expressions involving specialArg
      greeting = if specialArg "hi" then "hi" else "hello";
    };

  excludeDrvArgNames = [
    "specialArg"
  ];
}

Aside from removedAttrNames lib.extendMkDerivation, other optional arguments are used to manipulate their behaviors. For example, lib.adaptMkDerivation { transformDrv = toPythonModule; } applies the function toPythonModule to the derivation produced by the derived build helper.

Cc:
Python maintainers: @FRidh @mweinelt @jonringer
Author of the buildRustPackage refactor PR @amesgen
Reviewer of the buildGoModule refactor PR @zowoq
Author of the merged recursive stdenv.mkDerivation PR @roberth
People who mention 119942 in Python application definition: @LunNova

Things done
  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandbox = true set in nix.conf? (See Nix manual)
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 23.05 Release Notes (or backporting 22.11 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
  • Fits CONTRIBUTING.md.

@github-actions github-actions bot added 6.topic: python Python is a high-level, general-purpose programming language. 8.has: documentation This PR adds or changes documentation labels May 28, 2023
@Artturin Artturin requested a review from roberth May 28, 2023 17:41
@ShamrockLee
Copy link
Contributor Author

ShamrockLee commented May 28, 2023

I should have checked more carefully.

There's a function called makeOverride that deprives the input builder of their recursive attributes support, and buildPythonPackage and buildPackageApplication happens to be makeOverrided. While the change keeps the current python packages unchanged, it still doesn't bring the recursive attributes support to buildPythonModule and buildPythonApplication unless we fix makeOverridable.

Updat: fixed, see below.

@ofborg ofborg bot added 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin. 10.rebuild-linux: 1-10 This PR causes between 1 and 10 packages to rebuild on Linux. labels May 28, 2023
@ShamrockLee
Copy link
Contributor Author

ShamrockLee commented May 28, 2023

Fix lib.makeOverridable and makeOverridablePythonPackage. Now buildPython* accepts recursive attributes.
Turn a Python application pyspread into the recursive attributes style.

@ShamrockLee ShamrockLee mentioned this pull request May 28, 2023
12 tasks
@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/proposal-move-values-from-derivation-attributes-to-function-arguments/20697/21

@ofborg ofborg bot requested a review from LunNova May 29, 2023 01:18
@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/avoid-rec-expresions-in-nixpkgs/8293/18

@infinisil infinisil added the 1.severity: significant Novel ideas, large API changes, notable refactorings, issues with RFC potential, etc. label May 29, 2023
@ShamrockLee ShamrockLee changed the title lib.extendRecursiveBuilder: init; mkPythonPackage / mkPythonApplication: support recursive attributes lib.extendRecursiveBuilder: init; buildPython*: support recursive attributes May 29, 2023
Copy link
Member

@roberth roberth left a comment

Choose a reason for hiding this comment

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

A mix of incremental and fundamental suggestions, because I can't decide for the nixpkgs-python maintainers.

I'm concerned about the increasing complexity, while the goal can be achieved through simplification instead: removing the python-specific level of overriding and turning it into an "overlay" on the mkDerivation arguments instead.

The alternative strategy is to

  1. Provide this python layer, which contains the relevant mkPyton* logic in a way that works with overlay-style overriding. This can be done by reading the existing code, attribute for attribute, and adding the logic to the python layer.
  2. Change the python packages to use that layer in combination with mkDerivation, instead of the current mkPython* functions.
  3. Perhaps make the mkPython functions reuse the overlay so that they don't literally reimplement the same logic. I don't know if this would be worthwhile.
  4. Eventually deprecate the mkPython* functions.

I have played around with step 1 of the alternative strategy. It is feasible, but it requires a bit of migration for each package. I'm not a nixpkgs-python maintainer, so I don't feel like I should be the one to make the decision whether to accept the complexity of this PR, or refactor to actually simplify the python logic by fitting it into a mkDerivation layer.

}:

lib.extendRecursiveBuilder stdenv.mkDerivation [ ] (finalAttrs:

Copy link
Member

Choose a reason for hiding this comment

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

It's worth noting that finalAttrs contains the final arguments to mkDerivation attrs, and not the final arguments to the function you're constructing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now the arguments after applying the modifier function is also considered.

IMO, the finalAttrs is meant to be the final state of the attributes passing to the base builder (mkDerivation). That's how user could access finalAttrs.finalPackage and other goodies. If we just want to get the input argument set manually, the user could just lib.fix the function themselves.

Input arguments that doesn't mean to be passed to the base builder are subject to special care, and should never enter the recursion. Those not-to-pass arguments are also the reason why builder overlays are not drop-in replacement of current builders.

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 tried to add those special arguments to finalAttrs, but that greatly increase the complexity of the code and makes behavior surprising (the specified result will be different from what is got through finalAttrs by design).

A better way would be to encourage passing all the arguments. When all the arguments are passed, they will all be available inside finalAttrs, and we could then switch to the overlay-based workflow specification (from the current, build-helper-based one).

in
if builtins.isAttrs result then result
else if builtins.isFunction result then {
overridePythonAttrs = newArgs: makeOverridablePythonPackage f (overrideWith newArgs);
__functor = self: result;
}
else result;
else result);
Copy link
Member

Choose a reason for hiding this comment

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

This feels too custom or repeated (not sure yet which). Does this add mkDerivation-like fixpoint logic at the python attrs level?

I believe we should merge the python attrs level into the mkDerivation attrs, so that the interface and implementation become simpler. Having multiple levels of overriding has a huge complexity cost, so getting rid of an unnecessary level would be a huge win. We'd get rid of overridePythonAttrs and all the user facing complexity, implementation complexity and bugs that come with it.

The python-specific attrs can almost be implemented as an "overlay" on the mkDerivation arguments. When I tried this, I think only like 3 attributes had the same name but a slightly different meaning. That made it a breaking change, but migrating those attributes is feasible and would vastly simplify the python/mkDerivation wiring.

Copy link
Member

Choose a reason for hiding this comment

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

With same names that is generally speaking because the Python builder will extend the lists with some "defaults". That's really the only value of the custom builder over just plain hooks.

Copy link
Contributor Author

@ShamrockLee ShamrockLee May 29, 2023

Choose a reason for hiding this comment

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

This feels too custom or repeated (not sure yet which). Does this add mkDerivation-like fixpoint logic at the python attrs level?

I personally prefer passing most of the attributes into mkDerivation, encourage the use of overrideAttrs and gradually deprecates overridePythonAttrs as well as other builder-specific override methods.

That will be a mass rebuild, so I just work around the makeOverridablePythonPackage obstacle in order to demonstrate the possibility to add the recursive attributes support without rebuild.

@infinisil
Copy link
Member

infinisil commented May 29, 2023

Recently the Packages Modules Working Group started investigating whether something like the module system could be used to evaluate packages. We're tracking all work in this repository, meeting weekly, but working mostly asynchronously. It would be great if you could join the Matrix room of the WG and chat with us, or even join the team yourself to work on such issues!

@ShamrockLee
Copy link
Contributor Author

ShamrockLee commented May 29, 2023

Thank you for taking time reviewing this!

I'm concerned about the increasing complexity, while the goal can be achieved through simplification instead: removing the python-specific level of overriding and turning it into an "overlay" on the mkDerivation arguments instead.

The idea to shift workflow-specific overlays sounds neat, and that could also be friendlier when packaging multi-language projects. Nevertheless, there are some issue on the way to the switch:

  1. Current builders rely more or less on arguments they won't pass into mkDerivation attribute set.
    Developers would need to change the way they handle the arguments / attributes, either by passing them directly or through passthru. The impl input argument of the proposed function is essentially an overlay that allows passing not-to-pass-to-mkDerivation arguments through the previous set.

  2. Current attempt to try to implement a builder that supports the (finalAttrs: { }) through an overlay passing to .overrideAttrs, such as rustPlatform.buildRustPackage: support finalAttrs style #194475, discards the set pattern of input arguments ({ a, b ? "some default", ... }@args) completely and degenerate it into previousAttrs.
    It is a pity, since the set patterns itself serves as an interface of the builder, providing information about the expected input arguments to both and the Nix interpreter and people reading the Nix expression.
    The solution would be rather simple -- replace the previousAttrs with the set pattern, and then the remaining issue will be point 1.

Overall, the goal of the proposed function is to add (finalAttrs: { }) input support with minimum changes to the existing expressions, and raise the discussion about general approaches to bring such supports to builders.

}
```

A list of functions to modify the resulting derivation can be specified after the base builder. Modifications such as overriding and `extendDerivation` can be applied there.
Copy link
Contributor

Choose a reason for hiding this comment

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

Where do we find information about extendDerivation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

lib.customisation is currently not presented in the Nixpkgs manual. We'll need to add the documentation for those functions before referring to them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Besides, the Nixpkgs Library Functions documentation automatically generated from the comment don't have anchors. So there's no way to link against them so far.

Copy link
Contributor Author

@ShamrockLee ShamrockLee Jan 3, 2024

Choose a reason for hiding this comment

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

My fault. The anchor in the form function-library-<long attribute path> is also automatically generated. E. g. function-library-lib.customisation.extendMkDerivation.

This usage is very hard to discover, as the documentation is missing, and the manual provides no hyperlink in the title of each function document.

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 addressed this review in the changes to add a new section about using lib.extendMkDerivatien to define build helpers. Please take a look.

@LunNova
Copy link
Member

LunNova commented May 31, 2023

Just reviewing your change to input-remapper, I like how much this cleaned up its definition. :)

@ShamrockLee ShamrockLee changed the title lib.extendRecursiveBuilder: init; buildPython*: support recursive attributes lib.extendMkDerivation: init; buildPython*: support function-based attribute recursion Jun 2, 2023
@ShamrockLee ShamrockLee force-pushed the extend-builder branch 3 times, most recently from f3c34fb to d868837 Compare June 3, 2023 00:49
@ShamrockLee ShamrockLee deleted the extend-builder branch February 19, 2025 12:54
drupol added a commit to drupol/nixpkgs that referenced this pull request Feb 28, 2025
drupol added a commit to drupol/nixpkgs that referenced this pull request Feb 28, 2025
drupol added a commit to drupol/nixpkgs that referenced this pull request Feb 28, 2025
drupol added a commit to drupol/nixpkgs that referenced this pull request Feb 28, 2025
JohnRTitor pushed a commit that referenced this pull request Mar 1, 2025
JohnRTitor pushed a commit that referenced this pull request Mar 1, 2025
@nixpkgs-ci
Copy link
Contributor

nixpkgs-ci bot commented Mar 16, 2025

Backport failed for release-24.11, because it was unable to cherry-pick the commit(s).

Please cherry-pick the changes locally and resolve any conflicts.

git fetch origin release-24.11
git worktree add -d .worktree/backport-234651-to-release-24.11 origin/release-24.11
cd .worktree/backport-234651-to-release-24.11
git switch --create backport-234651-to-release-24.11
git cherry-pick -x 2439ead79150130e48f6c45409337125f4250685 bbdf8601bcf2a7e733d5ef2552109a5d8d5a44ce

@JohnRTitor
Copy link
Member

We should manually backport all of these PRs. Without these, new package PRs changing to finalAttrs will break eval.

@philiptaron

This comment was marked as outdated.

@bjornfor
Copy link
Contributor

  • What's the risk of backporting this PR?
  • Will anyone want to backport packages that need this PR?

@philiptaron
Copy link
Contributor

We should manually backport all of these PRs. Without these, new package PRs changing to finalAttrs will break eval.

After a night to sleep on it, I agree. I've done so in #390950. Please take a look. It was not so difficult.

nixpkgs-ci bot pushed a commit that referenced this pull request Mar 18, 2025
…poserProject2`

Inspired from #382550

Context: #234651
(cherry picked from commit 862a3e9)
nixpkgs-ci bot pushed a commit that referenced this pull request Mar 18, 2025
…erVendor`

Inspired from #382550

Context: #234651
(cherry picked from commit cd850d5)
@izelnakri izelnakri mentioned this pull request Mar 19, 2025
4 tasks
@ShamrockLee
Copy link
Contributor Author

@philiptaron Based on our previous discussion about the name "derivation constructor" instead of "build helper", would you like to persue a tree-wide (wlole-Nixpkgs-Manual) renaming?

@fricklerhandwerk
Copy link
Contributor

I wouldn't be in favor of a more cumbersome word for something which I think is already described precisely enough by "build helper" (since it's a convenience wrapper for building software, a special case of constructing derivations).

@philiptaron
Copy link
Contributor

I'll defer to Valentin (@fricklerhandwerk) as a representative of @NixOS/documentation-team though I do not share his perspective. I wouldn't merge that update without some rough consensus on it.

@ShamrockLee
Copy link
Contributor Author

ShamrockLee commented May 5, 2025

Thank you both for the input.

I'm drafting a Nix RFC which heavily uses the phrase "build helper," and I'm just trying to make sure that the wording wouldn't get obsolete in the near future.

@philiptaron
Copy link
Contributor

My critique of "build helper" is sort of pedantic, but here it is. This is likely the wrong place to have the discussion, too, so I'd appreciate a redirect to a better place.

As a word, "helper" doesn't mean anything specific. It could be a binary, it could be a hook, it could be // adding more attributes into an attrset... anything that in any sense "helps" can be described as a "helper". So I'd prefer not to call something a helper if there is at all a more specific word.

As a word, "building" is what happens when a derivation is actually realized and instantiated. That's what section 4.5 of the Nix manual, titled "Building", uses to describe the process.

Therefore, the derivation isn't in any sense a "build". It's a derivation, which when "built" will produce some set of outputs.

The word that the Nix manual uses for what stdenv.mkDerivation returns, as I read it, is "derivation expression". It's an expression in the Nix language that yields a derivation. Simple enough.

As I read it, though, that's the combination of the Nix function with its arguments. That's the "derivation expression". So we need a word for the function that, when called with suitable arguments, is a "derivation expression". We could call these "derivation-returning functions", but that's quite general and a bit wordy. So, the best I can come up with is a "derivation constructor" -- some Nix function that, when called, "constructs" a derivation. The most simple "derivation constructor" would be derivationStrict. A mid-level one would be stdenv.mkDerivation. Something like rustPlatform.buildRustApplication would be at the high end of complexity. But they'd all be caught under this term.

So that's the argument, @fricklerhandwerk, both against using "build helper" as a name for these things and in favor of using "derivation constructor" for it.

@fricklerhandwerk
Copy link
Contributor

Thanks @philiptaron, greatly appreciate the elaboration! I see your points and fully agree on the grounds of being precise. My point is that it's sometimes meaningful to trade ease of use (which also relates to existing habits) against precision, at least to some degree, and that's one thing that makes naming hard.

That said, while I wouldn't be in favor of a change, I wouldn't be against a more precise term either that ideally would be easy to adopt for both new and habitual users. And in any case, I'm not the boss of Nixpkgs terminology! A good place to discuss this would be a PR proposing the change, and giving it a bit more exposure by posting it on Discourse.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
1.severity: significant Novel ideas, large API changes, notable refactorings, issues with RFC potential, etc. 2.status: wait for branch‐off Waiting for the next Nixpkgs branch‐off 6.topic: lib The Nixpkgs function library 8.has: clean-up This PR removes packages or removes other cruft 8.has: documentation This PR adds or changes documentation 10.rebuild-darwin: 1-10 This PR causes between 1 and 10 packages to rebuild on Darwin. 10.rebuild-linux: 1-10 This PR causes between 1 and 10 packages to rebuild on Linux.
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.