Hi,
Am Freitag, dem 17.12.2021 um 21:48 -0500 schrieb Timothy Sample:
Liliana Marie Prikler <liliana.prikler@gmail.com> writes:
For the GNU build system (and likewise meson-build-system), the
default behaviour if you haven't specified anything as per upstream
conventions is typically to error if the package is required and
omit it if it's not. The default behaviour of node-build-system
(and likewise cargo and most other build systems that come with the
advertisement of "we know package managers better than people who
actually produce usefulpackage managers) is "Oh my god, you don't
have an exact copy of the machine that built this stuff locally, I
am going to barf huge walls of noise at you". Therefore, we can't
meaningfully compare those build systems in terms of strategies.
NPM packages tend to wildly over-specify their dependencies. We
already remove dependency version checking, and before this change,
many of our packages skipped any kind of dependency checking by
skipping the configure phase altogether. To me, the ‘#:absent-
dependencies’ approach tries to work around the dependency over-
specification by listing exactly those things that are only there to
elicit a useless “Oh my god [...], I’m going to barf huge walls of
noise” message. The rest of the dependencies are those that the Guix
package author deemed required (or at least important). Basically,
‘#:absent-dependencies’ helps us translate between the NPM culture of
over-specification (which is really a culture of prioritizing package
author over package user) and the GNU culture of “DWIM” dependencies.
Except that it's not. The current workaround is "I know this thing's
going to barf at me, so I prepare an umbrella and hope it has no
holes".
If we really want some static verification for node-build-system, I
think we should take that as an approach rather than hard-coding
(absent) dependencies literally everywhere.
We need some way to know what to statically verify. We can’t
magically know what’s important and what isn’t. The two options in
this thread are ‘#:absent-dependencies’, and only checking what’s
already in the package’s inputs. What worries me about the second
approach is that it offers no help when updating a package. With
‘#:absent-dependencies’, if the developer adds a new dependency that
really is required, we will get a build-time failure letting us
know. Whoever is updating the package can fix it before even
committing anything. If we just check the inputs, that’s not the
case, and we might end up with Philip’s “mysterious runtime error,
potentially many steps down a dependency chain.” Hopefully tests
would catch it, but I like the extra assurance.
That's why I didn't want to default to "do nothing", but to *warn*
about missing dependencies in configure. Then whoever bumps the
package will at least know which warnings are produced if they do so
and they can cross-check by manually building past versions. Including
#:absent-dependencies is no safe-guard against a failure here anyway.
A dependency that was optional in V1 might become required in V2.
I guess I’m starting to sound like a broken record now – this is
basically what we covered before! :) Maybe we’re in need of a fresh
perspective. (If anyone is reading along and has thoughts, feel free
to chime in!)
I think the NPM convention is to put everything you need "at build
time, but not at runtime" into dev-dependencies, no? In any case, one
approach I could offer is to sanity-check by searching for require()
statements and trying them in a controlled node environment. This
could look something like
eval("try { var dep = require('" + dependency + "'); true }
catch (e) { false; }")
Once we know where require statements are made and whether they
succeed, we can start estimating the impact of a missing dependency.
For this, it'd be nice to have a full function call graph, in which a
node is coloured dirty if it has a failing require statement, lies
within a module that has one or calls into a dirty node. However, as a
primitive approximation we can also count the node modules with failing
requires against those that don't. We set an arbitrary threshold of
allowed failures, e.g. 0.42, and then check that whatever value we
obtain from the above is lower than that.
While that would be nice and all, I think the overall issue with the
current node implementation in Guix is that 'configure' and 'sanity-
check' are the same phase, so you have to disable both or none. I
think we could easily do with a configure phase that does nothing, not
even warn about a missing dependency, and a sanity-check phase that
requires every dependency mentioned in package.json to be met.
Packagers would then outright delete sanity-check as they do for python
and as they did for configure (but not have configure fail due to it!)
or deliberately rewrite the package.json for the sanity check and
dropping absent dependencies, i.e. what you do minus the keyword. If
later needed for the purposes of an importer, we would then still have
that database and could at some point introduce the key #:insane-
requirements. WDYT?