ef788ee2dc
Until now the .go files were generated first, and only after that the .scm files were installed into the target location. That led to a lot of messages about `source file ... newer than compiled' if the custom 'check phase tried to load the compiled files. Swapping the order of the actions resolves the issue allowing the tests to be written without lot of noise in the build log. For final artifacts it was not a problem, since daemon resets the timestamps. * guix/build/guile-build-system.scm (build): Install .scm before producing .go. Change-Id: I3428d144fcbaa6c904ee662193c3bca82589e344 Signed-off-by: Ludovic Courtès <ludo@gnu.org>
244 lines
9.2 KiB
Scheme
244 lines
9.2 KiB
Scheme
;;; GNU Guix --- Functional package management for GNU
|
|
;;; Copyright © 2018, 2019 Ludovic Courtès <ludo@gnu.org>
|
|
;;;
|
|
;;; 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 guile-build-system)
|
|
#:use-module ((guix build gnu-build-system) #:prefix gnu:)
|
|
#:use-module (guix build utils)
|
|
#:use-module (srfi srfi-1)
|
|
#:use-module (srfi srfi-26)
|
|
#:use-module (ice-9 match)
|
|
#:use-module (ice-9 popen)
|
|
#:use-module (ice-9 rdelim)
|
|
#:use-module (ice-9 regex)
|
|
#:use-module (ice-9 format)
|
|
#:use-module (guix build utils)
|
|
#:export (target-guile-effective-version
|
|
target-guile-scm+go
|
|
%standard-phases
|
|
guile-build))
|
|
|
|
(define* (target-guile-effective-version #:optional guile)
|
|
"Return the effective version of GUILE or whichever 'guile' is in $PATH.
|
|
Return #false if it cannot be determined."
|
|
(let* ((pipe (open-pipe* OPEN_READ
|
|
(if guile
|
|
(string-append guile "/bin/guile")
|
|
"guile")
|
|
"-c" "(display (effective-version))"))
|
|
(line (read-line pipe)))
|
|
(and (zero? (close-pipe pipe))
|
|
(string? line)
|
|
line)))
|
|
|
|
(define* (target-guile-scm+go output #:optional guile)
|
|
"Return paths under `output' for scm and go files for effective version of
|
|
GUILE or whichever `guile' is in $PATH. Raises an error if they cannot be
|
|
determined."
|
|
(let* ((version (or (target-guile-effective-version guile)
|
|
(error "Cannot determine the effective target guile version.")))
|
|
(scm (string-append output "/share/guile/site/" version))
|
|
(go (string-append output "/lib/guile/" version "/site-ccache")))
|
|
(values scm go)))
|
|
|
|
(define (file-sans-extension file) ;TODO: factorize
|
|
"Return the substring of FILE without its extension, if any."
|
|
(let ((dot (string-rindex file #\.)))
|
|
(if dot
|
|
(substring file 0 dot)
|
|
file)))
|
|
|
|
(define %scheme-file-regexp
|
|
;; Regexp to match Scheme files.
|
|
"\\.(scm|sls)$")
|
|
|
|
(define %documentation-file-regexp
|
|
;; Regexp to match README files and the likes.
|
|
"^(README.*|.*\\.html|.*\\.org|.*\\.md)$")
|
|
|
|
(define* (set-locale-path #:key inputs native-inputs
|
|
#:allow-other-keys)
|
|
"Set 'GUIX_LOCPATH'."
|
|
(match (assoc-ref (or native-inputs inputs) "locales")
|
|
(#f #t)
|
|
(locales
|
|
(setenv "GUIX_LOCPATH" (string-append locales "/lib/locale"))
|
|
#t)))
|
|
|
|
(define* (invoke-each commands
|
|
#:key (max-processes (current-processor-count))
|
|
report-progress)
|
|
"Run each command in COMMANDS in a separate process, using up to
|
|
MAX-PROCESSES processes in parallel. Call REPORT-PROGRESS at each step.
|
|
Raise an error if one of the processes exit with non-zero."
|
|
(define total
|
|
(length commands))
|
|
|
|
(define processes
|
|
(make-hash-table))
|
|
|
|
(define (wait-for-one-process)
|
|
(match (waitpid WAIT_ANY)
|
|
((pid . status)
|
|
(let ((command (hashv-ref processes pid)))
|
|
(hashv-remove! processes command)
|
|
(unless (zero? (status:exit-val status))
|
|
(format (current-error-port)
|
|
"process '~{~a ~}' failed with status ~a~%"
|
|
command status)
|
|
(exit 1))))))
|
|
|
|
(define (fork-and-run-command command)
|
|
(match (primitive-fork)
|
|
(0
|
|
(dynamic-wind
|
|
(const #t)
|
|
(lambda ()
|
|
(apply execlp command))
|
|
(lambda ()
|
|
(primitive-exit 127))))
|
|
(pid
|
|
(hashv-set! processes pid command)
|
|
#t)))
|
|
|
|
(let loop ((commands commands)
|
|
(running 0)
|
|
(completed 0))
|
|
(match commands
|
|
(()
|
|
(or (zero? running)
|
|
(let ((running (- running 1))
|
|
(completed (+ completed 1)))
|
|
(wait-for-one-process)
|
|
(report-progress total completed)
|
|
(loop commands running completed))))
|
|
((command . rest)
|
|
(if (< running max-processes)
|
|
(let ((running (+ 1 running)))
|
|
(fork-and-run-command command)
|
|
(loop rest running completed))
|
|
(let ((running (- running 1))
|
|
(completed (+ completed 1)))
|
|
(wait-for-one-process)
|
|
(report-progress total completed)
|
|
(loop commands running completed)))))))
|
|
|
|
(define* (report-build-progress total completed
|
|
#:optional (log-port (current-error-port)))
|
|
"Report that COMPLETED out of TOTAL files have been completed."
|
|
(format log-port "[~2d/~2d] Compiling...~%"
|
|
completed total)
|
|
(force-output log-port))
|
|
|
|
(define* (build #:key outputs inputs native-inputs
|
|
(source-directory ".")
|
|
(compile-flags '())
|
|
(scheme-file-regexp %scheme-file-regexp)
|
|
(not-compiled-file-regexp #f)
|
|
target
|
|
#:allow-other-keys)
|
|
"Build files in SOURCE-DIRECTORY that match SCHEME-FILE-REGEXP. Files
|
|
matching NOT-COMPILED-FILE-REGEXP, if true, are not compiled but are
|
|
installed; this is useful for files that are meant to be included."
|
|
(let* ((out (assoc-ref outputs "out"))
|
|
(guile (assoc-ref (or native-inputs inputs) "guile"))
|
|
(effective (target-guile-effective-version guile))
|
|
(module-dir (string-append out "/share/guile/site/"
|
|
effective))
|
|
(go-dir (string-append out "/lib/guile/"
|
|
effective "/site-ccache/"))
|
|
(guild (string-append guile "/bin/guild"))
|
|
(flags (if target
|
|
(cons (string-append "--target=" target)
|
|
compile-flags)
|
|
compile-flags)))
|
|
(if target
|
|
(format #t "Cross-compiling for '~a' with Guile ~a...~%"
|
|
target effective)
|
|
(format #t "Compiling with Guile ~a...~%" effective))
|
|
(format #t "compile flags: ~s~%" flags)
|
|
|
|
;; Make installation directories.
|
|
(mkdir-p module-dir)
|
|
(mkdir-p go-dir)
|
|
|
|
;; Compile .scm files and install.
|
|
(setenv "GUILE_AUTO_COMPILE" "0")
|
|
(setenv "GUILE_LOAD_COMPILED_PATH"
|
|
(string-append go-dir
|
|
(match (getenv "GUILE_LOAD_COMPILED_PATH")
|
|
(#f "")
|
|
(path (string-append ":" path)))))
|
|
|
|
(let ((source-files
|
|
(with-directory-excursion source-directory
|
|
(find-files "." scheme-file-regexp))))
|
|
(for-each
|
|
(lambda (file)
|
|
(install-file (string-append source-directory "/" file)
|
|
(string-append module-dir
|
|
"/" (dirname file))))
|
|
source-files)
|
|
(invoke-each
|
|
(filter-map (lambda (file)
|
|
(and (or (not not-compiled-file-regexp)
|
|
(not (string-match not-compiled-file-regexp
|
|
file)))
|
|
(cons* guild
|
|
"guild" "compile"
|
|
"-L" source-directory
|
|
"-o" (string-append go-dir
|
|
(file-sans-extension file)
|
|
".go")
|
|
(string-append source-directory "/" file)
|
|
flags)))
|
|
source-files)
|
|
#:max-processes (parallel-job-count)
|
|
#:report-progress report-build-progress))
|
|
#t))
|
|
|
|
(define* (install-documentation #:key outputs
|
|
(documentation-file-regexp
|
|
%documentation-file-regexp)
|
|
#:allow-other-keys)
|
|
"Install files that match DOCUMENTATION-FILE-REGEXP."
|
|
(let* ((out (assoc-ref outputs "out"))
|
|
(doc (string-append out "/share/doc/"
|
|
(strip-store-file-name out))))
|
|
(for-each (cut install-file <> doc)
|
|
(find-files "." documentation-file-regexp))
|
|
#t))
|
|
|
|
(define %standard-phases
|
|
(modify-phases gnu:%standard-phases
|
|
(delete 'bootstrap)
|
|
(delete 'configure)
|
|
(add-before 'install-locale 'set-locale-path
|
|
set-locale-path)
|
|
(replace 'build build)
|
|
(add-after 'build 'install-documentation
|
|
install-documentation)
|
|
(delete 'check)
|
|
(delete 'strip)
|
|
(delete 'validate-runpath)
|
|
(delete 'install)))
|
|
|
|
(define* (guile-build #:key (phases %standard-phases)
|
|
#:allow-other-keys #:rest args)
|
|
"Build the given Guile package, applying all of PHASES in order."
|
|
(apply gnu:gnu-build #:phases phases args))
|