guix-commits
[Top][All Lists]
Advanced

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

01/01: DRAFT Add (guix status).


From: Ludovic Courtès
Subject: 01/01: DRAFT Add (guix status).
Date: Sun, 28 May 2017 17:03:49 -0400 (EDT)

civodul pushed a commit to branch wip-ui
in repository guix.

commit c367549b4779591844cfbf2975301e4d8ae70b92
Author: Ludovic Courtès <address@hidden>
Date:   Wed Jan 18 23:21:29 2017 +0100

    DRAFT Add (guix status).
---
 guix/build/download.scm           |  11 ++
 guix/scripts/build.scm            |  10 +-
 guix/scripts/perform-download.scm |   1 +
 guix/scripts/substitute.scm       |  53 ++++++----
 guix/status.scm                   | 217 ++++++++++++++++++++++++++++++++++++++
 guix/store.scm                    |   9 +-
 nix/libstore/builtins.cc          |   2 +
 7 files changed, 281 insertions(+), 22 deletions(-)

diff --git a/guix/build/download.scm b/guix/build/download.scm
index ce4708a..3c3d175 100644
--- a/guix/build/download.scm
+++ b/guix/build/download.scm
@@ -45,6 +45,7 @@
             byte-count->string
             current-terminal-columns
             progress-proc
+            trace-progress-proc
             uri-abbreviation
             nar-uri-abbreviation
             store-path-abbreviation))
@@ -207,6 +208,16 @@ used to shorten FILE for display."
                 (flush-output-port log-port)
                 (cont))))))))
 
+(define* (trace-progress-proc file url size
+                              #:optional (log-port (current-output-port)))
+  "Like 'progress-proc', but instead of returning human-readable progress
+reports, write \"build trace\" lines to be processed elsewhere."
+  (lambda (transferred cont)
+    (format log-port "@ download-progress ~a ~a ~a ~a~%"
+            file (or url "-") (or size "-") transferred)
+    (flush-output-port log-port)
+    (cont)))
+
 (define* (uri-abbreviation uri #:optional (max-length 42))
   "If URI's string representation is larger than MAX-LENGTH, return an
 abbreviation of URI showing the scheme, host, and basename of the file."
diff --git a/guix/scripts/build.scm b/guix/scripts/build.scm
index 558e8e7..0dd78f4 100644
--- a/guix/scripts/build.scm
+++ b/guix/scripts/build.scm
@@ -682,6 +682,7 @@ needed."
 ;;; Entry point.
 ;;;
 
+(use-modules (guix status))
 (define (guix-build . args)
   (define opts
     (parse-command-line args %options
@@ -698,9 +699,12 @@ needed."
         ;; Set the build options before we do anything else.
         (set-build-options-from-command-line store opts)
 
-        (parameterize ((current-build-output-port (if quiet?
-                                                      (%make-void-port "w")
-                                                      (current-error-port))))
+        (parameterize ((current-build-output-port
+                        (if quiet?
+                            (%make-void-port "w")
+                            (build-event-output-port
+                             (build-status-updater print-build-status)
+                             (build-status)))))
           (let* ((mode  (assoc-ref opts 'build-mode))
                  (drv   (options->derivations store opts))
                  (urls  (map (cut string-append <> "/log")
diff --git a/guix/scripts/perform-download.scm 
b/guix/scripts/perform-download.scm
index aee506a..d65b123 100644
--- a/guix/scripts/perform-download.scm
+++ b/guix/scripts/perform-download.scm
@@ -48,6 +48,7 @@ OUTPUT.
 Note: Unless OUTPUT is #f, we don't read the value of 'out' in DRV since the
 actual output is different from that when we're doing a 'bmCheck' or
 'bmRepair' build."
+  ;; TODO: Use 'trace-progress-proc' when possible.
   (derivation-let drv ((url "url")
                        (output* "out")
                        (executable "executable")
diff --git a/guix/scripts/substitute.scm b/guix/scripts/substitute.scm
index 73d4f6e..6b1d184 100755
--- a/guix/scripts/substitute.scm
+++ b/guix/scripts/substitute.scm
@@ -34,6 +34,7 @@
   #:use-module ((guix build utils) #:select (mkdir-p dump-port))
   #:use-module ((guix build download)
                 #:select (current-terminal-columns
+                          trace-progress-proc
                           progress-proc uri-abbreviation nar-uri-abbreviation
                           (open-connection-for-uri
                            . guix:open-connection-for-uri)
@@ -836,7 +837,7 @@ expected by the daemon."
           (or (narinfo-size narinfo) 0)))
 
 (define* (process-query command
-                        #:key cache-urls acl)
+                        #:key cache-urls acl print-build-trace?)
   "Reply to COMMAND, a query as written by the daemon to this process's
 standard input.  Use ACL as the access-control list against which to check
 authorized substitutes."
@@ -860,7 +861,7 @@ authorized substitutes."
      (error "unknown `--query' command" wtf))))
 
 (define* (process-substitution store-item destination
-                               #:key cache-urls acl)
+                               #:key cache-urls acl print-build-trace?)
   "Substitute STORE-ITEM (a store file name) from CACHE-URLS, and write it to
 DESTINATION as a nar file.  Verify the substitute against ACL."
   (let* ((narinfo (lookup-narinfo cache-urls store-item))
@@ -871,31 +872,38 @@ DESTINATION as a nar file.  Verify the substitute against 
ACL."
     ;; Tell the daemon what the expected hash of the Nar itself is.
     (format #t "~a~%" (narinfo-hash narinfo))
 
-    (format (current-error-port)
-            ;; TRANSLATORS: The second part of this message looks like
-            ;; "(4.1MiB installed)"; it shows the size of the package once
-            ;; installed.
-            (G_ "Downloading ~a~:[~*~; (~a installed)~]...~%")
-            (uri->string uri)
-            ;; Use the Nar size as an estimate of the installed size.
-            (narinfo-size narinfo)
-            (and=> (narinfo-size narinfo)
-                   (cute byte-count->string <>)))
+    (unless print-build-trace?
+      (format (current-error-port)
+              ;; TRANSLATORS: The second part of this message looks like
+              ;; "(4.1MiB installed)"; it shows the size of the package once
+              ;; installed.
+              (G_ "Downloading ~a~:[~*~; (~a installed)~]...~%")
+              (uri->string uri)
+              ;; Use the Nar size as an estimate of the installed size.
+              (narinfo-size narinfo)
+              (and=> (narinfo-size narinfo)
+                     (cute byte-count->string <>))))
     (let*-values (((raw download-size)
                    ;; Note that Hydra currently generates Nars on the fly
                    ;; and doesn't specify a Content-Length, so
                    ;; DOWNLOAD-SIZE is #f in practice.
                    (fetch uri #:buffered? #f #:timeout? #f))
+                  ((progress-proc)
+                   (if print-build-trace?
+                       trace-progress-proc
+                       (lambda (destination uri size port)
+                         (progress-proc uri size port
+                                        #:abbreviation
+                                        nar-uri-abbreviation))))
                   ((progress)
                    (let* ((comp     (narinfo-compression narinfo))
                           (dl-size  (or download-size
                                         (and (equal? comp "none")
                                              (narinfo-size narinfo))))
-                          (progress (progress-proc (uri->string uri)
+                          (progress (progress-proc destination
+                                                   (uri->string uri)
                                                    dl-size
-                                                   (current-error-port)
-                                                   #:abbreviation
-                                                   nar-uri-abbreviation)))
+                                                   (current-error-port))))
                      (progress-report-port progress raw)))
                   ((input pids)
                    (decompressed-port (and=> (narinfo-compression narinfo)
@@ -986,6 +994,13 @@ default value."
 
 (define (guix-substitute . args)
   "Implement the build daemon's substituter protocol."
+  (define print-build-trace?
+    (match (or (find-daemon-option "untrusted-print-build-trace")
+               (find-daemon-option "print-build-trace"))
+      (#f #f)
+      ((= string->number number) (> number 0))
+      (_ #f)))
+
   (mkdir-p %narinfo-cache-directory)
   (maybe-remove-expired-cache-entries %narinfo-cache-directory
                                       cached-narinfo-files
@@ -1025,7 +1040,8 @@ default value."
                 (begin
                   (process-query command
                                  #:cache-urls %cache-urls
-                                 #:acl acl)
+                                 #:acl acl
+                                 #:print-build-trace? print-build-trace?)
                   (loop (read-line)))))))
        (("--substitute" store-path destination)
         ;; Download STORE-PATH and add store it as a Nar in file DESTINATION.
@@ -1034,7 +1050,8 @@ default value."
         (parameterize ((current-terminal-columns (client-terminal-columns)))
           (process-substitution store-path destination
                                 #:cache-urls %cache-urls
-                                #:acl (current-acl))))
+                                #:acl (current-acl)
+                                #:print-build-trace? print-build-trace?)))
        (("--version")
         (show-version-and-exit "guix substitute"))
        (("--help")
diff --git a/guix/status.scm b/guix/status.scm
new file mode 100644
index 0000000..cd6f09d
--- /dev/null
+++ b/guix/status.scm
@@ -0,0 +1,217 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2017 Ludovic Courtès <address@hidden>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix status)
+  #:use-module (guix records)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-9)
+  #:use-module (srfi srfi-19)
+  #:use-module (ice-9 match)
+  #:use-module (ice-9 format)
+  #:use-module (ice-9 binary-ports)
+  #:use-module (rnrs bytevectors)
+  #:export (build-event-output-port
+
+            build-status
+            build-status?
+
+            build-status-updater
+            print-build-status))
+
+(define %newline
+  (char-set #\return #\newline))
+
+(define (build-event-output-port proc seed)
+  "Return an output port for use as 'current-build-output-port' that calls
+PROC with its current state value, initialized with SEED, on every build
+event.  Build events passed to PROC are tuples corresponding to the \"build
+traces\" produced by the daemon:
+
+  (build-started \"/gnu/store/...-foo.drv\" ...)
+  (substituter-started \"/gnu/store/...-foo\" ...)
+
+and so on. "
+  (define %fragments
+    ;; Line fragments received so far.
+    '())
+
+  (define %state
+    ;; Current state for PROC.
+    seed)
+
+  (define (process-line line)
+    (when (string-prefix? "@ " line)
+      (match (string-tokenize (string-drop line 2))
+        (((= string->symbol event-name) args ...)
+         (set! %state
+           (proc (cons event-name args)
+                 %state))))))
+
+  (define (write! bv offset count)
+    (let loop ((str (utf8->string bv)))
+      (match (string-index str %newline)
+        ((? integer? cr)
+         (let ((tail (string-take str cr)))
+           (process-line (string-concatenate-reverse
+                          (cons tail %fragments)))
+           (set! %fragments '())
+           (loop (string-drop str (+ 1 cr)))))
+        (#f
+         (set! %fragments (cons str %fragments))
+         count))))
+
+  (make-custom-binary-output-port "filtering-input-port"
+                                  write!
+                                  #f #f
+                                  #f))
+
+
+;;;
+;;; Build status tracking.
+;;;
+
+;; Builds and substitutions performed by the daemon.
+(define-record-type* <build-status> build-status make-build-status
+  build-status?
+  (building     build-status-building             ;list of drv
+                (default '()))
+  (substituting build-status-substituting         ;list of <ongoing-download>
+                (default '()))
+  (builds-completed build-status-builds-completed ;list of drv
+                    (default '()))
+  (substitutes-completed build-status-substitutes-completed ;list of store 
items
+                         (default '())))
+
+(define-record-type <ongoing-download>
+  (ongoing-download item uri size start transferred)
+  ongoing-download?
+  (item         ongoing-download-item)            ;store item
+  (uri          ongoing-download-uri)             ;string | #f
+  (size         ongoing-download-size)            ;integer | #f
+  (start        ongoing-download-start)           ;<time>
+  (transferred  ongoing-download-transferred))    ;integer
+
+(define (matching-download item)
+  (lambda (ongoing)
+    (string=? item (ongoing-download-item ongoing))))
+
+(define (compute-status event status)
+  "Given EVENT, a tuple like (build-started \"/gnu/store/...-foo.drv\" ...),
+compute a new status based on STATUS."
+  (match event
+    (('build-started drv _ ...)
+     (build-status
+      (inherit status)
+      (building (cons drv (build-status-building status)))))
+    (('build-succeeded drv _ ...)
+     (build-status
+      (inherit status)
+      (building (delete drv (build-status-building status)))
+      (builds-completed (cons drv (build-status-builds-completed status)))))
+    (('substituter-started item _ ...)
+     (build-status
+      (inherit status)
+      (substituting (cons (ongoing-download item #f #f (current-time) 0)
+                          (build-status-substituting status)))))
+    (('substituter-succeeded item _ ...)
+     (build-status
+      (inherit status)
+      (substituting (remove (matching-download item)
+                            (build-status-substituting status)))
+      (substitutes-completed
+       (cons item (build-status-substitutes-completed status)))))
+    (('download-progress item uri
+                         (= string->number size)
+                         (= string->number transferred))
+     (let ((downloads (remove (matching-download item)
+                              (build-status-substituting status)))
+           (current   (find (matching-download item)
+                            (build-status-substituting status))))
+       (build-status
+        (inherit status)
+        (substituting (cons (ongoing-download item uri size
+                                              (ongoing-download-start current)
+                                              transferred)
+                            downloads)))))
+    (_
+     (pk 'unhandled event)
+     status)))
+
+(define (simultaneous-jobs status)
+  (+ (length (build-status-building status))
+     (length (build-status-substituting status))))
+
+(define (cursor-up n port)
+  (format port "\r\x1b[K\x1b~a[A" n)              ;erase in line + up
+  (force-output port))
+
+(define* (print-build-status event old-status status #:optional
+                             (port (current-error-port)))
+  (define (new-download? download)
+    (zero? (ongoing-download-transferred download)))
+
+  ;; (let loop ((n (lines-printed old-status)))
+  ;;   (when (> n 1)
+  ;;     (cursor-up port)))
+
+  (match event
+    (('substituter-succeeded _ ...)
+     (newline port))
+    (_ #t))
+
+  (match (build-status-building status)
+    (() #t)
+    ((building ...)
+     (format port "building~{ ~a~}~%" building)))
+
+  (match (build-status-substituting status)
+    (() #t)
+    ((download rest ...)
+     (if (and (<= (simultaneous-jobs old-status) 1)
+              (= 1 (simultaneous-jobs status)))
+         (let* ((now      (current-time))
+                (elapsed  (time-difference now
+                                           (ongoing-download-start download)))
+                (throughput (if (zero? (time-second elapsed))
+                                0
+                                (/ (ongoing-download-transferred download)
+                                   1024.
+                                   (time-second elapsed)))))
+           (if (zero? (ongoing-download-transferred download))
+               (format port "downloading ~a..."
+                       (ongoing-download-item download))
+               (format port "\r\x1b[K~a ~a KiB | ~6,1f KiB/s"
+                       (ongoing-download-uri download)
+                       (ongoing-download-transferred download)
+                       throughput))
+           (force-output port))
+         (let ((downloads (filter new-download? (cons download rest))))
+           (unless (null? downloads)
+             (format port "downloading ~{ ~a~}~%"
+                     (map ongoing-download-item downloads))
+             (force-output port)))))))
+
+(define* (build-status-updater #:optional (on-change (const #t)))
+  (lambda (event status)
+    (let ((new (compute-status event status)))
+      (on-change event status new)
+      new)))
+
+;; (define current-build-output-port
+;;   ;; The port where build output is sent.
+;;   (make-parameter (filtering-output-port (current-error-port))))
diff --git a/guix/store.scm b/guix/store.scm
index c94dfea..99151c5 100644
--- a/guix/store.scm
+++ b/guix/store.scm
@@ -685,7 +685,14 @@ encoding conversion errors."
     (when (>= (nix-server-minor-version server) 10)
       (send (boolean use-substitutes?)))
     (when (>= (nix-server-minor-version server) 12)
-      (let ((pairs `(,@(if timeout
+      (let ((pairs `(;; This 'print-build-trace' option is honored by 'guix
+                     ;; substitute' et al.
+                     ,@(if print-build-trace
+                           `(("print-build-trace"
+                              . ,(if print-build-trace "1" "0")))
+                           '())
+
+                     ,@(if timeout
                            `(("build-timeout" . ,(number->string timeout)))
                            '())
                      ,@(if max-silent-time
diff --git a/nix/libstore/builtins.cc b/nix/libstore/builtins.cc
index a5ebb47..31b0943 100644
--- a/nix/libstore/builtins.cc
+++ b/nix/libstore/builtins.cc
@@ -53,6 +53,8 @@ static void builtinDownload(const Derivation &drv,
     const string subdir = getenv("GUIX_UNINSTALLED") != NULL
        ? "" : "/guix";
 
+    setenv("_NIX_OPTIONS", settings.pack().c_str(), 1);
+
     const string program = settings.nixLibexecDir + subdir + "/download";
     execv(program.c_str(), (char *const *) argv);
 



reply via email to

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