diff options
Diffstat (limited to 'guix/build')
-rw-r--r-- | guix/build/cmake-build-system.scm | 4 | ||||
-rw-r--r-- | guix/build/glib-or-gtk-build-system.scm | 6 | ||||
-rw-r--r-- | guix/build/gnu-build-system.scm | 73 | ||||
-rw-r--r-- | guix/build/gnu-dist.scm | 10 | ||||
-rw-r--r-- | guix/build/gremlin.scm | 89 | ||||
-rw-r--r-- | guix/build/perl-build-system.scm | 8 | ||||
-rw-r--r-- | guix/build/python-build-system.scm | 12 | ||||
-rw-r--r-- | guix/build/ruby-build-system.scm | 10 | ||||
-rw-r--r-- | guix/build/utils.scm | 81 | ||||
-rw-r--r-- | guix/build/waf-build-system.scm | 8 |
10 files changed, 220 insertions, 81 deletions
diff --git a/guix/build/cmake-build-system.scm b/guix/build/cmake-build-system.scm index d8d437c653..f57622e0f4 100644 --- a/guix/build/cmake-build-system.scm +++ b/guix/build/cmake-build-system.scm @@ -73,8 +73,8 @@ ;; Everything is as with the GNU Build System except for the `configure' ;; and 'check' phases. (modify-phases gnu:%standard-phases - (replace check check) - (replace configure configure))) + (replace 'check check) + (replace 'configure configure))) (define* (cmake-build #:key inputs (phases %standard-phases) #:allow-other-keys #:rest args) diff --git a/guix/build/glib-or-gtk-build-system.scm b/guix/build/glib-or-gtk-build-system.scm index 40f1bb85fa..15d7de2236 100644 --- a/guix/build/glib-or-gtk-build-system.scm +++ b/guix/build/glib-or-gtk-build-system.scm @@ -242,9 +242,9 @@ needed." (define %standard-phases (modify-phases gnu:%standard-phases - (add-after install glib-or-gtk-compile-schemas compile-glib-schemas) - (add-after install glib-or-gtk-icon-cache generate-icon-cache) - (add-after install glib-or-gtk-wrap wrap-all-programs))) + (add-after 'install 'glib-or-gtk-compile-schemas compile-glib-schemas) + (add-after 'install 'glib-or-gtk-icon-cache generate-icon-cache) + (add-after 'install 'glib-or-gtk-wrap wrap-all-programs))) (define* (glib-or-gtk-build #:key inputs (phases %standard-phases) #:allow-other-keys #:rest args) diff --git a/guix/build/gnu-build-system.scm b/guix/build/gnu-build-system.scm index 5ae537150f..c60f8ba162 100644 --- a/guix/build/gnu-build-system.scm +++ b/guix/build/gnu-build-system.scm @@ -18,12 +18,15 @@ (define-module (guix build gnu-build-system) #:use-module (guix build utils) + #:use-module (guix build gremlin) + #:use-module (guix elf) #:use-module (ice-9 ftw) #:use-module (ice-9 match) #:use-module (ice-9 regex) #:use-module (ice-9 format) #:use-module (srfi srfi-1) #:use-module (srfi srfi-26) + #:use-module (rnrs io ports) #:export (%standard-phases gnu-build)) @@ -398,6 +401,64 @@ makefiles." strip-directories))) outputs)))) +(define (every* pred lst) + "This is like 'every', but process all the elements of LST instead of +stopping as soon as PRED returns false. This is useful when PRED has side +effects, such as displaying warnings or error messages." + (let loop ((lst lst) + (result #t)) + (match lst + (() + result) + ((head . tail) + (loop tail (and (pred head) result)))))) + +(define* (validate-runpath #:key + validate-runpath? + (elf-directories '("lib" "lib64" "libexec" + "bin" "sbin")) + outputs #:allow-other-keys) + "When VALIDATE-RUNPATH? is true, validate that all the ELF files in +ELF-DIRECTORIES have their dependencies found in their 'RUNPATH'. + +Since the ELF parser needs to have a copy of files in memory, better run this +phase after stripping." + (define (sub-directory parent) + (lambda (directory) + (let ((directory (string-append parent "/" directory))) + (and (directory-exists? directory) directory)))) + + (define (validate directory) + (define (file=? file1 file2) + (let ((st1 (stat file1)) + (st2 (stat file2))) + (= (stat:ino st1) (stat:ino st2)))) + + ;; There are always symlinks from '.so' to '.so.1' and so on, so delete + ;; duplicates. + (let ((files (delete-duplicates (find-files directory (lambda (file stat) + (elf-file? file))) + file=?))) + (format (current-error-port) + "validating RUNPATH of ~a binaries in ~s...~%" + (length files) directory) + (every* validate-needed-in-runpath files))) + + (if validate-runpath? + (let ((dirs (append-map (match-lambda + (("debug" . _) + ;; The "debug" output is full of ELF files + ;; that are not worth checking. + '()) + ((name . output) + (filter-map (sub-directory output) + elf-directories))) + outputs))) + (every* validate dirs)) + (begin + (format (current-error-port) "skipping RUNPATH validation~%") + #t))) + (define* (validate-documentation-location #:key outputs #:allow-other-keys) "Documentation should go to 'share/info' and 'share/man', not just 'info/' @@ -477,6 +538,16 @@ DOCUMENTATION-COMPRESSOR-FLAGS." (format #t "not compressing documentation~%") #t))) +(define* (delete-info-dir-file #:key outputs #:allow-other-keys) + "Delete any 'share/info/dir' file from OUTPUTS." + (for-each (match-lambda + ((output . directory) + (let ((info-dir-file (string-append directory "/share/info/dir"))) + (when (file-exists? info-dir-file) + (delete-file info-dir-file))))) + outputs) + #t) + (define %standard-phases ;; Standard build phases, as a list of symbol/procedure pairs. (let-syntax ((phases (syntax-rules () @@ -486,7 +557,9 @@ DOCUMENTATION-COMPRESSOR-FLAGS." patch-source-shebangs configure patch-generated-file-shebangs build check install patch-shebangs strip + validate-runpath validate-documentation-location + delete-info-dir-file compress-documentation))) diff --git a/guix/build/gnu-dist.scm b/guix/build/gnu-dist.scm index 887b5e94e9..ad69c6cf16 100644 --- a/guix/build/gnu-dist.scm +++ b/guix/build/gnu-dist.scm @@ -83,10 +83,10 @@ (define %dist-phases ;; Phases for building a source tarball. (modify-phases %standard-phases - (delete strip) - (replace install install-dist) - (replace build build) - (add-before configure autoreconf autoreconf) - (replace unpack copy-source))) + (delete 'strip) + (replace 'install install-dist) + (replace 'build build) + (add-before 'configure 'autoreconf autoreconf) + (replace 'unpack copy-source))) ;;; gnu-dist.scm ends here diff --git a/guix/build/gremlin.scm b/guix/build/gremlin.scm index e8429129e1..30b06034dd 100644 --- a/guix/build/gremlin.scm +++ b/guix/build/gremlin.scm @@ -18,14 +18,22 @@ (define-module (guix build gremlin) #:use-module (guix elf) + #:use-module ((guix build utils) #:select (store-file-name?)) #:use-module (ice-9 match) #:use-module (srfi srfi-1) #:use-module (srfi srfi-9) #:use-module (srfi srfi-26) + #:use-module (srfi srfi-34) + #:use-module (srfi srfi-35) #:use-module (system foreign) #:use-module (rnrs bytevectors) #:use-module (rnrs io ports) - #:export (elf-dynamic-info + #:export (elf-error? + elf-error-elf + invalid-segment-size? + invalid-segment-size-segment + + elf-dynamic-info elf-dynamic-info? elf-dynamic-info-sopath elf-dynamic-info-needed @@ -41,12 +49,31 @@ ;;; ;;; Code: +(define-condition-type &elf-error &error + elf-error? + (elf elf-error-elf)) + +(define-condition-type &invalid-segment-size &elf-error + invalid-segment-size? + (segment invalid-segment-size-segment)) + + (define (dynamic-link-segment elf) "Return the 'PT_DYNAMIC' segment of ELF--i.e., the segment that contains dynamic linking information." - (find (lambda (segment) - (= (elf-segment-type segment) PT_DYNAMIC)) - (elf-segments elf))) + (let ((size (bytevector-length (elf-bytes elf)))) + (find (lambda (segment) + (unless (<= (+ (elf-segment-offset segment) + (elf-segment-filesz segment)) + size) + ;; This happens on separate debug output files created by + ;; 'strip --only-keep-debug' (Binutils 2.25.) + (raise (condition (&invalid-segment-size + (elf elf) + (segment segment))))) + + (= (elf-segment-type segment) PT_DYNAMIC)) + (elf-segments elf)))) (define (word-reader size byte-order) "Return a procedure to read a word of SIZE bytes according to BYTE-ORDER." @@ -197,6 +224,7 @@ value of DT_NEEDED entries is a string.)" "libc.so" "libdl.so" "libm.so" + "libnsl.so" ;NEEDED by nscd "libpthread.so" "libresolv.so" "librt.so" @@ -214,23 +242,42 @@ value of DT_NEEDED entries is a string.)" present in its RUNPATH, or if FILE lacks dynamic-link information. Return #f otherwise. Libraries whose name matches ALWAYS-FOUND? are considered to be always available." - (let* ((elf (call-with-input-file file - (compose parse-elf get-bytevector-all))) - (dyninfo (elf-dynamic-info elf))) - (when dyninfo - (let* ((runpath (elf-dynamic-info-runpath dyninfo)) - (needed (remove always-found? - (elf-dynamic-info-needed dyninfo))) - (not-found (remove (cut search-path runpath <>) - needed))) - (for-each (lambda (lib) - (format (current-error-port) - "error: '~a' depends on '~a', which cannot \ + (guard (c ((invalid-segment-size? c) + (let ((segment (invalid-segment-size-segment c))) + (format (current-error-port) + "~a: error: offset + size of segment ~a (type ~a) \ +exceeds total size~%" + file + (elf-segment-index segment) + (elf-segment-type segment)) + #f))) + + (let* ((elf (call-with-input-file file + (compose parse-elf get-bytevector-all))) + (dyninfo (elf-dynamic-info elf))) + (when dyninfo + (let* ((runpath (filter store-file-name? + (elf-dynamic-info-runpath dyninfo))) + (bogus (remove store-file-name? + (elf-dynamic-info-runpath dyninfo))) + (needed (remove always-found? + (elf-dynamic-info-needed dyninfo))) + (not-found (remove (cut search-path runpath <>) + needed))) + ;; XXX: $ORIGIN is not supported. + (unless (null? bogus) + (format (current-error-port) + "~a: warning: RUNPATH contains bogus entries: ~s~%" + file bogus)) + + (for-each (lambda (lib) + (format (current-error-port) + "~a: error: depends on '~a', which cannot \ be found in RUNPATH ~s~%" - file lib runpath)) - not-found) - ;; (when (null? not-found) - ;; (format (current-error-port) "~a is OK~%" file)) - (null? not-found))))) + file lib runpath)) + not-found) + ;; (when (null? not-found) + ;; (format (current-error-port) "~a is OK~%" file)) + (null? not-found)))))) ;;; gremlin.scm ends here diff --git a/guix/build/perl-build-system.scm b/guix/build/perl-build-system.scm index 9ca5353bb9..8f480eae16 100644 --- a/guix/build/perl-build-system.scm +++ b/guix/build/perl-build-system.scm @@ -72,10 +72,10 @@ ;; Everything is as with the GNU Build System except for the `configure', ;; `build', `check', and `install' phases. (modify-phases gnu:%standard-phases - (replace install install) - (replace check check) - (replace build build) - (replace configure configure))) + (replace 'install install) + (replace 'check check) + (replace 'build build) + (replace 'configure configure))) (define* (perl-build #:key inputs (phases %standard-phases) #:allow-other-keys #:rest args) diff --git a/guix/build/python-build-system.scm b/guix/build/python-build-system.scm index 9f853134bd..26a7254db9 100644 --- a/guix/build/python-build-system.scm +++ b/guix/build/python-build-system.scm @@ -123,12 +123,12 @@ installed with setuptools." ;; 'configure' and 'build' phases are not needed. Everything is done during ;; 'install'. (modify-phases gnu:%standard-phases - (delete configure) - (replace install install) - (replace check check) - (replace build build) - (add-after install wrap wrap) - (add-before strip rename-pth-file rename-pth-file))) + (delete 'configure) + (replace 'install install) + (replace 'check check) + (replace 'build build) + (add-after 'install 'wrap wrap) + (add-before 'strip 'rename-pth-file rename-pth-file))) (define* (python-build #:key inputs (phases %standard-phases) #:allow-other-keys #:rest args) diff --git a/guix/build/ruby-build-system.scm b/guix/build/ruby-build-system.scm index a143df467f..531cf382ae 100644 --- a/guix/build/ruby-build-system.scm +++ b/guix/build/ruby-build-system.scm @@ -72,11 +72,11 @@ directory." (define %standard-phases (modify-phases gnu:%standard-phases - (delete configure) - (add-after unpack gitify gitify) - (replace build build) - (replace install install) - (replace check check))) + (delete 'configure) + (add-after 'unpack 'gitify gitify) + (replace 'build build) + (replace 'install install) + (replace 'check check))) (define* (ruby-build #:key inputs (phases %standard-phases) #:allow-other-keys #:rest args) diff --git a/guix/build/utils.scm b/guix/build/utils.scm index a5a6167a8c..676a0120e3 100644 --- a/guix/build/utils.scm +++ b/guix/build/utils.scm @@ -32,6 +32,7 @@ #:re-export (alist-cons alist-delete) #:export (%store-directory + store-file-name? parallel-job-count directory-exists? @@ -44,6 +45,7 @@ mkdir-p copy-recursively delete-file-recursively + file-name-predicate find-files search-path-as-list @@ -80,6 +82,10 @@ (or (getenv "NIX_STORE") "/gnu/store")) +(define (store-file-name? file) + "Return true if FILE is in the store." + (string-prefix? (%store-directory) file)) + (define parallel-job-count ;; Number of processes to be passed next to GNU Make's `-j' argument. (make-parameter @@ -263,33 +269,46 @@ errors." ;; Don't follow symlinks. lstat))) -(define (find-files dir regexp) - "Return the lexicographically sorted list of files under DIR whose basename -matches REGEXP." - (define file-rx - (if (regexp? regexp) - regexp - (make-regexp regexp))) - - ;; Sort the result to get deterministic results. - (sort (file-system-fold (const #t) - (lambda (file stat result) ; leaf - (if (regexp-exec file-rx (basename file)) - (cons file result) - result)) - (lambda (dir stat result) ; down - result) - (lambda (dir stat result) ; up - result) - (lambda (file stat result) ; skip - result) - (lambda (file stat errno result) - (format (current-error-port) "find-files: ~a: ~a~%" - file (strerror errno)) - result) - '() - dir) - string<?)) +(define (file-name-predicate regexp) + "Return a predicate that returns true when passed a file name whose base +name matches REGEXP." + (let ((file-rx (if (regexp? regexp) + regexp + (make-regexp regexp)))) + (lambda (file stat) + (regexp-exec file-rx (basename file))))) + +(define* (find-files dir #:optional (pred (const #t)) + #:key (stat lstat)) + "Return the lexicographically sorted list of files under DIR for which PRED +returns true. PRED is passed two arguments: the absolute file name, and its +stat buffer; the default predicate always returns true. PRED can also be a +regular expression, in which case it is equivalent to (file-name-predicate +PRED). STAT is used to obtain file information; using 'lstat' means that +symlinks are not followed." + (let ((pred (if (procedure? pred) + pred + (file-name-predicate pred)))) + ;; Sort the result to get deterministic results. + (sort (file-system-fold (const #t) + (lambda (file stat result) ; leaf + (if (pred file stat) + (cons file result) + result)) + (lambda (dir stat result) ; down + result) + (lambda (dir stat result) ; up + result) + (lambda (file stat result) ; skip + result) + (lambda (file stat errno result) + (format (current-error-port) "find-files: ~a: ~a~%" + file (strerror errno)) + result) + '() + dir + stat) + string<?))) ;;; @@ -446,13 +465,13 @@ an expression evaluating to a procedure." (define-syntax %modify-phases (syntax-rules (delete replace add-before add-after) ((_ phases (delete old-phase-name)) - (alist-delete 'old-phase-name phases)) + (alist-delete old-phase-name phases)) ((_ phases (replace old-phase-name new-phase)) - (alist-replace 'old-phase-name new-phase phases)) + (alist-replace old-phase-name new-phase phases)) ((_ phases (add-before old-phase-name new-phase-name new-phase)) - (alist-cons-before 'old-phase-name 'new-phase-name new-phase phases)) + (alist-cons-before old-phase-name new-phase-name new-phase phases)) ((_ phases (add-after old-phase-name new-phase-name new-phase)) - (alist-cons-after 'old-phase-name 'new-phase-name new-phase phases)))) + (alist-cons-after old-phase-name new-phase-name new-phase phases)))) ;;; diff --git a/guix/build/waf-build-system.scm b/guix/build/waf-build-system.scm index d172c5a836..85f0abcfd6 100644 --- a/guix/build/waf-build-system.scm +++ b/guix/build/waf-build-system.scm @@ -70,10 +70,10 @@ (define %standard-phases (modify-phases gnu:%standard-phases - (replace configure configure) - (replace build build) - (replace check check) - (replace install install))) + (replace 'configure configure) + (replace 'build build) + (replace 'check check) + (replace 'install install))) (define* (waf-build #:key inputs (phases %standard-phases) #:allow-other-keys #:rest args) |