source: tags/0.00.6/j/build-abcl.lisp

Last change on this file was 9409, checked in by piso, 19 years ago

Work in progress.

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