source: trunk/abcl/build-abcl.lisp @ 11678

Last change on this file since 11678 was 11678, checked in by ehuelsmann, 14 years ago

Lisp builds store build-artifacts outside of the source tree (build/classes/) just like Ant.

build-abcl.lisp:

SUBSTITUTE-IN-STRING: replace multiple occurrences in 1 string.
MAKE-CLASSES: Create output directories before using them; pass '-d' argument to javac for

out-of-tree class file storage.

MAKE-JAR: Create output directories before using them.
DO-COMPILE-SYSTEM: Move platform specific bits into argument value calculation,

one main code path remaining. Pass OUTPUT-PATH argument for out-of-tree *.cls/*.abcl storage.
Create output directories before using them.

MAKE-LAUNCH-SCRIPT: Don't point to source directory in the classpath; there are no *.class files

anyway. Adjust for the fact that 'abcl.jar' is in dist/ now.

CLEAN: Use a list of directories and file patterns.

make-jar.bat.in, make-jar.in:

Adapt for the fact that build artifacts are now in build/classes/.

  • Property svn:eol-style set to native
File size: 24.6 KB
Line 
1;;; build-abcl.lisp
2
3#+abcl
4(require 'format)
5
6(defpackage build-abcl
7  (:use "COMMON-LISP")
8  (:export #:build-abcl #:make-dist)
9  #+abcl (:import-from #:extensions #:run-shell-command #:probe-directory)
10  #+allegro (:import-from #:excl #:probe-directory)
11  #+clisp (:import-from #:ext #:probe-directory)
12  )
13
14(in-package #:build-abcl)
15
16(defun safe-namestring (pathname)
17  (let ((string (namestring pathname)))
18    (when (position #\space string)
19      (setf string (concatenate 'string "\"" string "\"")))
20    string))
21
22
23
24;; Platform detection.
25
26(defun platform ()
27  #-clisp
28  (let ((software-type (software-type)))
29    (cond ((search "Linux" software-type)
30           :linux)
31          ((or (search "Mac OS X" software-type) ; abcl
32               (search "Darwin" software-type))  ; sbcl
33           :darwin)
34          ((search "Windows" software-type)
35           :windows)
36          (t
37           :unknown)))
38  #+clisp
39  (cond ((member :win32 *features*)
40         :windows)
41        ((zerop (ext:run-shell-command "uname | grep -i darwin" :output nil))
42         :darwin)
43        ((zerop (ext:run-shell-command "uname | grep -i linux" :output nil))
44         :linux)
45        (t
46         :unknown)))
47
48(defparameter *platform* (platform))
49
50(defparameter *file-separator-char*
51  (if (eq *platform* :windows) #\\ #\/))
52
53(defparameter *path-separator-char*
54  (if (eq *platform* :windows) #\; #\:))
55
56
57#+sbcl
58(defun run-shell-command (command &key directory (output *standard-output*))
59  (when directory
60    (setf command (concatenate 'string
61                               "\\cd \""
62                               (namestring (pathname directory))
63                               "\" && "
64                               command)))
65  (sb-ext:process-exit-code
66   (sb-ext:run-program
67    "/bin/sh"
68    (list  "-c" command)
69    :input nil :output output)))
70
71#+cmu
72(defun run-shell-command (command &key directory (output *standard-output*))
73  (when directory
74    (setf command (concatenate 'string
75                               "\\cd \""
76                               (namestring (pathname directory))
77                               "\" && "
78                               command)))
79  (ext::process-exit-code
80   (ext:run-program
81    "/bin/sh"
82    (list  "-c" command)
83    :input nil :output output)))
84
85#+clisp
86(defun run-shell-command (command &key directory (output *standard-output*))
87  (declare (ignore output)) ;; FIXME
88  (let (status old-directory)
89    (when directory
90      (setf old-directory (ext:cd))
91      (ext:cd directory))
92    (unwind-protect
93        (setf status (ext:shell command))
94      (when old-directory
95        (ext:cd old-directory)))
96    (cond ((numberp status)
97           status)
98          ((or (eq status t) (null status)) ;; clisp 2.47 returns NIL on success
99           0)
100          (t
101           -1))))
102
103#+lispworks
104(defun run-shell-command (command &key directory (output *standard-output*))
105  (when directory
106    (unless (eq *platform* :windows)
107      (setf command (concatenate 'string
108                                 "\\cd \""
109                                 (namestring (pathname directory))
110                                 "\" && "
111                                 command))))
112  (system:call-system-showing-output command
113                                     :shell-type "/bin/sh"
114                                     :output-stream output))
115
116#+allegro
117(defun run-shell-command (command &key directory (output *standard-output*))
118  (excl:run-shell-command command
119                          :directory directory
120                          :input nil
121                          :output #+ide nil #-ide output))
122
123#+openmcl
124(defun run-shell-command (command &key directory (output *standard-output*))
125  (when directory
126    (setf command (concatenate 'string
127                               "\\cd \""
128                               (namestring (pathname directory))
129                               "\" && "
130                               command)))
131  (multiple-value-bind (status exitcode)
132      (ccl:external-process-status
133       (ccl:run-program
134        "/bin/sh"
135        (list  "-c" command)
136        :wait t :input nil :output output))
137    (declare (ignore status))
138    exitcode))
139
140#+(or sbcl cmu lispworks openmcl)
141(defun probe-directory (pathspec)
142  (let* ((truename (probe-file pathspec)) ; TRUENAME is a pathname.
143         (namestring (and truename (namestring truename)))) ; NAMESTRING is a string.
144    (and namestring
145         (> (length namestring) 0)
146         (eql (char namestring (1- (length namestring))) *file-separator-char*)
147         truename)))
148
149(defparameter *build-root*
150  (make-pathname :device (pathname-device *load-truename*)
151                 :directory (pathname-directory *load-truename*)))
152
153(defparameter *customizations-file*
154  (merge-pathnames "customizations.lisp" *build-root*))
155
156(defparameter *abcl-dir*
157  (merge-pathnames "src/org/armedbear/lisp/" *build-root*))
158
159(defparameter *jdk*           nil)
160(defparameter *java-compiler* nil)
161(defparameter *javac-options* nil)
162(defparameter *jikes-options* nil)
163(defparameter *jar*           nil)
164
165(defvar *classpath*)
166(defvar *java*)
167(defvar *java-compiler-options*)
168(defvar *java-compiler-command-line-prefix*)
169
170(defun initialize-build ()
171  (setf *jdk*           nil
172        *java-compiler* nil
173        *javac-options* nil
174        *jikes-options* nil
175        *jar*           nil)
176  (load *customizations-file*)
177  (setf *java* (probe-file (merge-pathnames (if (eq *platform* :windows)
178                                                "bin\\java.exe"
179                                                "bin/java")
180                                            *jdk*)))
181  (unless *java*
182    (error "Can't find Java executable."))
183  (unless *java-compiler*
184    (setf *java-compiler* (merge-pathnames (if (eq *platform* :windows)
185                                               "bin/javac.exe"
186                                               "bin/javac")
187                                           *jdk*)))
188  (unless *jar*
189    (setf *jar* (merge-pathnames (if (eq *platform* :windows)
190                                     "bin/jar.exe"
191                                     "bin/jar")
192                                 *jdk*)))
193  (let ((classpath-components (list (merge-pathnames "src" *build-root*)
194                                    (if (eq *platform* :darwin)
195                                        #p"/System/Library/Frameworks/JavaVM.framework/Classes/classes.jar"
196                                        (merge-pathnames "jre/lib/rt.jar" *jdk*)))))
197    (setf *classpath*
198          (with-output-to-string (s)
199            (do* ((components classpath-components (cdr components))
200                  (component (car components) (car components)))
201                 ((null components))
202              (princ (safe-namestring component) s)
203              (unless (null (cdr components))
204                (write-char *path-separator-char* s))))))
205  (let ((prefix (concatenate 'string
206                             (safe-namestring *java-compiler*)
207                             " -classpath " *classpath*)))
208    (setf *java-compiler-options*
209          (if (string-equal (pathname-name (pathname *java-compiler*)) "jikes")
210              *jikes-options*
211              *javac-options*))
212    (setf prefix
213          (if *java-compiler-options*
214              (concatenate 'string prefix " " *java-compiler-options* " ")
215              (concatenate 'string prefix " ")))
216    (setf *java-compiler-command-line-prefix* prefix)))
217
218(defun substitute-in-string (string substitutions-alist)
219  (dolist (entry substitutions-alist)
220    (loop named replace
221         for index = (search (car entry) string :test #'string=)
222         do
223         (unless index
224           (return-from replace))
225         (setf string (concatenate 'string
226                                   (subseq string 0 index)
227                                   (cdr entry)
228                                   (subseq string (+ index (length (car entry))))))))
229  string)
230
231(defun copy-with-substitutions (source-file target-file substitutions-alist)
232  (with-open-file (in source-file :direction :input)
233    (with-open-file (out target-file :direction :output :if-exists :supersede)
234      (loop
235        (let ((string (read-line in nil)))
236          (when (null string)
237            (return))
238          (write-line (substitute-in-string string substitutions-alist) out))))))
239
240(defun build-javac-command-line (source-file)
241  (concatenate 'string
242               *java-compiler-command-line-prefix*
243               (namestring source-file)))
244
245(defun java-compile-file (source-file)
246  (let ((cmdline (build-javac-command-line source-file)))
247    (zerop (run-shell-command cmdline :directory *abcl-dir*))))
248
249(defun make-classes (force batch)
250  (let* ((source-files
251          (remove-if-not #'(lambda (name)
252                             (let ((output-name
253                                    (make-pathname :type "class"
254                                                     :defaults name)))
255                               (or force
256                                   (null (probe-file output-name))
257                                   (>= (file-write-date name)
258                                       (file-write-date output-name)))))
259                         (mapcan #'(lambda (default)
260                                     (directory (merge-pathnames "*.java"
261                                                                 default)))
262                                 (list *abcl-dir*
263                                       (merge-pathnames "util/" *abcl-dir*))))))
264    (format t "~&JDK: ~A~%" *jdk*)
265    (format t "Java compiler: ~A~%" *java-compiler*)
266    (format t "Compiler options: ~A~%~%" (if *java-compiler-options* *java-compiler-options* ""))
267    (finish-output)
268    (cond ((null source-files)
269           (format t "Classes are up to date.~%")
270           (finish-output)
271           t)
272          (t
273           (cond (batch
274                  (ensure-directories-exist (merge-pathnames "build/classes/" *build-root*))
275                  (let* ((dir (pathname-directory *abcl-dir*))
276                         (cmdline (with-output-to-string (s)
277                                    (princ *java-compiler-command-line-prefix* s)
278                                    (princ " -d " s)
279                                    (princ (merge-pathnames "build/classes/"
280                                                            *build-root*) s)
281                                    (princ #\Space s)
282                                    (dolist (source-file source-files)
283                                      (princ
284                                       (if (equal (pathname-directory source-file) dir)
285                                           (file-namestring source-file)
286                                           (namestring source-file))
287                                       s)
288                                      (princ #\space s))))
289                         (status (run-shell-command cmdline :directory *abcl-dir*)))
290                    (zerop status)))
291                 (t
292                  (ensure-directories-exist (merge-pathnames "build/classes/" *build-root*))
293                  (dolist (source-file source-files t)
294                    (unless (java-compile-file source-file)
295                      (format t "Build failed.~%")
296                      (return nil)))))))))
297
298(defun make-jar ()
299  (let ((*default-pathname-defaults* *build-root*)
300        (jar-namestring (namestring *jar*)))
301    (when (position #\space jar-namestring)
302      (setf jar-namestring (concatenate 'string "\"" jar-namestring "\"")))
303    (let ((substitutions-alist (acons "@JAR@" jar-namestring nil))
304          (source-file (if (eq *platform* :windows) "make-jar.bat.in" "make-jar.in"))
305          (target-file (if (eq *platform* :windows) "make-jar.bat"    "make-jar"))
306          (command     (if (eq *platform* :windows) "make-jar.bat"    "sh make-jar")))
307      (copy-with-substitutions source-file target-file substitutions-alist)
308      (ensure-directories-exist (merge-pathnames "dist/" *build-root*))
309      (let ((status (run-shell-command command :directory *build-root*)))
310        (unless (zerop status)
311          (format t "~A returned ~S~%" command status))
312        status))))
313
314(defun do-compile-system (&key (zip t))
315  (terpri)
316  (finish-output)
317  (let* ((java-namestring (safe-namestring *java*))
318         status
319         (abcl-home (substitute-in-string
320                     (namestring *abcl-dir*)
321                     (when (eq *platform* :windows)
322                       '(("\\" . "/")
323                         ("/" . "\\\\")))))
324         (output-path (substitute-in-string
325                       (namestring
326                        (merge-pathnames "build/classes/org/armedbear/lisp/"
327                                         *build-root*))
328                       (when (eq *platform* :windows)
329                         '(("\\" . "/")))))
330         (cmdline (format nil
331                          "~A -cp build\\classes -Dabcl.home=\"~A\" ~
332org.armedbear.lisp.Main --noinit ~
333--eval \"(compile-system :zip ~A :quit t :output-path \\\"~A\\\")\"~%"
334                          java-namestring
335                          abcl-home
336                          (not (not zip)) ;; because that ensures T or NIL
337                          output-path)))
338    (ensure-directories-exist output-path)
339    (setf status
340          (run-shell-command cmdline
341                             :directory *build-root*))
342    status))
343
344(defun make-libabcl ()
345  (and (let* ((javah-namestring (namestring (probe-file (merge-pathnames "bin/javah" *jdk*))))
346              (command
347               (format nil "~A -o org/armedbear/lisp/native.h org.armedbear.lisp.Native"
348                       javah-namestring))
349              (status
350               (run-shell-command command :directory (merge-pathnames "src/" *build-root*))))
351         (unless (zerop status)
352           (format t "~A returned ~S~%" command status))
353         (zerop status))
354       (let* ((jdk-namestring (namestring *jdk*))
355              (command
356               (format nil "gcc -shared -o libabcl.so -O -D_REENTRANT -fpic -I~Ainclude -I~Ainclude/~A native.c"
357                       jdk-namestring jdk-namestring
358                       (cond ((eq *platform* :linux)
359                              "linux")
360                             ((search "SunOS" (software-type))
361                              "solaris")
362                             ((search "FreeBSD" (software-type))
363                              "freebsd"))))
364              (status
365               (run-shell-command command :directory *abcl-dir*)))
366         (unless (zerop status)
367           (format t "~A returned ~S~%" command status))
368         (zerop status))))
369
370;; abcl/abcl.bat
371(defun make-launch-script ()
372  ;; Use the -Xss4M and -Xmx256M flags so that the default launch script can be
373  ;; used to build sbcl.
374  (cond ((eq *platform* :windows)
375         (with-open-file (s
376                          (merge-pathnames "abcl.bat" *build-root*)
377                          :direction :output
378                          :if-exists :supersede)
379           (format s "~A -Xss4M -Xmx256M -cp \"~A\" org.armedbear.lisp.Main %1 %2 %3 %4 %5 %6 %7 %8 %9~%"
380                   (safe-namestring *java*)
381                   (namestring (merge-pathnames "dist\\abcl.jar" *build-root*)))))
382        (t
383         (let ((pathname (merge-pathnames "abcl" *build-root*)))
384           (with-open-file (s pathname :direction :output :if-exists :supersede)
385             (if (eq *platform* :linux)
386                 ;; On Linux, set java.library.path for libabcl.so.
387                 (format s "#!/bin/sh~%exec ~A -Xss4M -Xmx256M -Xrs -Djava.library.path=~A -cp ~A:~A org.armedbear.lisp.Main \"$@\"~%"
388                         (safe-namestring *java*)
389                         (safe-namestring *abcl-dir*)
390                         (safe-namestring (merge-pathnames "src" *build-root*))
391                         (safe-namestring (merge-pathnames "abcl.jar" *build-root*)))
392                 ;; Not Linux.
393                 (format s "#!/bin/sh~%exec ~A -Xss4M -Xmx256M -cp ~A:~A org.armedbear.lisp.Main \"$@\"~%"
394                         (safe-namestring *java*)
395                         (safe-namestring (merge-pathnames "src" *build-root*))
396                         (safe-namestring (merge-pathnames "abcl.jar" *build-root*)))))
397           (run-shell-command (format nil "chmod +x ~A" (safe-namestring pathname))
398                              :directory *build-root*)))))
399
400(defun build-stamp ()
401  (multiple-value-bind
402      (second minute hour date month year day daylight-p zone)
403      (decode-universal-time (get-universal-time))
404    (declare (ignore daylight-p))
405    (setf day (nth day '("Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun")))
406    (setf month (nth (1- month) '("Jan" "Feb" "Mar" "Apr" "May" "Jun"
407                                  "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")))
408    (setf zone (* zone 100)) ;; FIXME
409    (format nil "~A ~A ~D ~D ~2,'0D:~2,'0D:~2,'0D -~4,'0D"
410            day month date year hour minute second zone)))
411
412(defun make-build-stamp ()
413  (with-open-file (s
414                   (merge-pathnames (make-pathname :name "build"
415                                                   :defaults *abcl-dir*))
416                   :direction :output
417                   :if-exists :supersede)
418    (format s "~A" (build-stamp))))
419
420(defun delete-files (pathnames)
421  (dolist (pathname pathnames)
422    (let ((truename (probe-file pathname)))
423      (when truename
424        (delete-file truename)))))
425
426(defun clean ()
427  (dolist (f (list (list *build-root* "abcl.jar" "abcl.bat" "make-jar.bat"
428                                      "compile-system.bat")
429                   (list *abcl-dir* "*.class" "*.abcl" "*.cls"
430                                    "native.h" "libabcl.so" "build")
431                   (list (merge-pathnames "build/classes/org/armedbear/lisp/"
432                                          *build-root*)
433                                    "*.class" "*.abcl" "*.cls"
434                                    "native.h" "libabcl.so" "build")
435                   (list (merge-pathnames
436                          "build/classes/org/armedbear/lisp/util/"
437                          *build-root*)
438                                    "*.class" "*.abcl" "*.cls")
439                   (list (merge-pathnames "dist/" *build-root*)
440                                    "*.jar" "*.class" "*.abcl" "*.cls")
441                  (list (merge-pathnames "java/awt/" *abcl-dir*)
442                         "*.class")))
443    (let ((default (car f)))
444      (when (probe-directory default)
445        (delete-files (mapcan #'(lambda (name)
446                                  (directory (merge-pathnames name default)))
447                              (cdr f)))))))
448
449(defun build-abcl (&key force
450                        (batch t)
451                        compile-system
452                        jar
453                        clean
454                        libabcl
455                        full)
456  (let ((start (get-internal-real-time)))
457
458    #+lispworks
459    (when (eq *platform* :windows)
460      (setf batch nil))
461
462    (initialize-build)
463    (format t "~&Platform: ~A~%"
464            (case *platform*
465              (:windows "Windows")
466              (:linux   "Linux")
467              (:darwin  "Mac OS X")
468              (t        (software-type))))
469    (finish-output)
470    ;; clean
471    (when clean
472      (clean))
473    ;; classes
474    (unless (make-classes force batch)
475      (format t "Build failed.~%")
476      (return-from build-abcl nil))
477    ;; COMPILE-SYSTEM
478    (when (or full compile-system)
479      (let* ((zip    (if (or full jar) nil t))
480             (status (do-compile-system :zip zip)))
481        (unless (zerop status)
482          (format t "Build failed.~%")
483          (return-from build-abcl nil))))
484    ;; abcl.jar
485    (when (or full jar)
486      (let ((status (make-jar)))
487        (unless (zerop status)
488          (format t "Build failed.~%")
489          (return-from build-abcl nil))))
490    ;; libabcl.so
491    (when (and (or full libabcl)
492               (or (eq *platform* :linux)
493                   (search "SunOS" (software-type))
494                   (search "FreeBSD" (software-type))))
495      ;; A failure here is not fatal.
496      (make-libabcl))
497    ;; abcl/abcl.bat
498    (make-launch-script)
499    (make-build-stamp)
500    (let ((end (get-internal-real-time)))
501      (format t "Build completed successfully in ~A seconds.~%"
502              (/ (float (- end start)) internal-time-units-per-second)))
503    t))
504
505(defun build-abcl-executable ()
506  (let* ((*default-pathname-defaults* *abcl-dir*)
507         (source-files (directory "*.java"))
508         (cmdline (with-output-to-string (s)
509                    (princ "gcj -g -O0 " s)
510                    (dolist (source-file source-files)
511                      (unless (string= (pathname-name source-file) "Native")
512                        (princ (pathname-name source-file) s)
513                        (princ ".java" s)
514                        (princ #\space s)))
515                    (princ "--main=org.armedbear.lisp.Main -o lisp" s)))
516         (result (run-shell-command cmdline :directory *abcl-dir*)))
517    (zerop result)))
518
519(defvar *copy-verbose* nil)
520
521(defun copy-file (source target)
522  (when *copy-verbose*
523    (format t "~A -> ~A~%" source target))
524  (let ((buffer (make-array 4096 :element-type '(unsigned-byte 8))))
525    (with-open-file (in source :direction :input :element-type '(unsigned-byte 8))
526      (with-open-file (out target :direction :output :element-type '(unsigned-byte 8)
527                           :if-exists :supersede)
528        (loop
529          (let ((end (read-sequence buffer in)))
530            (when (zerop end)
531              (return))
532            (write-sequence buffer out :end end)))))))
533
534(defun copy-files (files source-dir target-dir)
535  (ensure-directories-exist target-dir)
536  (dolist (file files)
537    (copy-file (merge-pathnames file source-dir)
538               (merge-pathnames file target-dir))))
539
540(defun make-dist-dir (version-string)
541  (unless (eq *platform* :linux)
542    (error "MAKE-DIST is only supported on Linux."))
543  (let ((target-root (pathname (concatenate 'string "/var/tmp/" version-string "/"))))
544    (when (probe-directory target-root)
545      (error "Target directory ~S already exists." target-root))
546    (let* ((source-dir *build-root*)
547           (target-dir target-root)
548           (files (list "README"
549                        "COPYING"
550                        "build-abcl.lisp"
551                        "customizations.lisp"
552                        "make-jar.bat.in"
553                        "make-jar.in")))
554      (copy-files files source-dir target-dir))
555    (let* ((source-dir (merge-pathnames "examples/" *build-root*))
556           (target-dir (merge-pathnames "examples/" target-root))
557           (files '("hello.java")))
558      (copy-files files source-dir target-dir))
559    (let* ((source-dir (merge-pathnames "src/" *build-root*))
560           (target-dir (merge-pathnames "src/" target-root))
561           (files '("manifest-abcl")))
562      (copy-files files source-dir target-dir))
563    (let* ((source-dir *abcl-dir*)
564           (target-dir (merge-pathnames "src/org/armedbear/lisp/" target-root))
565           (*default-pathname-defaults* source-dir)
566           (files (mapcar #'file-namestring (append (directory "*.java")
567                                                    (directory "*.lisp")
568                                                    (list "LICENSE" "native.c")))))
569      (copy-files files source-dir target-dir))
570    (let* ((source-dir (merge-pathnames "tests/" *abcl-dir*))
571           (target-dir (merge-pathnames "src/org/armedbear/lisp/tests/" target-root))
572           (*default-pathname-defaults* source-dir)
573           (files (append (mapcar #'file-namestring (directory "*.lisp"))
574                          (list "jl-config.cl"))))
575      (copy-files files source-dir target-dir))
576    (let* ((source-dir (merge-pathnames "java/awt/" *abcl-dir*))
577           (target-dir (merge-pathnames "src/org/armedbear/lisp/java/awt/" target-root))
578           (*default-pathname-defaults* source-dir)
579           (files (mapcar #'file-namestring (directory "*.java"))))
580      (copy-files files source-dir target-dir))
581    target-root))
582
583(defun make-dist (version-string)
584  (let* ((dist-dir (make-dist-dir version-string))
585         (parent-dir (merge-pathnames (make-pathname :directory '(:relative :back))
586                                      dist-dir)))
587    (let* ((command (format nil "tar czf ~A~A.tar.gz ~A"
588                            (namestring parent-dir)
589                            version-string version-string))
590           (status (run-shell-command command :directory parent-dir)))
591      (unless (zerop status)
592        (format t "~A returned ~S~%" command status)))
593    (let* ((command (format nil "zip -q -r ~A~A.zip ~A"
594                            (namestring parent-dir)
595                            version-string version-string))
596           (status (run-shell-command command :directory parent-dir)))
597      (unless (zerop status)
598        (format t "~A returned ~S~%" command status)))))
Note: See TracBrowser for help on using the repository browser.