File: make.lisp

package info (click to toggle)
gcl 2.6.14-19
  • links: PTS
  • area: main
  • in suites: trixie
  • size: 60,804 kB
  • sloc: ansic: 177,407; lisp: 151,508; asm: 128,169; sh: 22,510; cpp: 11,923; tcl: 3,181; perl: 2,930; makefile: 2,360; sed: 334; yacc: 226; lex: 95; awk: 30; fortran: 24; csh: 23
file content (409 lines) | stat: -rwxr-xr-x 14,296 bytes parent folder | download | duplicates (15)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
;;; -*-  Mode: Lisp; Package: MAKE; Syntax: Common-Lisp; Base: 10 -*- ;;;;
;; Copyright William F. Schelter 1989.

;; The author expressly permits copying and alteration of this file,
;; provided any modifications are clearly labeled, and this notice is
;; preserved.   The author provides no warranty and this software is
;; provided on an 'as is' basis.
(in-package "MAKE" :use '("LISP") #+gcl :external #+gcl 11
	    #+gcl :internal #+gcl 79)

(export '(make system-load system-compile))
(provide "MAKE")
;;;  *******  Description of Make Facility ************
;;  We provide a simple MAKE facility to allow
;;compiling and loading of a tree of files
;;If the tree is '(a b (d e g h) i)
;;   a will be loaded before b is compiled,
;;   b will be loaded before d, e, g, h are compiled
;;   d e g h will be loaded before i is compiled.

;;  A record is kept of write dates of loaded compiled files, and a file
;;won't be reloaded if it is the same version (unless a force flag is t).

;;Thus if you do (make :uinfor) twice in a row, the second one would not
;;load anything.  NOTE: If you change a, and a macro in it would affect
;;b, b still will not be recompiled.  You must choose the :recompile t
;;option, to force the recompiling if you change macro files.
;;Alternately you may specify dependency information (see :depends below).


;;****** Sample file which when loaded causes system ALGEBRA 
;;              to be compiled and loaded ******

;;(require "MAKE")
;;(use-package "MAKE")
;;(setf (get :algebra :make) '(a b (d e) l))
;;(setf (get :algebra :source-path) "/usr2/wfs/algebra/foo.lisp")
;;(setf (get :algebra :object-path) "/usr2/wfs/algebra/o/foo.o")
;;(make :algebra :compile t)

;;  More complex systems may need to do some special operations
;;at certain points of the make.  
;;the tree of files may contain some keywords which have special meaning.
;;eg. '(a b (:progn (gbc) (if make::*compile*
;;                                  (format t "A and B finally compiled")))
;;          (:load-source h i)
;;          (d e) l)

;;then during the load and compile phases the function (gbc) will be
;;called after a and b have been acted on, and during the compile phase
;;the message about "A and B finally.." will be printed.
;;the lisp files h and i will be loaded after merging the paths with 
;;the source directory.  This feature is extensible: see the definitions
;;of :load-source and :progn.

;;  The keyword feature is extensible, and you may specify what 
;;happens during the load or compile phase for your favorite keyword.
;;To do this look at the definition of :progn, and :load-source
;;in the source for make.


;;Dependency feature:

;;   This make NEVER loads or compiles files in an order different from
;;that specified by the tree.  It will omit loading files which are
;;loaded and up to date, but if two files are out of date, the first (in
;;the printed representation of the tree), will always be loaded before
;;the second.  A consequence of this is that circular dependencies can
;;never occur.
;;
;;  If the :make tree contains (a b c d (:depends (c d) (a b))) then c
;;and d depend on a and b, so that if a or b need recompilation then c
;;and d will also be recompiled.  Thus the general form of a :depends
;;clause is (:depends later earlier) where LATER and EARLIER are either
;;a single file or a list of files. Read it as LATER depends on EARLIER.
;;A declaration of a (:depends (c) (d)) would have no effect, since the
;;order in the tree already rules out such a dependence.

;;  An easy way of specifying a linear dependence is by using :serial.
;;The tree (a (:serial b c d) e)  is completely equivalent to the tree
;;(a b c d e (:depends c b)(:depends d (b c))), but with a long list of
;;serial files, it is inconvenient to specify them in the
;;latter representation.

;;A common case is a set of macros whose dependence is serial followed by a set
;;of files whose order is unimportant.  A conventient way of building that
;;tree is
;;
;;(let ((macros '(a b c d))
;;      (files '(c d e f g)))
;;  `((:serial ,@ macros)
;;    ,files
;;    (:depends ,files ,macros)))

;;  The depends clause may occur anywhere within the tree, since
;;an initial pass collects all dependency information.

;;  Make takes a SHOW keyword argument.  It is almost impossible to simulate
;;all the possible features of make, for show.  Nonetheless, it is good
;;to get an idea of the compiling and loading sequence for a new system.
;;As a byproduct, you could use the output, as a simple sequence of calls
;;to compile-file and load, to do the required work, when make is not around
;;to help.


;;*****  Definitions ********
(defvar *files-loaded* nil)
(defvar *show-files-loaded* nil) ;only for show option
(defvar *load* nil "Will be non nil inside load-files")
(defvar *compile* nil "Bound by compile-files to t")
(defvar *depends* nil)
(defvar *depends-new* nil)
(defvar *force* nil)
(defvar *when-compile* nil "Each compile-file evals things in this list and sets it to nil")
#+kcl(defvar *system-p* nil)
(defvar *compile-file-function* 'make-compile-file)
(defvar *load-function* 'make-load-file)
(defvar show nil)
(defvar *cflags* #-kcl nil
  #+kcl '(:system-p  *system-p*))


;;this is the main entry point

(defun make (system &key recompile compile batch object-path source-path
		    show proclaims
		    &aux files *depends* *when-compile*
		    *show-files-loaded*
		    #+gcl (*load-fn-too* proclaims)

		    )

  "SYSTEM is a tree of files, or a symbol with :make property.  It
loads all file files in system.  If COMPILE it will try to compile
files with newer source versions than object versions, before loading.
If RECOMPILE it will recompile all files.  This is equivalent to deleting all
objects and using :compile t.   SOURCE-PATH is merged with the name given
in the files list, when looking for a file to compile.  OBJECT-PATH is
merged with the name in the files list, when looking for a file to
load.  If SYSTEM is a symbol, then a null OBJECT-PATH would be set to
the :object-path property of SYSTEM.  Similarly for :source-path"

  (declare (special object-path source-path show)) batch
  (cond ((symbolp system)
	 (or object-path (setf object-path (get system :object-path)))
	 (or source-path (setf source-path (get system :source-path)))
	 (setf files (get system :make))
	 (or files
	     (if (get system :files)
		 (error "Use :make property, :files property is obssolet{!")))
	 )
	(t (setf files system)))
  #+gcl (when proclaims (compiler::emit-fn t) (compiler::setup-sys-proclaims))
  (let (#+lispm ( si::inhibit-fdefine-warnings
		 (if batch :just-warn  si::inhibit-fdefine-warnings)))
    (let ((*depends*  (if (or compile recompile) (get-depends system)))
	  *depends-new*)
    (dolist (v files)
	    (when (or compile recompile)
		    (compile-files v recompile))
	    (load-files v recompile)))
    #+gcl
    (if proclaims (compiler::write-sys-proclaims))
    ))

(defun system-load (system-name &rest names)
  "If :infor is a system, (system-load :uinfor joe betty) will load
joe and betty from the object-path for :uinfor"
  (load-files names t (get system-name :object-path)))

(defun system-compile (system-name &rest names)
				  
  "If :iunfor is a system, (system-compile :uinfor joe) will in the
source path for joe and compile him into the object path for :uinfor"
  (compile-files names t :source-path
		 (get system-name :source-path) :object-path
		 (get system-name :object-path)))

(defun get-depends (system-name &aux result)
  (dolist (v (get system-name :make))
  (cond    ((atom v) )
	   ((eq (car v) :serial)
	    (do ((w (reverse (cdr v))(cdr w)))
		((null (cdr w)))
		(push (list (car w) (cdr w)) result)))
	   ((eq (car v) :depends)
	    (push (cdr v) result ))))
    result)
	   
#+kcl
(setq si::*default-time-zone* 6)
#+winnt
(setq SYSTEM:*DEFAULT-TIME-ZONE*  (GET-SYSTEM-TIME-ZONE))

(defun print-date (&optional(stream *standard-output*)
			    (time (get-universal-time)))
  (multiple-value-bind (sec min hr day mon yr wkday)
		       (decode-universal-time time)
	(format stream "~a ~a ~a ~d:~2,'0d:~2,'0d ~a"
		(nth wkday '( "Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun"))
		(nth (1- mon) '("Jan" "Feb" "Mar" "Apr" "May" "Jun"
			   "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"))
		day
		hr min sec yr)))
	       
;;This is an awfully roundabout downcase, but some machines
;;like symbolics swap cases on the pathname, so we have to do an extra 
;;swap!!
(defun lowcase (na &aux (*print-case* :downcase))
  (pathname-name (pathname  (format nil "~a" na))))

(defun our-merge (name path &optional ign  ) ign
  #+lispm (setq name (string-upcase (string name)))
    (make-pathname :name (string name)
		   :type (pathname-type path)
		   :version (pathname-version path)
		   :host (pathname-host path)
		   :directory (pathname-directory path)))


#+kcl
(setf (get :link 'load)
      #'(lambda (path to-link)
	  (declare (special object-path))
	  (si::faslink (our-merge	(lowcase  path) object-path)
		       to-link)))

(setf (get :link 'compile)
      #'(lambda (path to-link) 
	   to-link
	  (compile-files  path *force*)))

(setf (get :progn 'load)
      #'(lambda (&rest args)
	  (eval (cons 'progn args))))

(setf (get :progn 'compile) (get :progn 'load))

(setf (get :load-source 'load)
      #'(lambda (&rest args)
	  (declare (special source-path))
	  (load-files args *force* source-path)))

(setf (get :load-source-when-compile 'compile)
      (get :load-source 'load))

;;should nott use :lisp anymore
(setf (get :lisp 'load)
      #'(lambda (x) (error "please replace :lisp by :load-source")))

(setf (get :serial 'load) #'(lambda (&rest l)(load-files l)))
(setf (get :serial 'compile)
      #'(lambda (&rest l)
	  (dolist (v l)
	    (compile-files v)
	    (load-files v))))


(defun load-files (files &optional (*force* *force*) (object-path object-path)
			 &aux path tem (*load* t))
  (declare (special object-path source-path *force* show))
  (cond ((atom files)
	 (setq path (object files))
	 (cond (show
		(unless (member path *show-files-loaded* :test 'equalp)
			(push path *show-files-loaded*)
			(format t "~%(LOAD ~s)" (namestring path))))
	       ((null *load-function*))
	       ((or *force*
		    (or (not (setq tem
				   (member path *files-loaded*
					   :test 'equalp :key 'car)))
			(> (file-write-date  path) (cdr (car tem)))))
		(funcall *load-function* files)
		(push (cons path (file-write-date path)) *files-loaded*))))
	((keywordp (car files))
	 (let ((fun (get (car files) 'load)))
	   (cond (fun (apply fun (cdr files))))))
	(t (dolist (v files) (load-files v *force*  object-path)))))


(defun file-date (file)
  (if (probe-file file) (or (file-write-date file) 0) 0))

(defun source (file)
  (declare (special source-path))
   (our-merge  (lowcase file) source-path))

(defun object (file)
  (declare (special object-path))
   (our-merge  (lowcase file) object-path))


;;for lisp machines, and others where checking date is slow, this
;;we should try to cache some dates, and then remove them as we do
;;things like compile files...

(defun file-out-dated (file)
  (let ((obj-date (file-date (object file))))
    (or (<= obj-date (file-date (source file)))
	(dolist (v *depends*)
		(cond ((or (and (consp (car v))
				(member file (car v)))
			   (eq (car v) file))
		       (dolist (w (if (consp (second v))
				      (second v) (cdr v)))
			       (cond ((or (<= obj-date (file-date (source w)))
					  (member w *depends-new*))
				      (return-from file-out-dated t))))))))))


(defun make-compile-file ( l)
  (format t "~&Begin compile ~a at ~a~%" l (print-date nil))
  (dolist (v *when-compile*) (eval v))
  (setq *when-compile* nil)
  ;;Franz excl needs pathnames quoted, and some other lisp
  ;;would not allow an apply here.  Sad.
  (eval `(compile-file ',(source l) :output-file ',(object l)
		       ,@ *cflags*))
  (format t "~&End compile ~a at ~a~%" l (print-date nil))

  )

(defvar *load-fn-too* nil)
(defun make-load-file (l)
  (let ((na (object l)))
    (load na)
    (if (and *load-fn-too*
	     (probe-file
	      (setq na
		    (our-merge (lowcase l) (merge-pathnames "foo.fn" na)))))
	(load na))
	
  
  ))

;;these are versions which don't really compile or load files, but
;;do create a new "compiled file" and "fake load" to test date mechanism.
#+debug
(defun make-compile-file (file)
  (format t "~%Fake Compile ~a" (namestring (source file)))
    (dolist (v *when-compile*) (eval v))  (setq *when-compile* nil)
  (with-open-file (st (object file) :direction :output)
		  (format st "(print (list 'hi))")))
#+debug
(defun make-load-file (l)
  (format t "~%Fake loading ~a" (namestring(object l))))

 
		  

(defun compile-files (files &optional (*force*  *force*)
			    &key (source-path source-path)
			    (object-path object-path)
			    &aux
			    (*compile* t) )
  (declare (special object-path source-path *force* show))
  (cond ((atom files)
	 (when (or *force*  (file-out-dated files))
	      (push files  *depends-new*)
	       (cond
		(show
		 (format t "~%(COMPILE-FILE ~s)" (namestring (source files))))
		(t
		 (and *compile-file-function*
		      (funcall *compile-file-function* files))
		 ))))
	((keywordp (car files))
	 (let ((fun (get (car files) 'compile)))
	   (if fun (apply fun (cdr files)))))
	(t (dolist (v files) (compile-files v *force*)))))

;;Return the files for SYSTEM 

(defun system-files (system &aux *files*)
  (declare (special *files*))
  (let ((sys (get system :make)))
    (get-files1 sys))
  (nreverse *files*))

   
(defun get-files1 (sys)
  (declare (special *files*))
  (cond ((and sys (atom sys) )(pushnew sys *files*))
	((eq (car sys) :serial) (get-files1 (cdr sys)))
	((keywordp (car sys)))
	(t (dolist (v sys) (get-files1 v)))))

  
(defmacro make-user-init (files &aux (object-path
				      (if (boundp 'object-path) object-path
					  "foo.o")))
  (declare (special object-path))
    `(progn
       (clines "void gcl_init_or_load1 ();
#define init_or_load(fn,file) do {extern int fn(); gcl_init_or_load1(fn,file);}  while(0)

user_init{") ,@
     (sloop::sloop for x  in files
	for f  = (substitute #\- #\_ (lowcase x))
	for ff =  (namestring (truename (object x)))
	collect
	`(clines ,(Format nil "init_or_load(init_~a,\"~a\");" f ff)))
       (clines "}")))