From 8c3488259ea9e8d18a2c5b947cf9a137a12546a6 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Mon, 16 Oct 2017 23:16:39 +0200 Subject: Add (guix progress). Among other things, this removes (guix utils), (guix ui), (guix config), etc. from the closure of (guix build download), as was the case since 798648515b77507c242752457b4dc17c155bad6e. * guix/utils.scm (, call-with-progress-reporter): Move to... * guix/progress.scm: ... here. New file. * Makefile.am (MODULES): Add it. * guix/build/download.scm (current-terminal-columns) (nearest-exact-integer, duration->seconds, seconds->string) (byte-count->string, progress-bar, string-pad-middle) (rate-limited, progress-reporter/file, dump-port*) (time-monotonic): Move to progress.scm. * guix/scripts/download.scm: Adjust accordingly. * guix/scripts/substitute.scm: Likewise. --- guix/build/download.scm | 167 +----------------------------------------------- 1 file changed, 1 insertion(+), 166 deletions(-) (limited to 'guix/build') diff --git a/guix/build/download.scm b/guix/build/download.scm index 3b89f9412f..61c9c6d3f1 100644 --- a/guix/build/download.scm +++ b/guix/build/download.scm @@ -1,7 +1,6 @@ ;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017 Ludovic Courtès ;;; Copyright © 2015 Mark H Weaver -;;; Copyright © 2015 Steve Sprang ;;; Copyright © 2017 Tobias Geerinckx-Rice ;;; ;;; This file is part of GNU Guix. @@ -27,7 +26,7 @@ (define-module (guix build download) #:use-module (guix base64) #:use-module (guix ftp-client) #:use-module (guix build utils) - #:use-module (guix utils) + #:use-module (guix progress) #:use-module (rnrs io ports) #:use-module (rnrs bytevectors) #:use-module (srfi srfi-1) @@ -46,8 +45,6 @@ (define-module (guix build download) maybe-expand-mirrors url-fetch byte-count->string - current-terminal-columns - progress-reporter/file uri-abbreviation nar-uri-abbreviation store-path-abbreviation)) @@ -62,69 +59,6 @@ (define %http-receive-buffer-size ;; Size of the HTTP receive buffer. 65536) -(define current-terminal-columns - ;; Number of columns of the terminal. - (make-parameter 80)) - -(define (nearest-exact-integer x) - "Given a real number X, return the nearest exact integer, with ties going to -the nearest exact even integer." - (inexact->exact (round x))) - -(define (duration->seconds duration) - "Return the number of seconds represented by DURATION, a 'time-duration' -object, as an inexact number." - (+ (time-second duration) - (/ (time-nanosecond duration) 1e9))) - -(define (seconds->string duration) - "Given DURATION in seconds, return a string representing it in 'mm:ss' or -'hh:mm:ss' format, as needed." - (if (not (number? duration)) - "00:00" - (let* ((total-seconds (nearest-exact-integer duration)) - (extra-seconds (modulo total-seconds 3600)) - (num-hours (quotient total-seconds 3600)) - (hours (and (positive? num-hours) num-hours)) - (mins (quotient extra-seconds 60)) - (secs (modulo extra-seconds 60))) - (format #f "~@[~2,'0d:~]~2,'0d:~2,'0d" hours mins secs)))) - -(define (byte-count->string size) - "Given SIZE in bytes, return a string representing it in a human-readable -way." - (let ((KiB 1024.) - (MiB (expt 1024. 2)) - (GiB (expt 1024. 3)) - (TiB (expt 1024. 4))) - (cond - ((< size KiB) (format #f "~dB" (nearest-exact-integer size))) - ((< size MiB) (format #f "~dKiB" (nearest-exact-integer (/ size KiB)))) - ((< size GiB) (format #f "~,1fMiB" (/ size MiB))) - ((< size TiB) (format #f "~,2fGiB" (/ size GiB))) - (else (format #f "~,3fTiB" (/ size TiB)))))) - -(define* (progress-bar % #:optional (bar-width 20)) - "Return % as a string representing an ASCII-art progress bar. The total -width of the bar is BAR-WIDTH." - (let* ((fraction (/ % 100)) - (filled (inexact->exact (floor (* fraction bar-width)))) - (empty (- bar-width filled))) - (format #f "[~a~a]" - (make-string filled #\#) - (make-string empty #\space)))) - -(define (string-pad-middle left right len) - "Combine LEFT and RIGHT with enough padding in the middle so that the -resulting string has length at least LEN (it may overflow). If the string -does not overflow, the last char in RIGHT will be flush with the LEN -column." - (let* ((total-used (+ (string-length left) - (string-length right))) - (num-spaces (max 1 (- len total-used))) - (padding (make-string num-spaces #\space))) - (string-append left padding right))) - (define* (ellipsis #:optional (port (current-output-port))) "Make a rough guess at whether Unicode's HORIZONTAL ELLIPSIS can be written in PORT's encoding, and return either that or ASCII dots." @@ -143,105 +77,6 @@ (define* (store-path-abbreviation store-path #:optional (prefix-length 6)) (string-drop base 32))) store-path)) -(cond-expand - (guile-2.2 - ;; Guile 2.2.2 has a bug whereby 'time-monotonic' objects have seconds and - ;; nanoseconds swapped (fixed in Guile commit 886ac3e). Work around it. - (define time-monotonic time-tai)) - (else #t)) - - -;; TODO: replace '(@ (guix build utils) dump-port))'. -(define* (dump-port* in out - #:key (buffer-size 16384) - (reporter (make-progress-reporter noop noop noop))) - "Read as much data as possible from IN and write it to OUT, using chunks of -BUFFER-SIZE bytes. After each successful transfer of BUFFER-SIZE bytes or -less, report the total number of bytes transferred to the REPORTER, which -should be a object." - (define buffer - (make-bytevector buffer-size)) - - (call-with-progress-reporter reporter - (lambda (report) - (let loop ((total 0) - (bytes (get-bytevector-n! in buffer 0 buffer-size))) - (or (eof-object? bytes) - (let ((total (+ total bytes))) - (put-bytevector out buffer 0 bytes) - (report total) - (loop total (get-bytevector-n! in buffer 0 buffer-size)))))))) - -(define (rate-limited proc interval) - "Return a procedure that will forward the invocation to PROC when the time -elapsed since the previous forwarded invocation is greater or equal to -INTERVAL (a time-duration object), otherwise does nothing and returns #f." - (let ((previous-at #f)) - (lambda args - (let* ((now (current-time time-monotonic)) - (forward-invocation (lambda () - (set! previous-at now) - (apply proc args)))) - (if previous-at - (let ((elapsed (time-difference now previous-at))) - (if (time>=? elapsed interval) - (forward-invocation) - #f)) - (forward-invocation)))))) - -(define* (progress-reporter/file file size - #:optional (log-port (current-output-port)) - #:key (abbreviation basename)) - "Return a object to show the progress of FILE's download, -which is SIZE bytes long. The progress report is written to LOG-PORT, with -ABBREVIATION used to shorten FILE for display." - (let ((start-time (current-time time-monotonic)) - (transferred 0)) - (define (render) - "Write the progress report to LOG-PORT." - (define elapsed - (duration->seconds - (time-difference (current-time time-monotonic) start-time))) - (if (number? size) - (let* ((% (* 100.0 (/ transferred size))) - (throughput (/ transferred elapsed)) - (left (format #f " ~a ~a" - (abbreviation file) - (byte-count->string size))) - (right (format #f "~a/s ~a ~a~6,1f%" - (byte-count->string throughput) - (seconds->string elapsed) - (progress-bar %) %))) - (display "\r\x1b[K" log-port) - (display (string-pad-middle left right - (current-terminal-columns)) - log-port) - (flush-output-port log-port)) - (let* ((throughput (/ transferred elapsed)) - (left (format #f " ~a" - (abbreviation file))) - (right (format #f "~a/s ~a | ~a transferred" - (byte-count->string throughput) - (seconds->string elapsed) - (byte-count->string transferred)))) - (display "\r\x1b[K" log-port) - (display (string-pad-middle left right - (current-terminal-columns)) - log-port) - (flush-output-port log-port)))) - - (progress-reporter - (start render) - ;; Report the progress every 300ms or longer. - (report - (let ((rate-limited-render - (rate-limited render (make-time time-monotonic 300000000 0)))) - (lambda (value) - (set! transferred value) - (rate-limited-render)))) - ;; Don't miss the last report. - (stop render)))) - (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." -- cgit v1.2.3