;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2012, 2013, 2014, 2015, 2016 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018 Ludovic Courtès <ludo@gnu.org>
;;;
;;; This file is part of GNU Guix.
;;;
#:use-module (srfi srfi-26)
#:use-module (rnrs io ports)
#:export (%standard-phases
+ %license-file-regexp
gnu-build))
;; Commentary:
;;
;; Code:
+(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))
+
(define* (set-SOURCE-DATE-EPOCH #:rest _)
"Set the 'SOURCE_DATE_EPOCH' environment variable. This is used by tools
that incorporate timestamps as a way to tell them to use a fixed timestamp.
(zero? (system* "tar" "xvf" source)))
(chdir (first-subdirectory ".")))))
+(define %bootstrap-scripts
+ ;; Typical names of Autotools "bootstrap" scripts.
+ '("bootstrap" "bootstrap.sh" "autogen.sh"))
+
+(define* (bootstrap #:key (bootstrap-scripts %bootstrap-scripts)
+ #:allow-other-keys)
+ "If the code uses Autotools and \"configure\" is missing, run
+\"autoreconf\". Otherwise do nothing."
+ ;; Note: Run that right after 'unpack' so that the generated files are
+ ;; visible when the 'patch-source-shebangs' phase runs.
+ (if (not (file-exists? "configure"))
+
+ ;; First try one of the BOOTSTRAP-SCRIPTS. If none exists, and it's
+ ;; clearly an Autoconf-based project, run 'autoreconf'. Otherwise, do
+ ;; nothing (perhaps the user removed or overrode the 'configure' phase.)
+ (let ((script (find file-exists? bootstrap-scripts)))
+ ;; GNU packages often invoke the 'git-version-gen' script from
+ ;; 'configure.ac' so make sure it has a valid shebang.
+ (false-if-file-not-found
+ (patch-shebang "build-aux/git-version-gen"))
+
+ (if script
+ (let ((script (string-append "./" script)))
+ (format #t "running '~a'~%" script)
+ (if (executable-file? script)
+ (begin
+ (patch-shebang script)
+ (invoke script))
+ (invoke "sh" script)))
+ (if (or (file-exists? "configure.ac")
+ (file-exists? "configure.in"))
+ (invoke "autoreconf" "-vif")
+ (format #t "no 'configure.ac' or anything like that, \
+doing nothing~%"))))
+ (format #t "GNU build system bootstrapping not needed~%"))
+ #t)
+
;; See <http://bugs.gnu.org/17840>.
(define* (patch-usr-bin-file #:key native-inputs inputs
(patch-/usr/bin/file? #t)
debug-output objcopy-command))
(for-each (lambda (file)
- (and (file-exists? file) ;discard dangling symlinks
- (or (elf-file? file) (ar-file? file))
+ (and (or (elf-file? file) (ar-file? file))
(or (not debug-output)
(make-debug-file file))
+
+ ;; Ensure the file is writable.
+ (begin (make-file-writable file) #t)
+
(zero? (apply system* strip-command
(append strip-flags (list file))))
(or (not debug-output)
(add-debug-link file))))
- (find-files dir)))
+ (find-files dir
+ (lambda (file stat)
+ ;; Ignore symlinks such as:
+ ;; libfoo.so -> libfoo.so.0.0.
+ (eq? 'regular (stat:type stat)))
+ #:stat lstat)))
(or (not strip-binaries?)
(every strip-dir
(for-each validate-output directories)))
#t)
+(define* (reset-gzip-timestamps #:key outputs #:allow-other-keys)
+ "Reset embedded timestamps in gzip files found in OUTPUTS."
+ (define (process-directory directory)
+ (let ((files (find-files directory
+ (lambda (file stat)
+ (and (eq? 'regular (stat:type stat))
+ (or (string-suffix? ".gz" file)
+ (string-suffix? ".tgz" file))
+ (gzip-file? file)))
+ #:stat lstat)))
+ (for-each reset-gzip-timestamp files)))
+
+ (match outputs
+ (((names . directories) ...)
+ (for-each process-directory directories)))
+ #t)
+
(define* (compress-documentation #:key outputs
(compress-documentation? #t)
(documentation-compressor "gzip")
;; Return #t if FILE has hard links.
(> (stat:nlink (lstat file)) 1))
+ (define (points-to-symlink? symlink)
+ ;; Return #t if SYMLINK points to another symbolic link.
+ (let* ((target (readlink symlink))
+ (target-absolute (if (string-prefix? "/" target)
+ target
+ (string-append (dirname symlink)
+ "/" target))))
+ (catch 'system-error
+ (lambda ()
+ (symbolic-link? target-absolute))
+ (lambda args
+ (if (= ENOENT (system-error-errno args))
+ (begin
+ (format (current-error-port)
+ "The symbolic link '~a' target is missing: '~a'\n"
+ symlink target-absolute)
+ #f)
+ (apply throw args))))))
+
(define (maybe-compress-directory directory regexp)
(or (not (directory-exists? directory))
(match (find-files directory regexp)
;; Compress the non-symlink files, and adjust symlinks to refer
;; to the compressed files. Leave files that have hard links
;; unchanged ('gzip' would refuse to compress them anyway.)
- (and (zero? (apply system* documentation-compressor
- (append documentation-compressor-flags
- (remove has-links? regular-files))))
- (every retarget-symlink
- (filter (cut string-match regexp <>)
- symlinks)))))))))
+ ;; Also, do not retarget symbolic links pointing to other
+ ;; symbolic links, since these are not compressed.
+ (and (every retarget-symlink
+ (filter (lambda (symlink)
+ (and (not (points-to-symlink? symlink))
+ (string-match regexp symlink)))
+ symlinks))
+ (zero?
+ (apply system* documentation-compressor
+ (append documentation-compressor-flags
+ (remove has-links? regular-files)))))))))))
(define (maybe-compress output)
(and (maybe-compress-directory (string-append output "/share/man")
outputs)
#t)
+(define %license-file-regexp
+ ;; Regexp matching license files.
+ "^(COPYING.*|LICEN[CS]E.*|[Ll]icen[cs]e.*|Copy[Rr]ight(\\.(txt|md))?)$")
+
+(define* (install-license-files #:key outputs
+ (license-file-regexp %license-file-regexp)
+ #:allow-other-keys)
+ "Install license files matching LICENSE-FILE-REGEXP to 'share/doc'."
+ (let* ((regexp (make-regexp license-file-regexp))
+ (out (or (assoc-ref outputs "out")
+ (match outputs
+ (((_ . output) _ ...)
+ output))))
+ (package (strip-store-file-name out))
+ (directory (string-append out "/share/doc/" package))
+ (files (scandir "." (lambda (file)
+ (regexp-exec regexp file)))))
+ (format #t "installing ~a license files~%" (length files))
+ (for-each (lambda (file)
+ (if (file-is-directory? file)
+ (copy-recursively file directory)
+ (install-file file directory)))
+ files)
+ #t))
+
(define %standard-phases
;; Standard build phases, as a list of symbol/procedure pairs.
(let-syntax ((phases (syntax-rules ()
((_ p ...) `((p . ,p) ...)))))
(phases set-SOURCE-DATE-EPOCH set-paths install-locale unpack
+ bootstrap
patch-usr-bin-file
patch-source-shebangs configure patch-generated-file-shebangs
build check install
validate-documentation-location
delete-info-dir-file
patch-dot-desktop-files
+ install-license-files
+ reset-gzip-timestamps
compress-documentation)))
\f