8b2a9be465
* guix/build/clojure-build-system.scm (reset-class-timestamps): Use MKDTEMP from Guile core instead of MKDTEMP!.
189 lines
8.0 KiB
Scheme
189 lines
8.0 KiB
Scheme
;;; GNU Guix --- Functional package management for GNU
|
|
;;; Copyright © 2018 Alex Vong <alexvong1995@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 clojure-build-system)
|
|
#:use-module ((guix build ant-build-system)
|
|
#:select ((%standard-phases . %standard-phases@ant)
|
|
ant-build))
|
|
#:use-module (guix build clojure-utils)
|
|
#:use-module (guix build java-utils)
|
|
#:use-module (guix build utils)
|
|
#:use-module (ice-9 match)
|
|
#:use-module (ice-9 regex)
|
|
#:use-module (srfi srfi-1)
|
|
#:use-module (srfi srfi-26)
|
|
#:export (%standard-phases
|
|
clojure-build
|
|
reset-class-timestamps))
|
|
|
|
;; Commentary:
|
|
;;
|
|
;; Builder-side code of the standard build procedure for Clojure packages.
|
|
;;
|
|
;; Code:
|
|
|
|
(define* (compile-java #:key
|
|
java-source-dirs java-compile-dir
|
|
#:allow-other-keys)
|
|
"Compile java sources for use in clojure-build-system."
|
|
(let ((java-files (append-map (lambda (dir)
|
|
(find-files dir "\\.java$"))
|
|
java-source-dirs)))
|
|
(mkdir-p java-compile-dir)
|
|
(when (not (null? java-files))
|
|
(apply invoke
|
|
"javac"
|
|
"-verbose"
|
|
"-d" java-compile-dir
|
|
java-files))))
|
|
|
|
(define* (build #:key
|
|
source-dirs java-source-dirs
|
|
compile-dir java-compile-dir
|
|
jar-names main-class omit-source?
|
|
aot-include aot-exclude
|
|
#:allow-other-keys)
|
|
"Standard 'build' phase for clojure-build-system."
|
|
(let* ((libs (append-map find-clojure-libs source-dirs))
|
|
(libs* (include-list\exclude-list aot-include
|
|
aot-exclude
|
|
#:all-list libs)))
|
|
(mkdir-p compile-dir)
|
|
(eval-with-clojure `(run! compile ',libs*)
|
|
(cons* compile-dir
|
|
java-compile-dir
|
|
source-dirs))
|
|
(let ((source-dir-files-alist (map (lambda (dir)
|
|
(cons dir (find-files* dir)))
|
|
(append source-dirs
|
|
java-source-dirs)))
|
|
;; workaround transitive compilation in Clojure
|
|
(classes (filter (lambda (class)
|
|
(any (cut compiled-from? class <>)
|
|
libs*))
|
|
(find-files* compile-dir))))
|
|
(for-each (cut create-jar <> (cons* (cons compile-dir classes)
|
|
(cons java-compile-dir
|
|
(find-files* java-compile-dir))
|
|
(if omit-source?
|
|
'()
|
|
source-dir-files-alist))
|
|
#:main-class main-class)
|
|
jar-names)
|
|
#t)))
|
|
|
|
(define* (check #:key
|
|
test-dirs
|
|
jar-names
|
|
tests? test-include test-exclude
|
|
#:allow-other-keys)
|
|
"Standard 'check' phase for clojure-build-system. Note that TEST-EXCLUDE has
|
|
priority over TEST-INCLUDE."
|
|
(if tests?
|
|
(let* ((libs (append-map find-clojure-libs test-dirs))
|
|
(libs* (include-list\exclude-list test-include
|
|
test-exclude
|
|
#:all-list libs)))
|
|
(for-each (lambda (jar)
|
|
(eval-with-clojure `(do (apply require
|
|
'(clojure.test ,@libs*))
|
|
(if (clojure.test/successful?
|
|
(apply clojure.test/run-tests
|
|
',libs*))
|
|
(System/exit 0)
|
|
(System/exit 1)))
|
|
(cons jar test-dirs)))
|
|
jar-names)))
|
|
#t)
|
|
|
|
(define (regular-jar-file? file stat)
|
|
"Predicate returning true if FILE is ending on '.jar'
|
|
and STAT indicates it is a regular file."
|
|
(and (string-suffix? ".jar" file)
|
|
(eq? 'regular (stat:type stat))))
|
|
|
|
;; XXX: The only difference compared to 'strip-jar-timestamps' in
|
|
;; ant-build-system.scm is the date. TODO: Adjust and factorize.
|
|
(define* (reset-class-timestamps #:key outputs #:allow-other-keys)
|
|
"Unpack all jar archives, reset the timestamp of all contained class files,
|
|
and repack them. This is necessary to ensure that archives are reproducible."
|
|
(define (repack-archive jar)
|
|
(format #t "resetting class timestamps and repacking ~a\n" jar)
|
|
|
|
;; Note: .class files need to be strictly newer than source files,
|
|
;; otherwise the Clojure compiler will recompile sources.
|
|
(let* ((early-1980 315619200) ; 1980-01-02 UTC
|
|
(dir (mkdtemp "jar-contents.XXXXXX"))
|
|
(manifest (string-append dir "/META-INF/MANIFEST.MF")))
|
|
(with-directory-excursion dir
|
|
(invoke "jar" "xf" jar))
|
|
(delete-file jar)
|
|
(for-each (lambda (file)
|
|
(let ((s (lstat file)))
|
|
(unless (eq? (stat:type s) 'symlink)
|
|
(when (string-match "^(.*)\\.class$" file)
|
|
(utime file early-1980 early-1980)))))
|
|
(find-files dir #:directories? #t))
|
|
;; The jar tool will always set the timestamp on the manifest file
|
|
;; and the containing directory to the current time, even when we
|
|
;; reuse an existing manifest file. To avoid this we use "zip"
|
|
;; instead of "jar". It is important that the manifest appears
|
|
;; first.
|
|
(with-directory-excursion dir
|
|
(let* ((files (find-files "." ".*" #:directories? #t))
|
|
;; To ensure that the reference scanner can detect all
|
|
;; store references in the jars we disable compression
|
|
;; with the "-0" option.
|
|
(command (if (file-exists? manifest)
|
|
`("zip" "-0" "-X" ,jar ,manifest ,@files)
|
|
`("zip" "-0" "-X" ,jar ,@files))))
|
|
(apply invoke command)))
|
|
(utime jar 0 0)))
|
|
(for-each (match-lambda
|
|
((output . directory)
|
|
(for-each repack-archive
|
|
(find-files directory regular-jar-file?))))
|
|
outputs))
|
|
|
|
(define-with-docs install
|
|
"Standard 'install' phase for clojure-build-system."
|
|
(install-jars "./"))
|
|
|
|
(define-with-docs %standard-phases
|
|
"Standard build phases for clojure-build-system."
|
|
(modify-phases %standard-phases@ant
|
|
(add-before 'build 'compile-java compile-java)
|
|
(replace 'build build)
|
|
(replace 'check check)
|
|
(replace 'install install)
|
|
(add-after 'install-license-files 'install-doc install-doc)
|
|
(add-after 'reset-gzip-timestamps 'reset-class-timestamps reset-class-timestamps)))
|
|
|
|
(define* (clojure-build #:key
|
|
inputs
|
|
(phases %standard-phases)
|
|
#:allow-other-keys
|
|
#:rest args)
|
|
"Build the given Clojure package, applying all of PHASES in order."
|
|
(apply ant-build
|
|
#:inputs inputs
|
|
#:phases phases
|
|
args))
|
|
|
|
;;; clojure-build-system.scm ends here
|