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