summary refs log tree commit diff
diff options
context:
space:
mode:
authorLudovic Courtès <ludovic.courtes@inria.fr>2017-06-16 12:07:26 +0200
committerLudovic Courtès <ludo@gnu.org>2017-06-16 17:08:22 +0200
commitd27cc3bfaafe6b5b0831e88afb1c46311d382a0b (patch)
tree1cc92c5ece84a792e170913d5e4d73958f74027e
parentfa73c1937364872560c509f02b3d7648a5bed006 (diff)
downloadguix-d27cc3bfaafe6b5b0831e88afb1c46311d382a0b.tar.gz
discovery: Rewrite 'scheme-files' using 'scandir*'.
On a command like:

  guix environment --ad-hoc coreutils -- true

this reduces the number of 'stat' calls from 14.1K to 9.7K on my
setup (previously each getdents(2) call would be followed by one stat(2)
call per entry).

* guix/discovery.scm (scheme-files): Rewrite using 'scandir*'.
-rw-r--r--guix/discovery.scm50
1 files changed, 29 insertions, 21 deletions
diff --git a/guix/discovery.scm b/guix/discovery.scm
index 319ba7c872..6cf8d6d566 100644
--- a/guix/discovery.scm
+++ b/guix/discovery.scm
@@ -19,6 +19,7 @@
 (define-module (guix discovery)
   #:use-module (guix ui)
   #:use-module (guix combinators)
+  #:use-module (guix build syscalls)
   #:use-module (srfi srfi-1)
   #:use-module (ice-9 match)
   #:use-module (ice-9 vlist)
@@ -38,28 +39,35 @@
 (define* (scheme-files directory)
   "Return the list of Scheme files found under DIRECTORY, recursively.  The
 returned list is sorted in alphabetical order."
+  (define (entry-type name properties)
+    (match (assoc-ref properties 'type)
+      ('unknown
+       (stat:type (lstat name)))
+      ((? symbol? type)
+       type)))
 
-  ;; Sort entries so that 'fold-packages' works in a deterministic fashion
-  ;; regardless of details of the underlying file system.
-  (sort (file-system-fold (const #t)                 ;enter?
-                          (lambda (path stat result) ;leaf
-                            (if (string-suffix? ".scm" path)
-                                (cons path result)
-                                result))
-                          (lambda (path stat result) ;down
-                            result)
-                          (lambda (path stat result) ;up
-                            result)
-                          (const #f)                 ;skip
-                          (lambda (path stat errno result)
-                            (unless (= ENOENT errno)
-                              (warning (G_ "cannot access `~a': ~a~%")
-                                       path (strerror errno)))
-                            result)
-                          '()
-                          directory
-                          stat)
-        string<?))
+  ;; Use 'scandir*' so we can avoid an extra 'lstat' for each entry, as
+  ;; opposed to Guile's 'scandir' or 'file-system-fold'.
+  (fold-right (lambda (entry result)
+                (match entry
+                  (("." . _)
+                   result)
+                  ((".." . _)
+                   result)
+                  ((name . properties)
+                   (let ((absolute (string-append directory "/" name)))
+                     (case (entry-type absolute properties)
+                       ((directory)
+                        (append (scheme-files absolute) result))
+                       ((regular symlink)
+                        ;; XXX: We don't recurse if we find a symlink.
+                        (if (string-suffix? ".scm" name)
+                            (cons absolute result)
+                            result))
+                       (else
+                        result))))))
+              '()
+              (scandir* directory)))
 
 (define file-name->module-name
   (let ((not-slash (char-set-complement (char-set #\/))))