summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--doc/guix.texi21
-rw-r--r--guix/build-system/ant.scm149
-rw-r--r--guix/build/ant-build-system.scm160
4 files changed, 332 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
index f67de43f48..92a3bc5f82 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -56,6 +56,7 @@ MODULES =					\
   guix/graph.scm				\
   guix/cve.scm					\
   guix/build-system.scm				\
+  guix/build-system/ant.scm			\
   guix/build-system/cmake.scm			\
   guix/build-system/emacs.scm			\
   guix/build-system/glib-or-gtk.scm		\
@@ -75,6 +76,7 @@ MODULES =					\
   guix/cvs-download.scm				\
   guix/svn-download.scm				\
   guix/ui.scm					\
+  guix/build/ant-build-system.scm		\
   guix/build/download.scm			\
   guix/build/cmake-build-system.scm		\
   guix/build/emacs-build-system.scm		\
diff --git a/doc/guix.texi b/doc/guix.texi
index f7deafa516..7509ea9dc2 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -2720,6 +2720,27 @@ of @var{gnu-build-system}, and differ mainly in the set of inputs
 implicitly added to the build process, and in the list of phases
 executed.  Some of these build systems are listed below.
 
+@defvr {Scheme Variable} ant-build-system
+This variable is exported by @code{(guix build-system ant)}.  It
+implements the build procedure for Java packages that can be built with
+@url{http://ant.apache.org/, Ant build tool}.
+
+It adds both @code{ant} and the @dfn{Java Development Kit} (JDK) as
+provided by the @code{icedtea} package to the set of inputs.  Different
+packages can be specified with the @code{#:ant} and @code{#:jdk}
+parameters, respectively.
+
+When the original package does not provide a suitable Ant build file,
+the parameter @code{#:jar-name} can be used to generate a minimal Ant
+build file @file{build.xml} with tasks to build the specified jar
+archive.
+
+The parameter @code{#:build-target} can be used to specify the Ant task
+that should be run during the @code{build} phase.  By default the
+``jar'' task will be run.
+
+@end defvr
+
 @defvr {Scheme Variable} cmake-build-system
 This variable is exported by @code{(guix build-system cmake)}.  It
 implements the build procedure for packages using the
diff --git a/guix/build-system/ant.scm b/guix/build-system/ant.scm
new file mode 100644
index 0000000000..d3054e5ffa
--- /dev/null
+++ b/guix/build-system/ant.scm
@@ -0,0 +1,149 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2016 Ricardo Wurmus <rekado@elephly.net>
+;;;
+;;; 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-system ant)
+  #:use-module (guix store)
+  #:use-module (guix utils)
+  #:use-module (guix packages)
+  #:use-module (guix derivations)
+  #:use-module (guix search-paths)
+  #:use-module (guix build-system)
+  #:use-module (guix build-system gnu)
+  #:use-module (ice-9 match)
+  #:use-module (srfi srfi-26)
+  #:export (%ant-build-system-modules
+            ant-build
+            ant-build-system))
+
+;; Commentary:
+;;
+;; Standard build procedure for Java packages using Ant.
+;;
+;; Code:
+
+(define %ant-build-system-modules
+  ;; Build-side modules imported by default.
+  `((guix build ant-build-system)
+    (guix build syscalls)
+    ,@%gnu-build-system-modules))
+
+(define (default-jdk)
+  "Return the default JDK package."
+  ;; Lazily resolve the binding to avoid a circular dependency.
+  (let ((jdk-mod (resolve-interface '(gnu packages java))))
+    (module-ref jdk-mod 'icedtea)))
+
+(define (default-ant)
+  "Return the default Ant package."
+  ;; Lazily resolve the binding to avoid a circular dependency.
+  (let ((jdk-mod (resolve-interface '(gnu packages java))))
+    (module-ref jdk-mod 'ant)))
+
+(define* (lower name
+                #:key source inputs native-inputs outputs system target
+                (jdk (default-jdk))
+                (ant (default-ant))
+                #:allow-other-keys
+                #:rest arguments)
+  "Return a bag for NAME."
+  (define private-keywords
+    '(#:source #:target #:jdk #:ant #:inputs #:native-inputs))
+
+  (and (not target)                               ;XXX: no cross-compilation
+       (bag
+         (name name)
+         (system system)
+         (host-inputs `(,@(if source
+                              `(("source" ,source))
+                              '())
+                        ,@inputs
+
+                        ;; Keep the standard inputs of 'gnu-build-system'.
+                        ,@(standard-packages)))
+         (build-inputs `(("jdk" ,jdk "jdk")
+                         ("ant" ,ant)
+                         ,@native-inputs))
+         (outputs outputs)
+         (build ant-build)
+         (arguments (strip-keyword-arguments private-keywords arguments)))))
+
+(define* (ant-build store name inputs
+                    #:key
+                    (tests? #t)
+                    (test-target "tests")
+                    (configure-flags ''())
+                    (make-flags ''())
+                    (build-target "jar")
+                    (jar-name #f)
+                    (phases '(@ (guix build ant-build-system)
+                                %standard-phases))
+                    (outputs '("out"))
+                    (search-paths '())
+                    (system (%current-system))
+                    (guile #f)p
+                    (imported-modules %ant-build-system-modules)
+                    (modules '((guix build ant-build-system)
+                               (guix build utils))))
+  "Build SOURCE with INPUTS."
+  (define builder
+    `(begin
+       (use-modules ,@modules)
+       (ant-build #:name ,name
+                  #:source ,(match (assoc-ref inputs "source")
+                              (((? derivation? source))
+                               (derivation->output-path source))
+                              ((source)
+                               source)
+                              (source
+                               source))
+                  #:make-flags ,make-flags
+                  #:configure-flags ,configure-flags
+                  #:system ,system
+                  #:tests? ,tests?
+                  #:test-target ,test-target
+                  #:build-target ,build-target
+                  #:jar-name ,jar-name
+                  #:phases ,phases
+                  #:outputs %outputs
+                  #:search-paths ',(map search-path-specification->sexp
+                                        search-paths)
+                  #:inputs %build-inputs)))
+
+  (define guile-for-build
+    (match guile
+      ((? package?)
+       (package-derivation store guile system #:graft? #f))
+      (#f                               ; the default
+       (let* ((distro (resolve-interface '(gnu packages commencement)))
+              (guile  (module-ref distro 'guile-final)))
+         (package-derivation store guile system #:graft? #f)))))
+
+  (build-expression->derivation store name builder
+                                #:inputs inputs
+                                #:system system
+                                #:modules imported-modules
+                                #:outputs outputs
+                                #:guile-for-build guile-for-build))
+
+(define ant-build-system
+  (build-system
+    (name 'ant)
+    (description "The standard Ant build system")
+    (lower lower)))
+
+;;; ant.scm ends here
diff --git a/guix/build/ant-build-system.scm b/guix/build/ant-build-system.scm
new file mode 100644
index 0000000000..d302b948b5
--- /dev/null
+++ b/guix/build/ant-build-system.scm
@@ -0,0 +1,160 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2016 Ricardo Wurmus <rekado@elephly.net>
+;;;
+;;; 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)
+  "Create a simple build.xml with standard targets for Ant."
+  (call-with-output-file "build.xml"
+    (lambda (port)
+      (sxml->xml
+       `(project (@ (basedir "."))
+                 (property (@ (name "classes.dir")
+                              (value "${basedir}/build/classes")))
+                 (property (@ (name "jar.dir")
+                              (value "${basedir}/build/jar")))
+                 (property (@ (name "dist.dir")
+                              (value ,prefix)))
+
+                 ;; respect the CLASSPATH environment variable
+                 (property (@ (name "build.sysclasspath")
+                              (value "first")))
+                 (property (@ (environment "env")))
+                 (path (@ (id "classpath"))
+                       (pathelement (@ (location "${env.CLASSPATH}"))))
+
+                 (target (@ (name "compile"))
+                         (mkdir (@ (dir "${classes.dir}")))
+                         (javac (@ (includeantruntime "false")
+                                   (srcdir "src")
+                                   (destdir "${classes.dir}")
+                                   (classpath (@ (refid "classpath"))))))
+
+                 (target (@ (name "jar")
+                            (depends "compile"))
+                         (mkdir (@ (dir "${jar.dir}")))
+                         ;; We cannot use the simpler "jar" task here, because
+                         ;; there is no way to disable generation of a
+                         ;; manifest.  We do not include a generated manifest
+                         ;; to ensure determinism, because we cannot easily
+                         ;; reset the ctime/mtime before creating the archive.
+                         (exec (@ (executable "jar"))
+                               (arg (@ (line ,(string-append "-Mcf ${jar.dir}/" jar-name
+                                                             " -C ${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* (configure #:key inputs outputs (jar-name #f)
+                    #:allow-other-keys)
+  (when jar-name
+    (default-build.xml jar-name
+                       (string-append (assoc-ref outputs "out")
+                                      "/share/java")))
+  (setenv "JAVA_HOME" (assoc-ref inputs "jdk"))
+  (setenv "CLASSPATH" (generate-classpath inputs)))
+
+(define* (build #:key (make-flags '()) (build-target "jar")
+                #:allow-other-keys)
+  (zero? (apply system* `("ant" ,build-target ,@make-flags))))
+
+(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")))
+      (and (with-directory-excursion dir
+             (zero? (system* "jar" "xf" jar)))
+           ;; The manifest file contains timestamps
+           (for-each delete-file (find-files dir "MANIFEST.MF"))
+           (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))
+           (unless (zero? (system* "jar" "-Mcf" jar "-C" dir "."))
+             (error "'jar' failed"))
+           (utime jar 0 0)
+           #t)))
+
+  (every (match-lambda
+           ((output . directory)
+            (every repack-archive (find-files directory "\\.jar$"))))
+         outputs))
+
+(define* (check #:key target (make-flags '()) (tests? (not target))
+                (test-target "check")
+                #:allow-other-keys)
+  (if tests?
+      (zero? (apply system* `("ant" ,test-target ,@make-flags)))
+      (begin
+        (format #t "test suite not run~%")
+        #t)))
+
+(define* (install #:key (make-flags '()) #:allow-other-keys)
+  (zero? (apply system* `("ant" "install" ,@make-flags))))
+
+(define %standard-phases
+  (modify-phases gnu:%standard-phases
+    (replace 'configure configure)
+    (replace 'build build)
+    (replace 'check check)
+    (replace 'install install)
+    (add-after 'install '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