1 ;;; -*- Mode: Lisp; Package: MAKE; Syntax: Common-Lisp; Base: 10 -*- ;;;;
2 (in-package :make
:use
'(:common-lisp
))
4 (export '(make system-load system-compile
))
6 ;;; ******* Description of Make Facility ************
7 ;; We provide a simple MAKE facility to allow
8 ;;compiling and loading of a tree of files
9 ;;If the tree is '(a b (d e g h) i)
10 ;; a will be loaded before b is compiled,
11 ;; b will be loaded before d, e, g, h are compiled
12 ;; d e g h will be loaded before i is compiled.
14 ;; A record is kept of write dates of loaded compiled files, and a file
15 ;;won't be reloaded if it is the same version (unless a force flag is t).
17 ;;Thus if you do (make :uinfor) twice in a row, the second one would not
18 ;;load anything. NOTE: If you change a, and a macro in it would affect
19 ;;b, b still will not be recompiled. You must choose the :recompile t
20 ;;option, to force the recompiling if you change macro files.
21 ;;Alternately you may specify dependency information (see :depends below).
24 ;;****** Sample file which when loaded causes system ALGEBRA
25 ;; to be compiled and loaded ******
28 ;;(use-package "MAKE")
29 ;;(setf (get :algebra :make) '(a b (d e) l))
30 ;;(setf (get :algebra :source-path) "/usr2/wfs/algebra/foo.lisp")
31 ;;(setf (get :algebra :object-path) "/usr2/wfs/algebra/o/foo.o")
32 ;;(make :algebra :compile t)
34 ;; More complex systems may need to do some special operations
35 ;;at certain points of the make.
36 ;;the tree of files may contain some keywords which have special meaning.
37 ;;eg. '(a b (:progn (gbc) (if make::*compile*
38 ;; (format t "A and B finally compiled")))
42 ;;then during the load and compile phases the function (gbc) will be
43 ;;called after a and b have been acted on, and during the compile phase
44 ;;the message about "A and B finally.." will be printed.
45 ;;the lisp files h and i will be loaded after merging the paths with
46 ;;the source directory. This feature is extensible: see the definitions
47 ;;of :load-source and :progn.
49 ;; The keyword feature is extensible, and you may specify what
50 ;;happens during the load or compile phase for your favorite keyword.
51 ;;To do this look at the definition of :progn, and :load-source
52 ;;in the source for make.
57 ;; This make NEVER loads or compiles files in an order different from
58 ;;that specified by the tree. It will omit loading files which are
59 ;;loaded and up to date, but if two files are out of date, the first (in
60 ;;the printed representation of the tree), will always be loaded before
61 ;;the second. A consequence of this is that circular dependencies can
64 ;; If the :make tree contains (a b c d (:depends (c d) (a b))) then c
65 ;;and d depend on a and b, so that if a or b need recompilation then c
66 ;;and d will also be recompiled. Thus the general form of a :depends
67 ;;clause is (:depends later earlier) where LATER and EARLIER are either
68 ;;a single file or a list of files. Read it as LATER depends on EARLIER.
69 ;;A declaration of a (:depends (c) (d)) would have no effect, since the
70 ;;order in the tree already rules out such a dependence.
72 ;; An easy way of specifying a linear dependence is by using :serial.
73 ;;The tree (a (:serial b c d) e) is completely equivalent to the tree
74 ;;(a b c d e (:depends c b)(:depends d (b c))), but with a long list of
75 ;;serial files, it is inconvenient to specify them in the
76 ;;latter representation.
78 ;;A common case is a set of macros whose dependence is serial followed by a set
79 ;;of files whose order is unimportant. A convenient way of building that
82 ;;(let ((macros '(a b c d))
83 ;; (files '(c d e f g)))
84 ;; `((:serial ,@ macros)
86 ;; (:depends ,files ,macros)))
88 ;; The depends clause may occur anywhere within the tree, since
89 ;;an initial pass collects all dependency information.
91 ;; Make takes a SHOW keyword argument. It is almost impossible to simulate
92 ;;all the possible features of make, for show. Nonetheless, it is good
93 ;;to get an idea of the compiling and loading sequence for a new system.
94 ;;As a byproduct, you could use the output, as a simple sequence of calls
95 ;;to compile-file and load, to do the required work, when make is not around
99 ;;***** Definitions ********
100 (defvar *files-loaded
* nil
)
101 (defvar *show-files-loaded
* nil
) ;only for show option
102 (defvar *load
* nil
"Will be non nil inside load-files")
103 (defvar *compile
* nil
"Bound by compile-files to t")
104 (defvar *depends
* nil
)
105 (defvar *depends-new
* nil
)
107 (defvar *when-compile
* nil
"Each compile-file evals things in this list and sets it to nil")
108 #+kcl
(defvar *system-p
* nil
)
109 (defvar *compile-file-function
* 'make-compile-file
)
110 (defvar *load-function
* 'make-load-file
)
112 (defvar *cflags
* #-kcl nil
113 #+kcl
'(:system-p
*system-p
*))
116 ;;this is the main entry point
118 (defun make (system &key recompile compile batch object-path source-path
120 &aux files
*depends
* *when-compile
*
123 "SYSTEM is a tree of files, or a symbol with :make property. It
124 loads all file files in system. If COMPILE it will try to compile
125 files with newer source versions than object versions, before loading.
126 If RECOMPILE it will recompile all files. This is equivalent to deleting all
127 objects and using :compile t. SOURCE-PATH is merged with the name given
128 in the files list, when looking for a file to compile. OBJECT-PATH is
129 merged with the name in the files list, when looking for a file to
130 load. If SYSTEM is a symbol, then a null OBJECT-PATH would be set to
131 the :object-path property of SYSTEM. Similarly for :source-path"
133 (declare (special object-path source-path show
)) batch
134 (cond ((symbolp system
)
135 (or object-path
(setf object-path
(get system
:object-path
)))
136 (or source-path
(setf source-path
(get system
:source-path
)))
137 (setf files
(get system
:make
))
139 (if (get system
:files
)
140 (error "Use :make property, :files property is obssolet{!")))
142 (t (setf files system
)))
143 (let ((*depends
* (if (or compile recompile
) (get-depends system
)))
146 (when (or compile recompile
)
147 (compile-files v recompile
))
148 (load-files v recompile
))))
150 (defun system-load (system-name &rest names
)
151 "If :infor is a system, (system-load :uinfor joe betty) will load
152 joe and betty from the object-path for :uinfor"
153 (load-files names t
(get system-name
:object-path
)))
155 (defun system-compile (system-name &rest names
)
157 "If :iunfor is a system, (system-compile :uinfor joe) will in the
158 source path for joe and compile him into the object path for :uinfor"
159 (compile-files names t
:source-path
160 (get system-name
:source-path
) :object-path
161 (get system-name
:object-path
)))
163 (defun get-depends (system-name &aux result
)
164 (dolist (v (get system-name
:make
))
166 ((eq (car v
) :serial
)
167 (do ((w (reverse (cdr v
))(cdr w
)))
169 (push (list (car w
) (cdr w
)) result
)))
170 ((eq (car v
) :depends
)
171 (push (cdr v
) result
))))
175 (setq si
::*default-time-zone
* 6)
177 (defun print-date (&optional
(stream *standard-output
*)
178 (time (get-universal-time)))
179 (multiple-value-bind (sec min hr day mon yr wkday
)
180 (decode-universal-time time
)
181 (format stream
"~a ~a ~a ~d:~2,'0d:~2,'0d ~a"
182 (nth wkday
'( "Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun"))
183 (nth (1- mon
) '("Jan" "Feb" "Mar" "Apr" "May" "Jun"
184 "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"))
188 ;;This is an awfully roundabout downcase, but some machines
189 ;;like symbolics swap cases on the pathname, so we have to do an extra
191 (defun lowcase (na &aux
(*print-case
* :downcase
))
192 (pathname-name (pathname (format nil
"~a" na
))))
194 (defun our-merge (name path
&optional ign
) ign
195 (make-pathname :name
(string name
)
196 :type
(pathname-type path
)
197 :version
(pathname-version path
)
198 :host
(pathname-host path
)
199 :directory
(pathname-directory path
)))
203 (setf (get :link
'load
)
204 #'(lambda (path to-link
)
205 (declare (special object-path
))
206 (si::faslink
(our-merge (lowcase path
) object-path
)
209 (setf (get :link
'compile
)
210 #'(lambda (path to-link
)
212 (compile-files path
*force
*)))
214 (setf (get :progn
'load
)
215 #'(lambda (&rest args
)
216 (eval (cons 'progn args
))))
218 (setf (get :progn
'compile
) (get :progn
'load
))
220 (setf (get :load-source
'load
)
221 #'(lambda (&rest args
)
222 (declare (special source-path
))
223 (load-files args
*force
* source-path
)))
225 (setf (get :load-source-when-compile
'compile
)
226 (get :load-source
'load
))
228 ;;should not use :lisp anymore
229 (setf (get :lisp
'load
)
230 #'(lambda (x) (error "please replace :lisp by :load-source")))
232 (setf (get :serial
'load
) #'(lambda (&rest l
)(load-files l
)))
233 (setf (get :serial
'compile
)
240 (defun load-files (files &optional
(*force
* *force
*) (object-path object-path
)
241 &aux path tem
(*load
* t
))
242 (declare (special object-path source-path
*force
* show
))
244 (setq path
(object files
))
246 (unless (member path
*show-files-loaded
* :test
'equalp
)
247 (push path
*show-files-loaded
*)
248 (format t
"~%(LOAD ~s)" (namestring path
))))
249 ((null *load-function
*))
252 (member path
*files-loaded
*
253 :test
'equalp
:key
'car
)))
254 (> (file-write-date path
) (cdr (car tem
)))))
255 (funcall *load-function
* files
)
256 (push (cons path
(file-write-date path
)) *files-loaded
*))))
257 ((keywordp (car files
))
258 (let ((fun (get (car files
) 'load
)))
259 (cond (fun (apply fun
(cdr files
))))))
260 (t (dolist (v files
) (load-files v
*force
* object-path
)))))
263 (defun file-date (file)
264 (if (probe-file file
) (or (file-write-date file
) 0) 0))
267 (declare (special source-path
))
268 (our-merge (lowcase file
) source-path
))
271 (declare (special object-path
))
272 (our-merge (lowcase file
) object-path
))
275 ;;for lisp machines, and others where checking date is slow, this
276 ;;we should try to cache some dates, and then remove them as we do
277 ;;things like compile files...
279 (defun file-out-dated (file)
280 (let ((obj-date (file-date (object file
))))
281 (or (<= obj-date
(file-date (source file
)))
282 (dolist (v *depends
*)
283 (cond ((or (and (consp (car v
))
284 (member file
(car v
)))
286 (dolist (w (if (consp (second v
))
288 (cond ((or (<= obj-date
(file-date (source w
)))
289 (member w
*depends-new
*))
290 (return-from file-out-dated t
))))))))))
293 (defun make-compile-file ( l
)
294 (format t
"~&Begin compile ~a at ~a~%" l
(print-date nil
))
295 (dolist (v *when-compile
*) (eval v
))
296 (setq *when-compile
* nil
) (dolist (v *when-compile
*) (eval v
))
297 (setq *when-compile
* nil
)
298 ;;Franz excl needs pathnames quoted, and some other lisp
299 ;;would not allow an apply here. Sad.
300 (eval `(compile-file ',(source l
) :output-file
',(object l
)
302 (format t
"~&End compile ~a at ~a~%" l
(print-date nil
))
305 (defun make-load-file (l) (load (object l
)))
307 ;;these are versions which don't really compile or load files, but
308 ;;do create a new "compiled file" and "fake load" to test date mechanism.
310 (defun make-compile-file (file)
311 (format t
"~%Fake Compile ~a" (namestring (source file
)))
312 (dolist (v *when-compile
*) (eval v
)) (setq *when-compile
* nil
)
313 (with-open-file (st (object file
) :direction
:output
)
314 (format st
"(print (list 'hi))")))
316 (defun make-load-file (l)
317 (format t
"~%Fake loading ~a" (namestring(object l
))))
322 (defun compile-files (files &optional
(*force
* *force
*)
323 &key
(source-path source-path
)
324 (object-path object-path
)
327 (declare (special object-path source-path
*force
* show
))
329 (when (or *force
* (file-out-dated files
))
330 (push files
*depends-new
*)
333 (format t
"~%(COMPILE-FILE ~s)" (namestring (source files
))))
335 (and *compile-file-function
*
336 (funcall *compile-file-function
* files
))
338 ((keywordp (car files
))
339 (let ((fun (get (car files
) 'compile
)))
340 (if fun
(apply fun
(cdr files
)))))
341 (t (dolist (v files
) (compile-files v
*force
*)))))
343 (defun system-files (system &aux
*files
*)
344 (declare (special *files
*))
345 (let ((sys (get system
:make
)))
350 (defun get-files1 (sys)
351 (cond ((and sys
(atom sys
) )(pushnew sys
*files
*))
352 ((eq (car sys
) :serial
) (get-files1 (cdr sys
)))
353 ((keywordp (car sys
)))
354 (t (loop for v in sys do
(get-files1 v
)))))