Subject: WIP solution

From: [email-masked]
Date: Wed, 29 Apr 2020 14:55:13 +0000

Hello everybody,
I'm attempting to implement the discussed changes. I think these patches 
come pretty close but being my first contribution to Guix's core I would 
like to ask some feedback before submitting these patches with some 
trivial mistake. I tried to base my implementation on [0].

The first patch adds "globstar" support to (guix glob), namely the 
ability of recursively matching subdirectories in a glob pattern (i.e. 
"foo/**/bar.scm" matches both "foo/bar.scm" and "foo/baz/bar.scm").

The second patch adds (guix glob) to the imported modules of 
node-build-system and uses that to parse glob patterns in the "files" 
array of a package.json and then install all the matching files.

I tested the patches by verifying that

./pre-inst-env guix build -K node-semver node-util-deprecate 
node-statsd-parser node-stack-trace node-oop node-mersenne 
node-long-stack-traces node-far node-env-variable node-color-name

runs without error and by running make check TESTS="tests/glob.scm" .

Do you have any feedback/advice?

Thanks,

Giacomo

[0]: https://docs.npmjs.com/files/package.json#files

From 2aaed4af3f171fa0a5d1817d9e0902cf1088b1a7 Mon Sep 17 00:00:00 2001
From: Giacomo Leidi <[email-masked]>
Date: Wed, 29 Apr 2020 15:59:48 +0200
Subject: [PATCH 1/2] guix: Add globstar support.

* guix/glob.scm (string->sglob)
(glob-match?): Add globstar support.
* tests/glob.scm: Update accordingly.
---
 guix/glob.scm  | 13 +++++++++++++
 tests/glob.scm |  8 ++++++--
 2 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/guix/glob.scm b/guix/glob.scm
index a9fc744802..9b796ffd8f 100644
--- a/guix/glob.scm
+++ b/guix/glob.scm
@@ -1,5 +1,6 @@
 ;;; GNU Guix --- Functional package management for GNU
 ;;; Copyright © 2018 Ludovic Courtès <[email-masked]>
+;;; Copyright © 2020 Giacomo Leidi <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -61,6 +62,11 @@ STR, a glob pattern such as \"foo*\" or \"foo??bar\"."
        (flatten (reverse (if (null? pending)
                              result
                              (cons-string pending result)))))
+      ((#\* #\* #\/ . rest)
+       (if (zero? brackets)
+           (loop rest '() 0
+                 (cons* '**/ (cons-string pending result)))
+           (loop rest (cons '**/ pending) brackets result)))
       (((and chr (or #\? #\*)) . rest)
        (let ((wildcard (match chr
                          (#\? '?)
@@ -121,6 +127,13 @@ STR, a glob pattern such as \"foo*\" or \"foo??bar\"."
       (string-null? str))
      (('*)
       #t)
+     (('**/ suffix . rest)
+      (let ((rest (if (eq? '* suffix) (cdr rest) rest))
+              (suffix (if (eq? '* suffix) (car rest) suffix)))
+          (match (string-contains str suffix)
+            (#f    #f)
+            (index (loop rest (string-drop str
+                                           (+ index (string-length suffix))))))))
      (('* suffix . rest)
       (match (string-contains str suffix)
         (#f    #f)
diff --git a/tests/glob.scm b/tests/glob.scm
index 3134069789..2a5a40c3c6 100644
--- a/tests/glob.scm
+++ b/tests/glob.scm
@@ -1,5 +1,6 @@
 ;;; GNU Guix --- Functional package management for GNU
 ;;; Copyright © 2018 Ludovic Courtès <[email-masked]>
+;;; Copyright © 2020 Giacomo Leidi <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -53,7 +54,8 @@
  "foo[abc]bar" => '("foo" (set #\a #\b #\c) "bar")
  "foo[a[b]c]bar" => '("foo" (set #\a #\[ #\b #\] #\c) "bar")
  "[123]x" => '((set #\1 #\2 #\3) "x")
- "[a-z]" => '((range #\a #\z)))
+ "[a-z]" => '((range #\a #\z))
+ "**/*.scm" => '(**/ * ".scm"))
 
 (test-glob-match
  ("foo" matches "foo" (and not "foobar" "barfoo"))
@@ -64,6 +66,8 @@
  ("ab[0-9]c" matches "ab0c" "ab7c" "ab9c"
   (and not "ab-c" "ab00c" "ab3"))
  ("ab[cdefg]" matches "abc" "abd" "abg"
-  (and not "abh" "abcd" "ab[")))
+  (and not "abh" "abcd" "ab["))
+ ("foo/**/*.scm" matches "foo/bar/baz.scm" "foo/bar.scm" "foo/bar/baz/zab.scm"
+  (and not "foo/bar/baz.java" "foo/bar.smc")))
 
 (test-end "glob")
-- 
2.26.2

From 0a3f6a52fde940116112e348a86d76a9017de757 Mon Sep 17 00:00:00 2001
From: Giacomo Leidi <[email-masked]>
Date: Wed, 29 Apr 2020 16:07:28 +0200
Subject: [PATCH 2/2] guix: Enforce package.json "files" directive.

* guix/build/node-build-system.scm (install): Enforce package.json "files" directive.
* guix/build-system/node.scm (%node-build-system-modules)
(node-build)[modules]: Add (guix glob).
---
 guix/build-system/node.scm       |  4 ++-
 guix/build/node-build-system.scm | 57 ++++++++++++++++++++++++--------
 2 files changed, 47 insertions(+), 14 deletions(-)

diff --git a/guix/build-system/node.scm b/guix/build-system/node.scm
index 05c24c47d5..05bc9f2087 100644
--- a/guix/build-system/node.scm
+++ b/guix/build-system/node.scm
@@ -42,6 +42,7 @@ registry."
   `((guix build node-build-system)
     (guix build json)
     (guix build union)
+    (guix glob)
     ,@%gnu-build-system-modules)) ;; TODO: Might be not needed
 
 (define (default-node)
@@ -90,7 +91,8 @@ registry."
                      (modules '((guix build node-build-system)
 				(guix build json)
 				(guix build union)
-                                (guix build utils))))
+                                (guix build utils)
+                                (guix glob))))
   "Build SOURCE using NODE and INPUTS."
   (define builder
     `(begin
diff --git a/guix/build/node-build-system.scm b/guix/build/node-build-system.scm
index 7799f03595..8599c16be5 100644
--- a/guix/build/node-build-system.scm
+++ b/guix/build/node-build-system.scm
@@ -1,6 +1,7 @@
 ;;; GNU Guix --- Functional package management for GNU
 ;;; Copyright © 2015 David Thompson <[email-masked]>
 ;;; Copyright © 2016 Jelle Licht <[email-masked]>
+;;; Copyright © 2020 Giacomo Leidi <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -22,6 +23,7 @@
   #:use-module (guix build json)
   #:use-module (guix build union)
   #:use-module (guix build utils)
+  #:use-module (guix glob)
   #:use-module (ice-9 match)
   #:use-module (ice-9 popen)
   #:use-module (ice-9 regex)
@@ -110,18 +112,49 @@ the @file{bin} directory."
 				 (#f #f)))
          (dependencies (match (assoc-ref data "dependencies")
                          (('@ deps ...) deps)
-                         (#f #f))))
+                         (#f #f)))
+         (patterns (match (assoc-ref data "files")
+                     (() #f)
+                     ((? list? patrn) patrn)
+                     (#f #f)))
+         (install-dir (string-append target "/node_modules/" modulename))
+         (install-files (lambda (files directory)
+                          (for-each (lambda (file)
+                                      (install-file
+                                       file
+                                       (string-append directory "/"
+                                                      (dirname file))))
+                                    files))))
     (mkdir-p target)
-    (copy-recursively "." (string-append target "/node_modules/" modulename))
-    ;; Remove references to dependencies
-    (delete-file-recursively
-      (string-append target "/node_modules/" modulename "/node_modules"))
+    (if patterns
+        (install-files
+         (filter
+          (lambda (file)
+            (any
+             (lambda (pattern)
+               (glob-match?
+                (string->compiled-sglob pattern)
+                file))
+             (append
+              patterns
+              '("package.json" "README*"
+                "CHANGES*" "CHANGELOG*"
+                "HISTORY*" "NOTICE*"))))
+          (map (lambda (path)
+                 (string-drop path 2))
+               (find-files ".")))
+         install-dir)
+        (begin
+          (copy-recursively "." install-dir)
+          ;; Remove references to dependencies
+          (delete-file-recursively
+           (string-append install-dir "/node_modules"))))
+
     (cond
       ((string? binary-configuration)
        (begin
          (mkdir-p binaries)
-         (symlink (string-append target "/node_modules/" modulename "/"
-				 binary-configuration)
+         (symlink (string-append install-dir "/" binary-configuration)
                   (string-append binaries "/" modulename))))
       ((list? binary-configuration)
        (for-each
@@ -130,21 +163,19 @@ the @file{bin} directory."
              ((key . value)
               (begin
                 (mkdir-p (dirname (string-append binaries "/" key)))
-                (symlink (string-append target "/node_modules/" modulename "/"
-					value)
+                (symlink (string-append install-dir "/" value)
                          (string-append binaries "/" key))))))
-         binary-configuration)))
+        binary-configuration)))
     (when dependencies
       (mkdir-p
-        (string-append target "/node_modules/" modulename "/node_modules"))
+        (string-append install-dir "/node_modules"))
       (for-each
         (lambda (dependency)
           (let ((dependency (car dependency)))
             (symlink
               (string-append (assoc-ref inputs (string-append "node-" dependency))
                              "/lib/node_modules/" dependency)
-              (string-append target "/node_modules/" modulename
-                             "/node_modules/" dependency))))
+              (string-append install-dir "/node_modules/" dependency))))
         dependencies))
     #t))
 
-- 
2.26.2