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

stdenv: support meta.broken as a string to explain why it's broken #109407

Closed
wants to merge 3 commits into from

Conversation

B4dM4n
Copy link
Contributor

@B4dM4n B4dM4n commented Jan 14, 2021

Motivation for this change

This draft is based on the idea of @FRidh in #48663 to add a new meta.disabledBecause attribute which can describe why a Python package can't be build. Instead of adding a new attribute with a similar purpose as an existing one (meta.broken), I tried to add the wanted feature to the existing attribute.

meta.brokenBecause allows to add a custom text to the current Package ... is marked as broken ... message. Setting a message also implies broken = true to reduce the chance of only setting a message, but not actually marking the package as broken.

Instead of only booleans, meta.broken can now also be set to a string containing a descriptive message which will be included in the Package ... is marked as broken ... message.

For example the message for disabled Python would look like this:

Package ‘python2.7-sqlparse-0.4.1’ in /.../pkgs/development/python-modules/sqlparse/default.nix:29 is marked as broken because interpreter python2.7 is not supported by this version, refusing to evaluate.

a) To temporarily allow broken packages, you can use an environment variable
...

I included the buildPythonPackage change in this PR to show the potential usage of the attribute. It can be split off when needed.

Things done
  • Tested using sandboxing (nix.useSandbox on NixOS, or option sandbox in nix.conf on non-NixOS linux)
  • Built on platform(s)
    • NixOS
    • macOS
    • other Linux distributions
  • Tested via one or more NixOS test(s) if existing and applicable for the change (look inside nixos/tests)
  • Tested compilation of all pkgs that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review wip"
  • Tested execution of all binary files (usually in ./result/bin/)
  • Determined the impact on package closure size (by running nix path-info -S before and after)
  • Ensured that relevant documentation is up to date
  • Fits CONTRIBUTING.md.

@veprbl
Copy link
Member

veprbl commented Jan 15, 2021

Some bikeshedding:

  1. This would warrant adding of lib.optionalBroken = cond: because: if cond then because else false;
  2. Also may want to just allow strings in the old attribute meta.broken = lib.optionalBroken stdenv.isFoo "because ...";

@B4dM4n
Copy link
Contributor Author

B4dM4n commented Jan 15, 2021

Some bikeshedding:

  1. This would warrant adding of lib.optionalBroken = cond: because: if cond then because else false;
  2. Also may want to just allow strings in the old attribute meta.broken = lib.optionalBroken stdenv.isFoo "because ...";

I like your ideas. This makes it even simpler to specify a descriptive error message.

@B4dM4n
Copy link
Contributor Author

B4dM4n commented Jan 15, 2021

A question I'm not sure about is, what the checkValidity function should return for the broken attribute:

https://github.com/NixOS/nixpkgs/blob/2a9dc3f48d11517be25c4817c634fd6db22d1f81/pkgs/stdenv/generic/check-meta.nix#L273-L279

Is this supposed to be a bool which is used by tools to determine the state of a package or should this propagate the original attribute / message?

As this is the value that the derivation receives, it might break some tools when changed.

@FRidh FRidh added this to the 21.05 milestone Jan 20, 2021
@holymonson
Copy link
Contributor

Do we really need lib.optionalBroken? Since we already have lib.optionalString, it seems easier if we test "" like false.

@primeos primeos mentioned this pull request Feb 10, 2021
10 tasks
@@ -61,7 +61,7 @@ in buildPythonPackage {
platform = if stdenv.isDarwin then "mac" else "linux";
unit = if cudaSupport then "gpu" else "cpu";
key = "${platform}_py_${pyVerNoDot}_${unit}";
in fetchurl packages.${key};
in fetchurl packages.${key} or { };
Copy link
Member

Choose a reason for hiding this comment

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

is it relevant ?

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'm not sure why it's needed, but running nixpkgs-review without it will fail:

error: attribute 'linux_py_39_cpu' missing
nixpkgs-review error
$ nix-env --option system x86_64-linux -f /home/fabian/.cache/nixpkgs-review/rev-a249429bb8a506c78a39a49ff3ebbe34f8b9ba9d/nixpkgs -qaP --xml --out-path --show-trace --meta
error: attribute 'linux_py_39_cpu' missing

     at /home/fabian/.cache/nixpkgs-review/rev-a249429bb8a506c78a39a49ff3ebbe34f8b9ba9d/nixpkgs/pkgs/development/python-modules/tensorflow/bin.nix:69:15:

         68|     key = "${platform}_py_${pyVerNoDot}_${unit}";
         69|   in fetchurl packages.${key};
           |               ^
         70|

     … while evaluating the attribute 'src.name'

     at /home/fabian/.cache/nixpkgs-review/rev-a249429bb8a506c78a39a49ff3ebbe34f8b9ba9d/nixpkgs/pkgs/development/python-modules/tensorflow/bin.nix:64:3:

         63|
         64|   src = let
           |   ^
         65|     pyVerNoDot = lib.strings.stringAsChars (x: if x == "." then "" else x) python.pythonVersion;

     … while evaluating 'hasSuffix'

     at /home/fabian/.cache/nixpkgs-review/rev-a249429bb8a506c78a39a49ff3ebbe34f8b9ba9d/nixpkgs/lib/strings.nix:234:5:

        233|     # Input string
        234|     content:
           |     ^
        235|     let

     … from call site

     at /home/fabian/.cache/nixpkgs-review/rev-a249429bb8a506c78a39a49ff3ebbe34f8b9ba9d/nixpkgs/pkgs/development/interpreters/python/mk-python-derivation.nix:121:25:

        120|       pythonRemoveBinBytecodeHook
        121|     ] ++ lib.optionals (lib.hasSuffix "zip" (attrs.src.name or "")) [
           |                         ^
        122|       unzip

     … while evaluating 'optionals'

     at /home/fabian/.cache/nixpkgs-review/rev-a249429bb8a506c78a39a49ff3ebbe34f8b9ba9d/nixpkgs/lib/lists.nix:270:5:

        269|     # List to return if condition is true
        270|     elems: if cond then elems else [];
           |     ^
        271|

     … from call site

     at /home/fabian/.cache/nixpkgs-review/rev-a249429bb8a506c78a39a49ff3ebbe34f8b9ba9d/nixpkgs/pkgs/development/interpreters/python/mk-python-derivation.nix:121:10:

        120|       pythonRemoveBinBytecodeHook
        121|     ] ++ lib.optionals (lib.hasSuffix "zip" (attrs.src.name or "")) [
           |          ^
        122|       unzip

     … while evaluating 'chooseDevOutputs'

     at /home/fabian/.cache/nixpkgs-review/rev-a249429bb8a506c78a39a49ff3ebbe34f8b9ba9d/nixpkgs/lib/attrsets.nix:497:22:

        496|   /* Pick the outputs of packages to place in buildInputs */
        497|   chooseDevOutputs = drvs: builtins.map getDev drvs;
           |                      ^
        498|

     … from call site

     … while evaluating the attribute 'nativeBuildInputs' of the derivation 'python3.9-tensorflow-2.4.0'

     at /home/fabian/.cache/nixpkgs-review/rev-a249429bb8a506c78a39a49ff3ebbe34f8b9ba9d/nixpkgs/pkgs/stdenv/generic/make-derivation.nix:201:11:

        200|         // (lib.optionalAttrs (attrs ? name || (attrs ? pname && attrs ? version)) {
        201|           name =
           |           ^
        202|             let

     … while evaluating the attribute 'out.outPath'

     at /home/fabian/.cache/nixpkgs-review/rev-a249429bb8a506c78a39a49ff3ebbe34f8b9ba9d/nixpkgs/lib/customisation.nix:156:13:

        155|             drvPath = assert condition; drv.${outputName}.drvPath;
        156|             outPath = assert condition; drv.${outputName}.outPath;
           |             ^
        157|           };

     … while querying the derivation named 'python3.9-tensorflow-2.4.0'

Running nix-instantiate --eval on the other hand fails with the correct error:

nix-instantiate --eval --strict . -A python39Packages.tensorflow-bin.outPath
error: Package ‘python3.9-tensorflow-2.4.0’ in /home/fabian/Repo/nixpkgs/pkgs/development/python-modules/tensorflow/bin.nix:192 is marked as broken because interpreter python3.9 is not supported by this version, refusing to evaluate.

       a) To temporarily allow broken packages, you can use an environment variable
          for a single invocation of the nix tools.

            $ export NIXPKGS_ALLOW_BROKEN=1

       b) For `nixos-rebuild` you can set
         { nixpkgs.config.allowBroken = true; }
       in configuration.nix to override this.

       c) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add
         { allowBroken = true; }
       to ~/.config/nixpkgs/config.nix.
(use '--show-trace' to show detailed location information)

lib/trivial.nix Outdated
=> false
*/
optionalBroken = cond: because:
if cond then because else false;
Copy link
Member

Choose a reason for hiding this comment

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

lets use optionalString instead

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using optionalString is possible, but I tried to avoid a second not broken value (false and "") and optionalBroken was my workaround.

But I admit, using optionalString would remove the need for a new lib function and perform the same task.

@teto
Copy link
Member

teto commented May 19, 2021

lets use optionalString instead of optionalBroken. This PR would be very helpful for the haskell ecosystem where packages regularly break. I wanted to update one package and I know from experience this can be a painful but sometimes a fix is just a jailbreak/donTest away and so being able to distinguish between the cases would be nice.

Package ‘wide-word-0.1.1.2’ in /home/teto/nixpkgs2/pkgs/development/haskell-modules/hackage-packages.nix:280435 is marked as broken, refusing to evaluate.

@@ -53,7 +53,7 @@ let
hasLicense attrs &&
isUnfree (lib.lists.toList attrs.meta.license);

isMarkedBroken = attrs: attrs.meta.broken or false;
isMarkedBroken = attrs: attrs.meta.broken or false != false;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
isMarkedBroken = attrs: attrs.meta.broken or false != false;
isMarkedBroken = attrs: attrs.meta.broken or true;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This change would mark all packages without a meta.broken attribute as broken.

The original meta.broken or false != false is correct, because the or is binding stronger than the !=.

@B4dM4n
Copy link
Contributor Author

B4dM4n commented Jun 1, 2021

I rebased the changes and replaced optionalBroken with optionalString as suggested, because it removes the need for a new lib function.

I'm still not sure whether the return value for broken should contain the string or stay a boolean.

A question I'm not sure about is, what the checkValidity function should return for the broken attribute:

https://github.com/NixOS/nixpkgs/blob/2a9dc3f48d11517be25c4817c634fd6db22d1f81/pkgs/stdenv/generic/check-meta.nix#L273-L279

Is this supposed to be a bool which is used by tools to determine the state of a package or should this propagate the original attribute / message?

As this is the value that the derivation receives, it might break some tools when changed.

@teto teto changed the title stdenv: add meta.brokenBecause attribute stdenv: support meta.broken as a string to explain why it's broken Jun 1, 2021
@teto
Copy link
Member

teto commented Jun 8, 2021

I managed to make this pass the test locally via rebasing + this diff:

diff --git a/pkgs/development/python-modules/zipfile36/default.nix b/pkgs/development/python-modules/zipfile36/default.nix
index 46dd1e173fb..d9ddf6e66a3 100644
--- a/pkgs/development/python-modules/zipfile36/default.nix
+++ b/pkgs/development/python-modules/zipfile36/default.nix
@@ -27,6 +27,6 @@ buildPythonPackage rec {
     description = "Read and write ZIP files - backport of the zipfile module from Python 3.6";
     homepage = "https://gitlab.com/takluyver/zipfile36";
     license = lib.licenses.psfl;
-    maintainers = lib.maintainers.fridh;
+    maintainers = [ lib.maintainers.fridh ];
   };
 }
diff --git a/pkgs/stdenv/generic/check-meta.nix b/pkgs/stdenv/generic/check-meta.nix
index 8c8b60c8021..53a734c9a6e 100644
--- a/pkgs/stdenv/generic/check-meta.nix
+++ b/pkgs/stdenv/generic/check-meta.nix
@@ -252,7 +252,8 @@ let
 
   checkMetaAttr = k: v:
     if metaTypes?${k} then
-      if metaTypes.${k}.check v then null else "key '${k}' has a value ${toString v} of an invalid type ${builtins.typeOf v}; expected ${metaTypes.${k}.description}"
+    # ${toString v}
+      if metaTypes.${k}.check v then null else "key '${k}' has a value of an invalid type ${builtins.typeOf v}; expected ${metaTypes.${k}.description}"
     else "key '${k}' is unrecognized; expected one of: \n\t      [${lib.concatMapStringsSep ", " (x: "'${x}'") (lib.attrNames metaTypes)}]";
   checkMeta = meta: if shouldCheckMeta then lib.remove null (lib.mapAttrsToList checkMetaAttr meta) else [];

@fricklerhandwerk
Copy link
Contributor

fricklerhandwerk commented Jun 11, 2021

I'm still not sure whether the return value for broken should contain the string or stay a boolean.

After searching around the code it does not look like it would immediately break anything if we allowed strings in meta.broken. There are a few callers of the attribute in a bunch of packages, and only changing those values in the package definition to strings will throw errors where conditionals expect a boolean. I'm in favor of keeping it consistent and boolean. Dynamic union types confuse me often enough. I'd like to keep them at a minimum, and only if necessary for backwards compatibility hacks.

Other than that - great pull request, looks ready for un-draft-ing to me.

@teto
Copy link
Member

teto commented Oct 2, 2021

I've opened a rebased version #140325

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

7 participants