summaryrefslogtreecommitdiff
path: root/guix/build
diff options
context:
space:
mode:
Diffstat (limited to 'guix/build')
-rw-r--r--guix/build/download.scm179
-rw-r--r--guix/build/emacs-build-system.scm45
-rw-r--r--guix/build/meson-build-system.scm150
3 files changed, 288 insertions, 86 deletions
diff --git a/guix/build/download.scm b/guix/build/download.scm
index 6ef6233346..9490f48055 100644
--- a/guix/build/download.scm
+++ b/guix/build/download.scm
@@ -2,6 +2,7 @@
;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2015 Mark H Weaver <mhw@netris.org>
;;; Copyright © 2015 Steve Sprang <scs@stevesprang.com>
+;;; Copyright © 2017 Tobias Geerinckx-Rice <me@tobias.gr>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -26,6 +27,7 @@
#:use-module (guix base64)
#:use-module (guix ftp-client)
#:use-module (guix build utils)
+ #:use-module (guix utils)
#:use-module (rnrs io ports)
#:use-module (rnrs bytevectors)
#:use-module (srfi srfi-1)
@@ -44,7 +46,7 @@
url-fetch
byte-count->string
current-terminal-columns
- progress-proc
+ progress-reporter/file
uri-abbreviation
nar-uri-abbreviation
store-path-abbreviation))
@@ -147,65 +149,97 @@ Otherwise return STORE-PATH."
(define time-monotonic time-tai))
(else #t))
-(define* (progress-proc file size
- #:optional (log-port (current-output-port))
- #:key (abbreviation basename))
- "Return a procedure to show the progress of FILE's download, which is SIZE
-bytes long. The returned procedure is suitable for use as an argument to
-`dump-port'. The progress report is written to LOG-PORT, with ABBREVIATION
-used to shorten FILE for display."
- ;; XXX: Because of <http://bugs.gnu.org/19939> this procedure is often not
- ;; called as frequently as we'd like too; this is especially bad with Nginx
- ;; on hydra.gnu.org, which returns whole nars as a single chunk.
- (let ((start-time #f))
- (let-syntax ((with-elapsed-time
- (syntax-rules ()
- ((_ elapsed body ...)
- (let* ((now (current-time time-monotonic))
- (elapsed (and start-time
- (duration->seconds
- (time-difference now
- start-time)))))
- (unless start-time
- (set! start-time now))
- body ...)))))
+
+;; 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 <progress-reporter> 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 <progress-reporter> 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)
- (lambda (transferred cont)
- (with-elapsed-time elapsed
- (let* ((% (* 100.0 (/ transferred size)))
- (throughput (if elapsed
- (/ transferred elapsed)
- 0))
- (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)
- (cont))))
- (lambda (transferred cont)
- (with-elapsed-time elapsed
- (let* ((throughput (if elapsed
- (/ transferred elapsed)
- 0))
- (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)
- (cont))))))))
+ (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
@@ -263,9 +297,10 @@ out if the connection could not be established in less than TIMEOUT seconds."
(dirname (uri-path uri)))))
(call-with-output-file file
(lambda (out)
- (dump-port in out
- #:buffer-size %http-receive-buffer-size
- #:progress (progress-proc (uri-abbreviation uri) size))))
+ (dump-port* in out
+ #:buffer-size %http-receive-buffer-size
+ #:reporter (progress-reporter/file
+ (uri-abbreviation uri) size))))
(ftp-close conn))
(newline)
@@ -754,16 +789,18 @@ certificates; otherwise simply ignore them."
(lambda (p)
(if (port? bv-or-port)
(begin
- (dump-port bv-or-port p
- #:buffer-size %http-receive-buffer-size
- #:progress (progress-proc (uri-abbreviation uri)
- size))
+ (dump-port* bv-or-port p
+ #:buffer-size %http-receive-buffer-size
+ #:reporter (progress-reporter/file
+ (uri-abbreviation uri) size))
(newline))
(put-bytevector p bv-or-port))))
file))
((301 ; moved permanently
302 ; found (redirection)
- 307) ; temporary redirection
+ 303 ; see other
+ 307 ; temporary redirection
+ 308) ; permanent redirection
(let ((uri (resolve-uri-reference (response-location resp) uri)))
(format #t "following redirection to `~a'...~%"
(uri->string uri))
@@ -860,8 +897,8 @@ otherwise simply ignore them."
hashes))
content-addressed-mirrors))
- ;; Make this unbuffered so 'progress-proc' works as expected. _IOLBF means
- ;; '\n', not '\r', so it's not appropriate here.
+ ;; Make this unbuffered so 'progress-report/file' works as expected. _IOLBF
+ ;; means '\n', not '\r', so it's not appropriate here.
(setvbuf (current-output-port) _IONBF)
(setvbuf (current-error-port) _IOLBF)
@@ -876,8 +913,4 @@ otherwise simply ignore them."
file url)
#f))))
-;;; Local Variables:
-;;; eval: (put 'with-elapsed-time 'scheme-indent-function 1)
-;;; End:
-
;;; download.scm ends here
diff --git a/guix/build/emacs-build-system.scm b/guix/build/emacs-build-system.scm
index bda699ddf4..2404dbddb4 100644
--- a/guix/build/emacs-build-system.scm
+++ b/guix/build/emacs-build-system.scm
@@ -110,22 +110,41 @@ store in '.el' files."
(define source (getcwd))
- (define (install-file? file stat)
- (let ((stripped-file (string-trim (string-drop file (string-length source)) #\/)))
- (and (any (cut string-match <> stripped-file) include)
- (not (any (cut string-match <> stripped-file) exclude)))))
+ (define* (install-file? file stat #:key verbose?)
+ (let* ((stripped-file (string-trim
+ (string-drop file (string-length source)) #\/)))
+ (define (match-stripped-file action regex)
+ (let ((result (string-match regex stripped-file)))
+ (when (and result verbose?)
+ (format #t "info: ~A ~A as it matches \"~A\"\n"
+ stripped-file action regex))
+ result))
+
+ (when verbose?
+ (format #t "info: considering installing ~A\n" stripped-file))
+
+ (and (any (cut match-stripped-file "included" <>) include)
+ (not (any (cut match-stripped-file "excluded" <>) exclude)))))
(let* ((out (assoc-ref outputs "out"))
(elpa-name-ver (store-directory->elpa-name-version out))
- (target-directory (string-append out %install-suffix "/" elpa-name-ver)))
- (for-each
- (lambda (file)
- (let* ((stripped-file (string-drop file (string-length source)))
- (target-file (string-append target-directory stripped-file)))
- (format #t "`~a' -> `~a'~%" file target-file)
- (install-file file (dirname target-file))))
- (find-files source install-file?)))
- #t)
+ (target-directory (string-append out %install-suffix "/" elpa-name-ver))
+ (files-to-install (find-files source install-file?)))
+ (cond
+ ((not (null? files-to-install))
+ (for-each
+ (lambda (file)
+ (let* ((stripped-file (string-drop file (string-length source)))
+ (target-file (string-append target-directory stripped-file)))
+ (format #t "`~a' -> `~a'~%" file target-file)
+ (install-file file (dirname target-file))))
+ files-to-install)
+ #t)
+ (else
+ (format #t "error: No files found to install.\n")
+ (find-files source (lambda (file stat)
+ (install-file? file stat #:verbose? #t)))
+ #f))))
(define* (move-doc #:key outputs #:allow-other-keys)
"Move info files from the ELPA package directory to the info directory."
diff --git a/guix/build/meson-build-system.scm b/guix/build/meson-build-system.scm
new file mode 100644
index 0000000000..2b92240c52
--- /dev/null
+++ b/guix/build/meson-build-system.scm
@@ -0,0 +1,150 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2017 Peter Mikkelsen <petermikkelsen10@gmail.com>
+;;;
+;;; 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 build meson-build-system)
+ #:use-module ((guix build gnu-build-system) #:prefix gnu:)
+ #:use-module ((guix build glib-or-gtk-build-system) #:prefix glib-or-gtk:)
+ #:use-module (guix build utils)
+ #:use-module (guix build rpath)
+ #:use-module (guix build gremlin)
+ #:use-module (guix elf)
+ #:use-module (ice-9 match)
+ #:use-module (rnrs io ports)
+ #:use-module (srfi srfi-1)
+ #:export (%standard-phases
+ meson-build))
+
+;; Commentary:
+;;
+;; Builder-side code of the standard meson build procedure.
+;;
+;; Code:
+
+(define* (configure #:key outputs configure-flags build-type
+ #:allow-other-keys)
+ "Configure the given package."
+ (let* ((out (assoc-ref outputs "out"))
+ (source-dir (getcwd))
+ (build-dir "../build")
+ (prefix (assoc-ref outputs "out"))
+ (args `(,(string-append "--prefix=" prefix)
+ ,(string-append "--buildtype=" build-type)
+ ,@configure-flags
+ ,source-dir)))
+ (mkdir build-dir)
+ (chdir build-dir)
+ (zero? (apply system* "meson" args))))
+
+(define* (build #:key parallel-build?
+ #:allow-other-keys)
+ "Build a given meson package."
+ (zero? (apply system* "ninja"
+ (if parallel-build?
+ `("-j" ,(number->string (parallel-job-count)))
+ '("-j" "1")))))
+
+(define* (check #:key test-target parallel-tests? tests?
+ #:allow-other-keys)
+ (setenv "MESON_TESTTHREADS"
+ (if parallel-tests?
+ (number->string (parallel-job-count))
+ "1"))
+ (if tests?
+ (zero? (system* "ninja" test-target))
+ (begin
+ (format #t "test suite not run~%")
+ #t)))
+
+(define* (install #:rest args)
+ (zero? (system* "ninja" "install")))
+
+(define* (fix-runpath #:key (elf-directories '("lib" "lib64" "libexec"
+ "bin" "sbin"))
+ outputs #:allow-other-keys)
+ "Try to make sure all ELF files in ELF-DIRECTORIES are able to find their
+local dependencies in their RUNPATH, by searching for the needed libraries in
+the directories of the package, and adding them to the RUNPATH if needed.
+Also shrink the RUNPATH to what is needed,
+since a lot of directories are left over from the build phase of meson,
+for example libraries only needed for the tests."
+
+ ;; Find the directories (if any) that contains DEP-NAME. The directories
+ ;; searched are the ones that ELF-FILES are in.
+ (define (find-deps dep-name elf-files)
+ (map dirname (filter (lambda (file)
+ (string=? dep-name (basename file)))
+ elf-files)))
+
+ ;; Return a list of libraries that FILE needs.
+ (define (file-needed file)
+ (let* ((elf (call-with-input-file file
+ (compose parse-elf get-bytevector-all)))
+ (dyninfo (elf-dynamic-info elf)))
+ (if dyninfo
+ (elf-dynamic-info-needed dyninfo)
+ '())))
+
+
+ ;; If FILE needs any libs that are part of ELF-FILES, the RUNPATH
+ ;; is modified accordingly.
+ (define (handle-file file elf-files)
+ (let* ((dep-dirs (concatenate (map (lambda (dep-name)
+ (find-deps dep-name elf-files))
+ (file-needed file)))))
+ (unless (null? dep-dirs)
+ (augment-rpath file (string-join dep-dirs ":")))))
+
+ (define handle-output
+ (match-lambda
+ ((output . directory)
+ (let* ((elf-dirnames (map (lambda (subdir)
+ (string-append directory "/" subdir))
+ elf-directories))
+ (existing-elf-dirs (filter (lambda (dir)
+ (and (file-exists? dir)
+ (file-is-directory? dir)))
+ elf-dirnames))
+ (elf-pred (lambda (name stat)
+ (elf-file? name)))
+ (elf-list (concatenate (map (lambda (dir)
+ (find-files dir elf-pred))
+ existing-elf-dirs))))
+ (for-each (lambda (elf-file)
+ (system* "patchelf" "--shrink-rpath" elf-file)
+ (handle-file elf-file elf-list))
+ elf-list)))))
+ (for-each handle-output outputs)
+ #t)
+
+(define %standard-phases
+ ;; The standard-phases of glib-or-gtk contains a superset of the phases
+ ;; from the gnu-build-system. If the glib-or-gtk? key is #f (the default)
+ ;; then the extra phases will be removed again in (guix build-system meson).
+ (modify-phases glib-or-gtk:%standard-phases
+ (replace 'configure configure)
+ (replace 'build build)
+ (replace 'check check)
+ (replace 'install install)
+ (add-after 'strip 'fix-runpath fix-runpath)))
+
+(define* (meson-build #:key inputs phases
+ #:allow-other-keys #:rest args)
+ "Build the given package, applying all of PHASES in order."
+ (apply gnu:gnu-build #:inputs inputs #:phases phases args))
+
+;;; meson-build-system.scm ends here