123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- ;;; GNU Guix --- Functional package management for GNU
- ;;; Copyright © 2016, 2018 Ricardo Wurmus <rekado@elephly.net>
- ;;; Copyright © 2019 Björn Höfling <bjoern.hoefling@bjoernhoefling.de>
- ;;;
- ;;; 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 ant-build-system)
- #:use-module ((guix build gnu-build-system) #:prefix gnu:)
- #:use-module (guix build syscalls)
- #:use-module (guix build utils)
- #:use-module (sxml simple)
- #:use-module (ice-9 match)
- #:use-module (ice-9 ftw)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-26)
- #:export (%standard-phases
- ant-build))
- ;; Commentary:
- ;;
- ;; Builder-side code of the standard build procedure for Java packages using
- ;; Ant.
- ;;
- ;; Code:
- (define* (default-build.xml jar-name prefix #:optional
- (source-dir ".") (test-dir "./test") (main-class #f)
- (test-include '("**/*Test.java"))
- (test-exclude '("**/Abstract*Test.java")))
- "Create a simple build.xml with standard targets for Ant."
- (call-with-output-file "build.xml"
- (lambda (port)
- (sxml->xml
- `(project (@ (basedir ".")
- (name ,jar-name))
- (property (@ (name "classes.dir")
- (value "${basedir}/build/classes")))
- (property (@ (name "manifest.dir")
- (value "${basedir}/build/manifest")))
- (property (@ (name "manifest.file")
- (value "${manifest.dir}/MANIFEST.MF")))
- (property (@ (name "jar.dir")
- (value "${basedir}/build/jar")))
- (property (@ (name "dist.dir")
- (value ,prefix)))
- (property (@ (name "test.home")
- (value ,test-dir)))
- (property (@ (name "test.classes.dir")
- (value "${basedir}/build/test-classes")))
- ;; respect the CLASSPATH environment variable
- (property (@ (name "build.sysclasspath")
- (value "first")))
- (property (@ (environment "env")))
- (path (@ (id "classpath"))
- (pathelement (@ (location "${env.CLASSPATH}"))))
- (target (@ (name "manifest"))
- (mkdir (@ (dir "${manifest.dir}")))
- (manifest (@ (file "${manifest.file}"))
- ,(if main-class
- `(attribute (@ (name "Main-Class")
- (value ,main-class)))
- "")))
- (target (@ (name "compile"))
- (mkdir (@ (dir "${classes.dir}")))
- (javac (@ (includeantruntime "false")
- (srcdir ,source-dir)
- (destdir "${classes.dir}")
- (classpath (@ (refid "classpath"))))))
- (target (@ (name "compile-tests"))
- (mkdir (@ (dir "${test.classes.dir}")))
- (javac (@ (includeantruntime "false")
- (srcdir ,test-dir)
- (destdir "${test.classes.dir}"))
- (classpath
- (pathelement (@ (path "${env.CLASSPATH}")))
- (pathelement (@ (location "${classes.dir}")))
- (pathelement (@ (location "${test.classes.dir}"))))))
- (target (@ (name "check")
- (depends "compile-tests"))
- (mkdir (@ (dir "${test.home}/test-reports")))
- (junit (@ (printsummary "true")
- (showoutput "true")
- (fork "yes")
- (haltonfailure "yes"))
- (classpath
- (pathelement (@ (path "${env.CLASSPATH}")))
- (pathelement (@ (location "${test.home}/resources")))
- (pathelement (@ (location "${classes.dir}")))
- (pathelement (@ (location "${test.classes.dir}"))))
- (formatter (@ (type "plain")
- (usefile "true")))
- (batchtest (@ (fork "yes")
- (todir "${test.home}/test-reports"))
- (fileset (@ (dir "${test.home}/java"))
- ,@(map (lambda (file)
- `(include (@ (name ,file))))
- test-include)
- ,@(map (lambda (file)
- `(exclude (@ (name ,file))))
- test-exclude)))))
- (target (@ (name "jar")
- (depends "compile, manifest"))
- (mkdir (@ (dir "${jar.dir}")))
- (jar (@ (destfile ,(string-append "${jar.dir}/" jar-name))
- (manifest "${manifest.file}")
- (basedir "${classes.dir}"))))
- (target (@ (name "install"))
- (copy (@ (todir "${dist.dir}"))
- (fileset (@ (dir "${jar.dir}"))
- (include (@ (name "**/*.jar")))))))
- port)))
- (utime "build.xml" 0 0)
- #t)
- (define (generate-classpath inputs)
- "Return a colon-separated string of full paths to jar files found among the
- INPUTS."
- (string-join
- (apply append (map (match-lambda
- ((_ . dir)
- (find-files dir "\\.jar$")))
- inputs)) ":"))
- (define* (unpack #:key source #:allow-other-keys)
- "Unpack the jar archive SOURCE. When SOURCE is not a jar archive fall back
- to the default GNU unpack strategy."
- (if (string-suffix? ".jar" source)
- (begin
- (mkdir "src")
- (with-directory-excursion "src"
- (invoke "jar" "-xf" source))
- #t)
- ;; Use GNU unpack strategy for things that aren't jar archives.
- ((assq-ref gnu:%standard-phases 'unpack) #:source source)))
- (define* (configure #:key inputs outputs (jar-name #f)
- (source-dir "src")
- (test-dir "src/test")
- (main-class #f)
- (test-include '("**/*Test.java"))
- (test-exclude '("**/Abstract*.java")) #:allow-other-keys)
- (when jar-name
- (default-build.xml jar-name
- (string-append (assoc-ref outputs "out")
- "/share/java")
- source-dir test-dir main-class test-include test-exclude))
- (setenv "JAVA_HOME" (assoc-ref inputs "jdk"))
- (setenv "CLASSPATH" (generate-classpath inputs))
- #t)
- (define* (build #:key (make-flags '()) (build-target "jar")
- #:allow-other-keys)
- (apply invoke `("ant" ,build-target ,@make-flags)))
- (define (regular-jar-file-predicate file stat)
- "Predicate returning true if FILE is ending on '.jar'
- and STAT indicates it is a regular file."
- (and ((file-name-predicate "\\.jar$") file stat)
- (eq? 'regular (stat:type stat))))
- (define* (generate-jar-indices #:key outputs #:allow-other-keys)
- "Generate file \"META-INF/INDEX.LIST\". This file does not use word wraps
- and is preferred over \"META-INF/MANIFEST.MF\", which does use word wraps,
- by Java when resolving dependencies. So we make sure to create it so that
- grafting works - and so that the garbage collector doesn't collect
- dependencies of this jar file."
- (define (generate-index jar)
- (invoke "jar" "-i" jar))
- (for-each (match-lambda
- ((output . directory)
- (for-each generate-index
- (find-files
- directory
- regular-jar-file-predicate))))
- outputs)
- #t)
- (define* (strip-jar-timestamps #:key outputs
- #:allow-other-keys)
- "Unpack all jar archives, reset the timestamp of all contained files, and
- repack them. This is necessary to ensure that archives are reproducible."
- (define (repack-archive jar)
- (format #t "repacking ~a\n" jar)
- (let* ((dir (mkdtemp! "jar-contents.XXXXXX"))
- (manifest (string-append dir "/META-INF/MANIFEST.MF")))
- (with-directory-excursion dir
- (invoke "jar" "xf" jar))
- (delete-file jar)
- ;; XXX: copied from (gnu build install)
- (for-each (lambda (file)
- (let ((s (lstat file)))
- (unless (eq? (stat:type s) 'symlink)
- (utime file 0 0 0 0))))
- (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)
- #t))
- (for-each (match-lambda
- ((output . directory)
- (for-each repack-archive
- (find-files directory regular-jar-file-predicate))))
- outputs)
- #t)
- (define* (check #:key target (make-flags '()) (tests? (not target))
- (test-target "check")
- #:allow-other-keys)
- (if tests?
- (apply invoke `("ant" ,test-target ,@make-flags))
- (format #t "test suite not run~%"))
- #t)
- (define* (install #:key (make-flags '()) #:allow-other-keys)
- (apply invoke `("ant" "install" ,@make-flags)))
- (define %standard-phases
- (modify-phases gnu:%standard-phases
- (replace 'unpack unpack)
- (delete 'bootstrap)
- (replace 'configure configure)
- (replace 'build build)
- (replace 'check check)
- (replace 'install install)
- (add-after 'install 'reorder-jar-content
- strip-jar-timestamps)
- (add-after 'reorder-jar-content 'generate-jar-indices generate-jar-indices)
- (add-after 'generate-jar-indices 'strip-jar-timestamps
- strip-jar-timestamps)))
- (define* (ant-build #:key inputs (phases %standard-phases)
- #:allow-other-keys #:rest args)
- "Build the given Java package, applying all of PHASES in order."
- (apply gnu:gnu-build #:inputs inputs #:phases phases args))
- ;;; ant-build-system.scm ends here
|