summary refs log tree commit diff
path: root/guix/build/download.scm
diff options
context:
space:
mode:
Diffstat (limited to 'guix/build/download.scm')
-rw-r--r--guix/build/download.scm84
1 files changed, 70 insertions, 14 deletions
diff --git a/guix/build/download.scm b/guix/build/download.scm
index 074315cc9f..09c62541de 100644
--- a/guix/build/download.scm
+++ b/guix/build/download.scm
@@ -1,20 +1,20 @@
-;;; Guix --- Nix package management from Guile.         -*- coding: utf-8 -*-
-;;; Copyright (C) 2012 Ludovic Courtès <ludo@gnu.org>
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2012, 2013 Ludovic Courtès <ludo@gnu.org>
 ;;;
-;;; This file is part of Guix.
+;;; This file is part of GNU Guix.
 ;;;
-;;; Guix is free software; you can redistribute it and/or modify it
+;;; 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.
 ;;;
-;;; Guix is distributed in the hope that it will be useful, but
+;;; 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 Guix.  If not, see <http://www.gnu.org/licenses/>.
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
 
 (define-module (guix build download)
   #:use-module (web uri)
@@ -27,6 +27,7 @@
   #:use-module (srfi srfi-11)
   #:use-module (srfi srfi-26)
   #:use-module (ice-9 match)
+  #:use-module (ice-9 format)
   #:export (url-fetch))
 
 ;;; Commentary:
@@ -35,17 +36,58 @@
 ;;;
 ;;; Code:
 
+(define* (progress-proc file size #:optional (log-port (current-output-port)))
+  "Return a procedure to show the progress of FILE's download, which is
+SIZE byte long.  The returned procedure is suitable for use as an
+argument to `dump-port'.  The progress report is written to LOG-PORT."
+  (if (number? size)
+      (lambda (transferred cont)
+        (let ((% (* 100.0 (/ transferred size))))
+          (display #\cr log-port)
+          (format log-port "~a\t~5,1f% of ~,1f KiB"
+                  file % (/ size 1024.0))
+          (flush-output-port log-port)
+          (cont)))
+      (lambda (transferred cont)
+        (display #\cr log-port)
+        (format log-port "~a\t~6,1f KiB transferred"
+                file (/ transferred 1024.0))
+        (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."
+  (define uri-as-string
+    (uri->string uri))
+
+  (define (elide-path)
+    (let ((path (uri-path uri)))
+      (string-append (symbol->string (uri-scheme uri))
+                     "://" (uri-host uri)
+                     (string-append "/.../" (basename path)))))
+
+  (if (> (string-length uri-as-string) max-length)
+      (let ((short (elide-path)))
+        (if (< (string-length short) (string-length uri-as-string))
+            short
+            uri-as-string))
+      uri-as-string))
+
 (define (ftp-fetch uri file)
   "Fetch data from URI and write it to FILE.  Return FILE on success."
   (let* ((conn (ftp-open (uri-host uri)))
+         (size (false-if-exception (ftp-size conn (uri-path uri))))
          (in   (ftp-retr conn (basename (uri-path uri))
                          (dirname (uri-path uri)))))
     (call-with-output-file file
       (lambda (out)
-        ;; TODO: Show a progress bar.
-        (dump-port in out)))
+        (dump-port in out
+                   #:buffer-size 65536            ; don't flood the log
+                   #:progress (progress-proc (uri-abbreviation uri) size))))
 
     (ftp-close conn))
+    (newline)
   file)
 
 (define (open-connection-for-uri uri)
@@ -103,20 +145,34 @@ which is not available during bootstrap."
 (define (http-fetch uri file)
   "Fetch data from URI and write it to FILE.  Return FILE on success."
 
-  ;; FIXME: Use a variant of `http-get' that returns a port instead of
-  ;; loading everything in memory.
   (let*-values (((connection)
                  (open-connection-for-uri uri))
-                ((resp bv)
-                 (http-get uri #:port connection #:decode-body? #f))
+                ((resp bv-or-port)
+                 ;; XXX: `http-get*' was introduced in 2.0.7.  We know
+                 ;; we're using it within the chroot, but
+                 ;; `guix-download' might be using a different version.
+                 ;; So keep this compatibility hack for now.
+                 (if (module-defined? (resolve-interface '(web client))
+                                      'http-get*)
+                     (http-get* uri #:port connection #:decode-body? #f)
+                     (http-get uri #:port connection #:decode-body? #f)))
                 ((code)
-                 (response-code resp)))
+                 (response-code resp))
+                ((size)
+                 (response-content-length resp)))
     (case code
       ((200)                                      ; OK
        (begin
          (call-with-output-file file
            (lambda (p)
-             (put-bytevector p bv)))
+             (if (port? bv-or-port)
+                 (begin
+                   (dump-port bv-or-port p
+                              #:buffer-size 65536  ; don't flood the log
+                              #:progress (progress-proc (uri-abbreviation uri)
+                                                        size))
+                   (newline))
+                 (put-bytevector p bv-or-port))))
          file))
       ((302)                                      ; found (redirection)
        (let ((uri (response-location resp)))