From df7d787ba45c4d7b5a4a49c7fdb163069ff2e47b Mon Sep 17 00:00:00 2001 From: Philip McGrath Date: Sat, 8 Jan 2022 03:41:52 -0500 Subject: guix: node-build-system: Add avoid-node-gyp-rebuild phase. Packages with native addons currently try to write to store paths when used as dependecies. This patch adds a phase to replace that behaviour with a no-op. * guix/build/node-build-system.scm (avoid-node-gyp-rebuild): New variable. (%standard-phases): Add 'avoid-node-gyp-rebuild' after 'install'. Signed-off-by: Liliana Marie Prikler --- guix/build/node-build-system.scm | 54 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) (limited to 'guix') diff --git a/guix/build/node-build-system.scm b/guix/build/node-build-system.scm index 5286a902c7..a1556ce4b8 100644 --- a/guix/build/node-build-system.scm +++ b/guix/build/node-build-system.scm @@ -242,6 +242,57 @@ (define* (install #:key outputs inputs #:allow-other-keys) "install" "../package.tgz") #t)) +(define* (avoid-node-gyp-rebuild #:key outputs #:allow-other-keys) + "Adjust the installed 'package.json' to remove an 'install' script that +would try to run 'node-gyp rebuild'." + ;; We want to take advantage of `npm install`'s automatic support for + ;; building native addons with node-gyp: in particular, it helps us avoid + ;; hard-coding the specifics of how npm's internal copy of node-gyp is + ;; currently packaged. However, the mechanism by which the automatic support + ;; is implemented causes problems for us. + ;; + ;; If a package contains a 'binding.gyp' file and does not define an + ;; 'install' or 'preinstall' script, 'npm install' runs a default install + ;; script consisting of 'node-gyp rebuild'. In our 'install' phase, this + ;; implicit 'install' script, if it is applicable, is explicitly added to + ;; the "package.json" file. However, if another Guix package were to use a + ;; Node.js package with such an 'install' script, the dependent package's + ;; build process would fail, because 'node-gyp rebuild' would try to write + ;; to the store. + ;; + ;; Here, if the installed "package.json" defines scripts.install as + ;; "node-gyp rebuild", we replace it with a no-op. Importantly, deleting the + ;; install script definition would not be enough, because the default + ;; install script would cause the same problem. + ;; + ;; For further details, see: + ;; - https://docs.npmjs.com/cli/v8/configuring-npm/package-json#default-values + ;; - https://docs.npmjs.com/cli/v8/using-npm/scripts#best-practices + (define installed-package.json + (search-input-file outputs (string-append "/lib/node_modules/" + (module-name ".") + "/package.json"))) + ;; We don't want to use an atomic replacement here, because we often don't + ;; even need to overwrite this file. Therefore, let's use some helpers + ;; that we'd otherwise not need. + (define pkg-meta + (call-with-input-file installed-package.json read-json)) + (define scripts + (jsobject-ref pkg-meta "scripts" '(@))) + (define (jsobject-set js key val) + (jsobject-update* js (list key (const val)))) + + (when (equal? "node-gyp rebuild" (jsobject-ref scripts "install" #f)) + (call-with-output-file installed-package.json + (lambda (out) + (write-json + (jsobject-set pkg-meta + "scripts" + (jsobject-set scripts + "install" + "echo Guix: avoiding node-gyp rebuild")) + out))))) + (define %standard-phases (modify-phases gnu:%standard-phases (add-after 'unpack 'set-home set-home) @@ -251,7 +302,8 @@ (define %standard-phases (replace 'build build) (replace 'check check) (add-before 'install 'repack repack) - (replace 'install install))) + (replace 'install install) + (add-after 'install 'avoid-node-gyp-rebuild avoid-node-gyp-rebuild))) (define* (node-build #:key inputs (phases %standard-phases) #:allow-other-keys #:rest args) -- cgit v1.2.3