guix-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[bug#51838] [PATCH v5 07/45] guix: node-build-system: Add #:absent-depen


From: Philip McGrath
Subject: [bug#51838] [PATCH v5 07/45] guix: node-build-system: Add #:absent-dependencies argument.
Date: Mon, 20 Dec 2021 14:33:34 -0500
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.3.1

Hi,

On 12/18/21 20:02, Liliana Marie Prikler wrote:
Am Samstag, dem 18.12.2021 um 17:55 -0500 schrieb Philip McGrath:
somewhat, but it was immensely helpful to be able to find in node-
xyz.scm all of the packages that wanted, but did not have, node-debug.
I imagine it would be even more useful in a more tangled dependency
graph.
That might be beneficial in your particular use case here, but you also
have to think about the maintenance burden your mechanism introduces.
What if another leftpad happens and we have to speedrun our core-
updates cycle to add #:absent-dependencies everywhere?

I don't understand the scenario you have in mind here.

As I remember/understand the left-pad debacle, the package was deleted from the NPM registry by its author. I don't understand how that would require changes to Guix package definitions. Hopefully, Software Heritage would save the day. Perhaps Guix would choose to provide an alternate, compatible package implementing that single, trivial function, but we could do that by just changing the definition of the hypothetical Scheme variable `node-left-pad`. Eventually, downstream packages would presumably make changes, but we'd have to address those changes anyway when updating those packages.

Even if I assume a scenario that is going to be fixed by removing a hypothetical `node-left-pad` packages from all the packages to which it is an input, that would change O(n) lines of code: having to change #:absent-dependencies in all cases would just be O(2n) lines, which doesn't seem prohibitive. Indeed, I think I would be glad to have an entry in #:absent-dependencies in such a case, communicating explicitly in the Guix repository that the package was affected and Guix packagers adopted a workaround. If I'm later updating the package, I can check to see if the workaround is still needed or if upstream has dropped node-left-pad. In any case, I much prefer to have this written explicitly in code in the Guix repository, rather than relying on external mechanisms like build logs and upstream source files.

Additionally, I think the use case I encountered with node-debug is likely to come up fairly often. For example, someone might notice that a lot of packages use `node-tap` for testing, package it for Guix, and then want to find all of the downstream packages to which to add it and enable tests.

I think we should optimize for the kind of high-quality packages we
aspire to have in mainline Guix, where eventually we hope to have all
sufficiently useful libre Node.js packages available. In that case,
#:absent-dependencies makes it explicit when Guix packagers are
overriding an upstream decision. While we work to achieve that reality,
I think #:absent-dependencies is a much better place for a to-do list
than having to search build logs or source "package.json"s. However...
Compare this to tests.  We have a keyword to disable all tests, which
defaults to #f and we have other idioms for disabling particular tests.
Your use case is no different.  If at all, we should only have a
keyword to disable the check completely and other idioms to e.g. patch
the package.json file so that sanity-check/patch-dependencies/what-
have-you doesn't error when it relies on its contents.

I don't mean to be dense, but I feel like I'm missing something. I assume the reason we don't have a declarative, high-level mechanism for disabling specific tests is that there isn't a general convention for doing that, AFAIK. We do have `#:configure-flags`, which can be used to pass things like `--disable-whatever`, even though, in principle, that could be done by replacing the configure phase. I see #:absent-dependencies as similar: it provides, IMO, a readable, declarative mechanism to make a commonly-needed adjustment to the behavior of the patch-dependencies phase.

I can see the use of a "warn" mode for hacking things together quickly
without having to totally delete configure. I think this could coexist
with #:absent-dependencies and could be done in a follow-on to this
patch series.

--8<---------------cut here---------------start------------->8---
(if (or (member key absent-dependencies)
          (and (memq 'infer absent-dependencies)
               (not (hash-ref index key #f))))
      acc
      (acons key (hash-ref index key value) acc))
--8<---------------cut here---------------end--------------->8---
You're actively making the code that resolves dependencies worse to
look at only to keep the keyword.  Don't.  There are tools besides a
hammer.

Would that meet your objective?
No.  As I repeatedly pointed out, I want either no keyword addition for
this "feature" or a keyword that can be regarded as essentially boolean
if not outright implemented as one.

Reading this again, the existing lines already do what I want (hash-ref
index key value) spits out value as a default if key is not found.  So
the solution is to not touch patch-dependencies at all; we don't have
to abuse a function that's not meant for sanity checking to check
sanity.

To clarify, I thought you wanted `node-build-system` to issue a warning and drop dependencies not supplied as package inputs. Is that correct? In the existing code, if `key` is not found and `value` is returned, the configure phase (i.e. `npm install`) will always fail. (The name `patch-dependencies` may be a little vague about the actual purpose of this phase.)

We never change APIs gratuitously.
In my personal opinion, #:absent-dependencies would be a gratuitous
change in API.  There's no need to have this in the build system.  We
should rather look into ways that make it possible/easy for users to
patch the package.json file between unpack and configure.

I don't think adding #:absent-dependencies is a breaking change in the API at all. Any package that builds currently should continue to build with #:absent-dependencies support added, and indeed with this entire patch series.

This also calls back to my earlier point of the assoc-set! being out of
place, which itself is also a manifestation of node-build-system not
exposing adequate primitives towards rewriting essential files.  For
instance, the procedure

   (lambda (file proc)
     (with-atomic-file-replacement file
       (lambda (in out)
         (write-json (proc (read-json in))))))

would probably deserve its own name and export from node-build-system.
Yes, you can export utility procedures from (guix build *-build-
system), look at the Emacs and Python build systems for example.

I do agree that we should provide more utilities for transforming "package.json" in general ways. It would be nice to make such transformations at least as convenient as more fragile ones using `substitute*`. But, as I wrote earlier, that seems out of scope for this patch series.


With this in place, we both get to have our cakes and eat it too.
There would be no keyword added, and package maintainers would drop
"absent" dependencies in a phase like so

   (add-after 'unpack 'drop-dependencies
     (lambda _
       (with-atomic-json-replacement "package.json"
         (lambda (json)
           (map (match-lambda
                  (('dependencies '@ . DEPENDENCIES)
                   (filter away unwanted dependencies))
                  (('devDependencies '@ . DEPENDENCIES)
                   (same))
                  (otherwise otherwise))
                json)))))

Of course, if that's too verbose, you can also expose that as a
function from node-build-system.  Then all we'd have to bikeshed is the
name, which would be comparatively simple.

Does that sound like a reasonable plan to you?

I'm not sure how to proceed here.

I still think the #:absent-dependencies keyword, with or without a "warn" mode, is the best approach. It has sounded like others on this thread also liked the approach, though I don't want to speak for anyone but myself.

I can understand that you would prefer a different approach. I can see how a warn-and-ignore could be useful in some cases. My last proposal was an attempt at a compromise, showing how adding #:absent-dependencies would not preclude adding support for a warn-and-ignore mode later.

But the impression I'm getting is that you think the #:absent-dependencies approach would be not merely sub-optimal but actively harmful, and, if that is indeed your view, I feel like I'm still failing to understand what the harm is.

If we took your final suggestion above, I think we'd have something like this:

```
#:phases
(modify-phases %standard-phases
  (add-after 'unpack 'delete-dependencies
    (make-delete-dependencies-phase '("node-tap"))))
```

That seems pretty similar to, under the current patch series:

```
#:absent-dependencies '("node-tap")
```

I can see pros and cons to both approaches. I still like `#:absent-dependencies` better, as I find it less verbose, more declarative, ... trade-offs we probably don't need to rehash further. But it sounds like you see a huge, prohibitive downside to `#:absent-dependencies`, and I am just not managing to see what that is.

I don't know what further steps to take to resolve this disagreement or how some decision ultimately gets made.

More broadly, I agree with you that the current `node-build-system` has some ugly code and is missing some useful utility functions. But I don't feel like I can address all of those preexisting issues in the scope of this patch series, which has already become somewhat unwieldy.

Maybe someone else could weigh in on how to proceed?

-Philip





reply via email to

[Prev in Thread] Current Thread [Next in Thread]