Commit | Line | Data |
---|---|---|
5f7a1a4d | 1 | ;;; GNU Guix --- Functional package management for GNU |
d7ee90fe | 2 | ;;; Copyright © 2016, 2018 Ricardo Wurmus <rekado@elephly.net> |
5f7a1a4d RW |
3 | ;;; |
4 | ;;; This file is part of GNU Guix. | |
5 | ;;; | |
6 | ;;; GNU Guix is free software; you can redistribute it and/or modify it | |
7 | ;;; under the terms of the GNU General Public License as published by | |
8 | ;;; the Free Software Foundation; either version 3 of the License, or (at | |
9 | ;;; your option) any later version. | |
10 | ;;; | |
11 | ;;; GNU Guix is distributed in the hope that it will be useful, but | |
12 | ;;; WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | ;;; GNU General Public License for more details. | |
15 | ;;; | |
16 | ;;; You should have received a copy of the GNU General Public License | |
17 | ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. | |
18 | ||
19 | (define-module (guix build ant-build-system) | |
20 | #:use-module ((guix build gnu-build-system) #:prefix gnu:) | |
21 | #:use-module (guix build syscalls) | |
22 | #:use-module (guix build utils) | |
23 | #:use-module (sxml simple) | |
24 | #:use-module (ice-9 match) | |
25 | #:use-module (ice-9 ftw) | |
26 | #:use-module (srfi srfi-1) | |
27 | #:use-module (srfi srfi-26) | |
28 | #:export (%standard-phases | |
29 | ant-build)) | |
30 | ||
31 | ;; Commentary: | |
32 | ;; | |
33 | ;; Builder-side code of the standard build procedure for Java packages using | |
34 | ;; Ant. | |
35 | ;; | |
36 | ;; Code: | |
37 | ||
8df64f73 | 38 | (define* (default-build.xml jar-name prefix #:optional |
f403d7ab JL |
39 | (source-dir ".") (test-dir "./test") (main-class #f) |
40 | (test-include '("**/*Test.java")) | |
41 | (test-exclude '("**/Abstract*Test.java"))) | |
5f7a1a4d RW |
42 | "Create a simple build.xml with standard targets for Ant." |
43 | (call-with-output-file "build.xml" | |
44 | (lambda (port) | |
45 | (sxml->xml | |
46 | `(project (@ (basedir ".")) | |
47 | (property (@ (name "classes.dir") | |
48 | (value "${basedir}/build/classes"))) | |
8df1faa0 JL |
49 | (property (@ (name "manifest.dir") |
50 | (value "${basedir}/build/manifest"))) | |
51 | (property (@ (name "manifest.file") | |
52 | (value "${manifest.dir}/MANIFEST.MF"))) | |
5f7a1a4d RW |
53 | (property (@ (name "jar.dir") |
54 | (value "${basedir}/build/jar"))) | |
55 | (property (@ (name "dist.dir") | |
56 | (value ,prefix))) | |
52a791f5 RW |
57 | (property (@ (name "test.home") |
58 | (value ,test-dir))) | |
59 | (property (@ (name "test.classes.dir") | |
60 | (value "${basedir}/build/test-classes"))) | |
5f7a1a4d RW |
61 | |
62 | ;; respect the CLASSPATH environment variable | |
63 | (property (@ (name "build.sysclasspath") | |
64 | (value "first"))) | |
65 | (property (@ (environment "env"))) | |
66 | (path (@ (id "classpath")) | |
67 | (pathelement (@ (location "${env.CLASSPATH}")))) | |
68 | ||
8df1faa0 JL |
69 | (target (@ (name "manifest")) |
70 | (mkdir (@ (dir "${manifest.dir}"))) | |
90ea1006 GB |
71 | (manifest (@ (file "${manifest.file}")) |
72 | ,(if main-class | |
73 | `(attribute (@ (name "Main-Class") | |
74 | (value ,main-class))) | |
75 | ""))) | |
8df1faa0 | 76 | |
5f7a1a4d RW |
77 | (target (@ (name "compile")) |
78 | (mkdir (@ (dir "${classes.dir}"))) | |
79 | (javac (@ (includeantruntime "false") | |
8df64f73 | 80 | (srcdir ,source-dir) |
5f7a1a4d RW |
81 | (destdir "${classes.dir}") |
82 | (classpath (@ (refid "classpath")))))) | |
83 | ||
52a791f5 RW |
84 | (target (@ (name "compile-tests")) |
85 | (mkdir (@ (dir "${test.classes.dir}"))) | |
86 | (javac (@ (includeantruntime "false") | |
87 | (srcdir ,test-dir) | |
88 | (destdir "${test.classes.dir}")) | |
89 | (classpath | |
90 | (pathelement (@ (path "${env.CLASSPATH}"))) | |
91 | (pathelement (@ (location "${classes.dir}"))) | |
92 | (pathelement (@ (location "${test.classes.dir}")))))) | |
93 | ||
94 | (target (@ (name "check") | |
95 | (depends "compile-tests")) | |
96 | (mkdir (@ (dir "${test.home}/test-reports"))) | |
97 | (junit (@ (printsummary "true") | |
98 | (showoutput "true") | |
99 | (fork "yes") | |
100 | (haltonfailure "yes")) | |
101 | (classpath | |
102 | (pathelement (@ (path "${env.CLASSPATH}"))) | |
103 | (pathelement (@ (location "${test.home}/resources"))) | |
104 | (pathelement (@ (location "${classes.dir}"))) | |
105 | (pathelement (@ (location "${test.classes.dir}")))) | |
106 | (formatter (@ (type "plain") | |
107 | (usefile "true"))) | |
108 | (batchtest (@ (fork "yes") | |
109 | (todir "${test.home}/test-reports")) | |
110 | (fileset (@ (dir "${test.home}/java")) | |
f403d7ab JL |
111 | ,@(map (lambda (file) |
112 | `(include (@ (name ,file)))) | |
113 | test-include) | |
114 | ,@(map (lambda (file) | |
115 | `(exclude (@ (name ,file)))) | |
116 | test-exclude))))) | |
52a791f5 | 117 | |
5f7a1a4d | 118 | (target (@ (name "jar") |
8df1faa0 | 119 | (depends "compile, manifest")) |
5f7a1a4d | 120 | (mkdir (@ (dir "${jar.dir}"))) |
5f7a1a4d | 121 | (exec (@ (executable "jar")) |
8df1faa0 JL |
122 | (arg (@ (line ,(string-append "-cmf ${manifest.file} " |
123 | "${jar.dir}/" jar-name | |
5f7a1a4d RW |
124 | " -C ${classes.dir} .")))))) |
125 | ||
126 | (target (@ (name "install")) | |
127 | (copy (@ (todir "${dist.dir}")) | |
128 | (fileset (@ (dir "${jar.dir}")) | |
129 | (include (@ (name "**/*.jar"))))))) | |
130 | port))) | |
131 | (utime "build.xml" 0 0) | |
132 | #t) | |
133 | ||
134 | (define (generate-classpath inputs) | |
135 | "Return a colon-separated string of full paths to jar files found among the | |
136 | INPUTS." | |
137 | (string-join | |
138 | (apply append (map (match-lambda | |
139 | ((_ . dir) | |
abc4cb57 | 140 | (find-files dir "\\.jar$"))) |
5f7a1a4d RW |
141 | inputs)) ":")) |
142 | ||
932f2b70 RW |
143 | (define* (unpack #:key source #:allow-other-keys) |
144 | "Unpack the jar archive SOURCE. When SOURCE is not a jar archive fall back | |
145 | to the default GNU unpack strategy." | |
146 | (if (string-suffix? ".jar" source) | |
147 | (begin | |
148 | (mkdir "src") | |
149 | (with-directory-excursion "src" | |
d7ee90fe RW |
150 | (invoke "jar" "-xf" source)) |
151 | #t) | |
932f2b70 RW |
152 | ;; Use GNU unpack strategy for things that aren't jar archives. |
153 | ((assq-ref gnu:%standard-phases 'unpack) #:source source))) | |
154 | ||
5f7a1a4d | 155 | (define* (configure #:key inputs outputs (jar-name #f) |
52a791f5 | 156 | (source-dir "src") |
8df1faa0 | 157 | (test-dir "src/test") |
f403d7ab JL |
158 | (main-class #f) |
159 | (test-include '("**/*Test.java")) | |
160 | (test-exclude '("**/Abstract*.java")) #:allow-other-keys) | |
5f7a1a4d RW |
161 | (when jar-name |
162 | (default-build.xml jar-name | |
163 | (string-append (assoc-ref outputs "out") | |
8df64f73 | 164 | "/share/java") |
f403d7ab | 165 | source-dir test-dir main-class test-include test-exclude)) |
5f7a1a4d | 166 | (setenv "JAVA_HOME" (assoc-ref inputs "jdk")) |
d6ac4d42 DM |
167 | (setenv "CLASSPATH" (generate-classpath inputs)) |
168 | #t) | |
5f7a1a4d RW |
169 | |
170 | (define* (build #:key (make-flags '()) (build-target "jar") | |
171 | #:allow-other-keys) | |
d7ee90fe | 172 | (apply invoke `("ant" ,build-target ,@make-flags))) |
5f7a1a4d | 173 | |
bc65332a DM |
174 | (define* (generate-jar-indices #:key outputs #:allow-other-keys) |
175 | "Generate file \"META-INF/INDEX.LIST\". This file does not use word wraps | |
176 | and is preferred over \"META-INF/MAINFEST.MF\", which does use word wraps, | |
177 | by Java when resolving dependencies. So we make sure to create it so that | |
178 | grafting works - and so that the garbage collector doesn't collect | |
179 | dependencies of this jar file." | |
180 | (define (generate-index jar) | |
181 | (invoke "jar" "-i" jar)) | |
182 | (every (match-lambda | |
183 | ((output . directory) | |
184 | (every generate-index (find-files directory "\\.jar$")))) | |
185 | outputs)) | |
186 | ||
5f7a1a4d | 187 | (define* (strip-jar-timestamps #:key outputs |
9941e081 | 188 | #:allow-other-keys) |
5f7a1a4d RW |
189 | "Unpack all jar archives, reset the timestamp of all contained files, and |
190 | repack them. This is necessary to ensure that archives are reproducible." | |
191 | (define (repack-archive jar) | |
192 | (format #t "repacking ~a\n" jar) | |
9941e081 RW |
193 | (let* ((dir (mkdtemp! "jar-contents.XXXXXX")) |
194 | (manifest (string-append dir "/META-INF/MANIFEST.MF"))) | |
d7ee90fe RW |
195 | (with-directory-excursion dir |
196 | (invoke "jar" "xf" jar)) | |
197 | (delete-file jar) | |
198 | ;; XXX: copied from (gnu build install) | |
199 | (for-each (lambda (file) | |
200 | (let ((s (lstat file))) | |
201 | (unless (eq? (stat:type s) 'symlink) | |
202 | (utime file 0 0 0 0)))) | |
203 | (find-files dir #:directories? #t)) | |
9941e081 | 204 | |
d7ee90fe RW |
205 | ;; The jar tool will always set the timestamp on the manifest file |
206 | ;; and the containing directory to the current time, even when we | |
207 | ;; reuse an existing manifest file. To avoid this we use "zip" | |
208 | ;; instead of "jar". It is important that the manifest appears | |
209 | ;; first. | |
210 | (with-directory-excursion dir | |
211 | (let* ((files (find-files "." ".*" #:directories? #t)) | |
212 | ;; To ensure that the reference scanner can detect all | |
213 | ;; store references in the jars we disable compression | |
214 | ;; with the "-0" option. | |
215 | (command (if (file-exists? manifest) | |
216 | `("zip" "-0" "-X" ,jar ,manifest ,@files) | |
217 | `("zip" "-0" "-X" ,jar ,@files)))) | |
218 | (apply invoke command))) | |
219 | (utime jar 0 0) | |
220 | #t)) | |
5f7a1a4d | 221 | |
d7ee90fe RW |
222 | (for-each (match-lambda |
223 | ((output . directory) | |
224 | (for-each repack-archive (find-files directory "\\.jar$")))) | |
225 | outputs) | |
226 | #t) | |
5f7a1a4d RW |
227 | |
228 | (define* (check #:key target (make-flags '()) (tests? (not target)) | |
229 | (test-target "check") | |
230 | #:allow-other-keys) | |
231 | (if tests? | |
d7ee90fe RW |
232 | (apply invoke `("ant" ,test-target ,@make-flags)) |
233 | (format #t "test suite not run~%")) | |
234 | #t) | |
5f7a1a4d RW |
235 | |
236 | (define* (install #:key (make-flags '()) #:allow-other-keys) | |
d7ee90fe | 237 | (apply invoke `("ant" "install" ,@make-flags))) |
5f7a1a4d RW |
238 | |
239 | (define %standard-phases | |
240 | (modify-phases gnu:%standard-phases | |
932f2b70 | 241 | (replace 'unpack unpack) |
189be331 | 242 | (delete 'bootstrap) |
5f7a1a4d RW |
243 | (replace 'configure configure) |
244 | (replace 'build build) | |
245 | (replace 'check check) | |
246 | (replace 'install install) | |
bc65332a DM |
247 | (add-after 'install 'generate-jar-indices generate-jar-indices) |
248 | (add-after 'generate-jar-indices 'strip-jar-timestamps | |
249 | strip-jar-timestamps))) | |
5f7a1a4d RW |
250 | |
251 | (define* (ant-build #:key inputs (phases %standard-phases) | |
252 | #:allow-other-keys #:rest args) | |
253 | "Build the given Java package, applying all of PHASES in order." | |
254 | (apply gnu:gnu-build #:inputs inputs #:phases phases args)) | |
255 | ||
256 | ;;; ant-build-system.scm ends here |