home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Tools / Languages / MacHaskell 2.2 / support / compile.scm next >
Encoding:
Text File  |  1994-09-27  |  14.3 KB  |  448 lines  |  [TEXT/CCL2]

  1. ;;; compile.scm -- compilation utilities
  2. ;;;
  3. ;;; author :  Sandra Loosemore
  4. ;;; date   :  24 Oct 1991
  5. ;;;
  6. ;;; This file defines a makefile-like compilation system that supports
  7. ;;; a hierarchy of dependencies.
  8. ;;; The external entry points are define-compilation-unit, load-unit, and
  9. ;;; compile-and-load-unit.
  10.  
  11.  
  12.  
  13. ;;;=====================================================================
  14. ;;; Parsing
  15. ;;;=====================================================================
  16.  
  17.  
  18. ;;; Establish global defaults for filenames.
  19.  
  20. (define compile.source-filename source-file-type)
  21. (define compile.binary-filename binary-file-type)
  22. (define compile.binary-subdir (string-append lisp-implementation-name "/"))
  23. (define compile.delayed-loads '())
  24.  
  25.  
  26. ;;; Top level units are stored in this table.
  27. ;;; This is really a slight wart on the whole scheme of things; this
  28. ;;; is done instead of storing the top-level units in variables because
  29. ;;; we were getting unintentional name collisions.
  30.  
  31. (define compile.unit-table (make-table))
  32.  
  33. (define-syntax (compile.lookup-unit name)
  34.   `(table-entry compile.unit-table ,name))
  35.  
  36. (define (mung-global-units names lexical-units)
  37.   (map (lambda (n)
  38.      (if (memq n lexical-units)
  39.          n
  40.          `(compile.lookup-unit ',n)))
  41.        names))
  42.  
  43.  
  44. ;;; Top-level compilation units are defined with define-compilation-unit.
  45. ;;; The body can consist of the following clauses:
  46. ;;;
  47. ;;; (source-filename <filename>)
  48. ;;; (binary-filename <filename>)
  49. ;;;   Specify source and/or binary file names.  For nested units, these
  50. ;;;   are merged with defaults from outer units.  If you don't specify
  51. ;;;   an explicit binary filename, it's inherited from the source file
  52. ;;;   name.
  53. ;;; (require ...)
  54. ;;;   Specify compile/load dependencies.  Arguments are names of other
  55. ;;;   units/component files; these names have scoping like let*, so a unit
  56. ;;;   can require previously listed units at the same or outer level.
  57. ;;; (unit name ....)
  58. ;;;   Specifies a nested unit.  This can appear multiple times.
  59. ;;;   If a unit doesn't include any nested units, then it's a leaf
  60. ;;;   consisting of a single source file.
  61. ;;; (load <boolean>)
  62. ;;;   If supplied and false, the unit isn't loaded unless it is needed
  63. ;;;   to satisfy a require clause.  Used for files containing compilation
  64. ;;;   support stuff.
  65. ;;; (compile <boolean>)
  66. ;;;   If supplied and false, the unit isn't compiled.  Only useful for
  67. ;;;   leaf nodes.  Typically used in combination with (load '#f) to suppress
  68. ;;;   compilation of stuff only used at compile time.
  69.  
  70. (define-syntax (define-compilation-unit name . clauses)
  71.   `(begin
  72.      (let ((unit  ,(compile.process-unit-spec name clauses '#t '())))
  73.        (setf (compile.lookup-unit ',name) unit)
  74.        (setf compilation-units (append compilation-units (list unit))))
  75.      ',name))
  76.  
  77.  
  78. ;;; The basic approach is to turn the compilation unit definition into
  79. ;;; a big LET*, and put calls to build the actual unit object inside
  80. ;;; of this.
  81. ;;; 
  82.  
  83. (define (compile.process-unit-spec name clauses top-level? lexical-units)
  84.   (multiple-value-bind
  85.       (source-filename binary-filename require nested-units
  86.                load? compile?)
  87.       (compile.parse-unit-spec clauses lexical-units)
  88.     `(let* ((compile.source-filename ,source-filename)
  89.         (compile.binary-filename ,binary-filename)
  90.         (compile.unit-require    (list ,@require))
  91.         (compile.delayed-loads   (append compile.delayed-loads
  92.                          (compile.select-delayed-loads
  93.                              compile.unit-require)))
  94.         ,@nested-units)
  95.        (make compile.unit
  96.          (name ',name)
  97.          (source-filename compile.source-filename)
  98.          (binary-filename compile.binary-filename)
  99.          (components (list ,@(map (function car) nested-units)))
  100.          (require compile.unit-require)
  101.          (top-level? ',top-level?)
  102.          (load? ,load?)
  103.          (compile? ,compile?)
  104.          (delayed-loads compile.delayed-loads)))))
  105.  
  106. (define (compile.parse-unit-spec clauses lexical-units)
  107.   (let ((source-filename  '#f)
  108.     (binary-filename  '#f)
  109.     (require          '#f)
  110.     (nested-units     '())
  111.     (load?            ''#t)
  112.     (compile?         ''#t))
  113.     (dolist (c clauses)
  114.       (cond ((not (pair? c))
  115.          (compile.unit-syntax-error c))
  116.         ((eq? (car c) 'source-filename)
  117.          (if source-filename
  118.          (compile.unit-duplicate-error c)
  119.          (setf source-filename (cadr c))))
  120.         ((eq? (car c) 'binary-filename)
  121.          (if binary-filename
  122.          (compile.unit-duplicate-error c)
  123.          (setf binary-filename (cadr c))))
  124.         ((eq? (car c) 'require)
  125.          (if require
  126.          (compile.unit-duplicate-error c)
  127.          (setf require (mung-global-units (cdr c) lexical-units))))
  128.         ((eq? (car c) 'unit)
  129.          (push (list (cadr c)
  130.              (compile.process-unit-spec (cadr c) (cddr c)
  131.                             '#f lexical-units))
  132.            nested-units)
  133.          (push (cadr c) lexical-units))
  134.         ((eq? (car c) 'load)
  135.          (setf load? (cadr c)))
  136.         ((eq? (car c) 'compile)
  137.          (setf compile? (cadr c)))
  138.         (else
  139.          (compile.unit-syntax-error c))))
  140.     (values
  141.         (if source-filename
  142.         `(compile.merge-filenames ,source-filename
  143.              compile.source-filename '#f)
  144.         'compile.source-filename)
  145.     (if binary-filename
  146.         `(compile.merge-filenames ,binary-filename
  147.              compile.binary-filename '#f)
  148.         (if source-filename
  149.         '(compile.merge-filenames compile.binary-filename
  150.              compile.source-filename
  151.              compile.binary-subdir)
  152.         'compile.binary-filename))
  153.     (or require '())
  154.     (nreverse nested-units)
  155.     load?
  156.     compile?)))
  157.  
  158.  
  159. (predefine (error format . args))
  160.  
  161. (define (compile.unit-syntax-error c)
  162.   (error "Invalid compilation unit clause ~s." c))
  163.  
  164. (define (compile.unit-duplicate-error c)
  165.   (error "Duplicate compilation unit clause ~s." c))
  166.  
  167.  
  168.  
  169. ;;;=====================================================================
  170. ;;; Representation and utilities
  171. ;;;=====================================================================
  172.  
  173. ;;; Here are constructors and accessors for unit objects.
  174. ;;; Implementationally, the compilation unit has the following slots:
  175. ;;;
  176. ;;; * The unit name.
  177. ;;; * The source file name.
  178. ;;; * The binary file name.
  179. ;;; * A list of component file/units.
  180. ;;; * A list of units/files to require.
  181. ;;; * A load timestamp.
  182. ;;; * A timestamp to keep track of the newest source file.
  183. ;;; * Flags for compile and load.
  184.  
  185. (define-struct compile.unit
  186.   (predicate compile.unit?)
  187.   (slots
  188.     (name             (type symbol))
  189.     (source-filename  (type string))
  190.     (binary-filename  (type string))
  191.     (components       (type list))
  192.     (require          (type list))
  193.     (top-level?       (type bool))
  194.     (load?            (type bool))
  195.     (compile?         (type bool))
  196.     (delayed-loads    (type list))
  197.     (load-time        (type (maybe integer)) (default '#f))
  198.     (source-time      (type (maybe integer)) (default '#f))
  199.     (last-update      (type (maybe integer)) (default 0))
  200.     ))
  201.  
  202. (define (compile.newer? t1 t2)
  203.   (and t1
  204.        t2
  205.        (> t1 t2)))
  206.  
  207. (define (compile.select-newest t1 t2)
  208.   (if (compile.newer? t1 t2) t1 t2))
  209.  
  210. (define (compile.get-source-time u)
  211.   (let ((source-file  (compile.unit-source-filename u)))
  212.     (if (file-exists? source-file)
  213.     (file-write-date source-file)
  214.     '#f)))
  215.  
  216. (define (compile.get-binary-time u)
  217.   (let ((binary-file  (compile.unit-binary-filename u)))
  218.     (if (file-exists? binary-file)
  219.     (file-write-date binary-file)
  220.     '#f)))
  221.  
  222. (define (compile.load-source u)
  223.   (load (compile.unit-source-filename u))
  224.   (setf (compile.unit-load-time u) (current-date)))
  225.  
  226. (define (compile.load-binary u)
  227.   (load (compile.unit-binary-filename u))
  228.   (setf (compile.unit-load-time u) (current-date)))
  229.  
  230. (define (compile.compile-and-load u)
  231.   (let ((source-file  (compile.unit-source-filename u))
  232.     (binary-file  (compile.unit-binary-filename u)))
  233.     (compile-file source-file binary-file)
  234.     (load binary-file)
  235.     (setf (compile.unit-load-time u) (current-date))))
  236.  
  237. (define (compile.do-nothing u)
  238.   u)
  239.  
  240.       
  241. ;;;=====================================================================
  242. ;;; Runtime support for define-compilation-unit
  243. ;;;=====================================================================
  244.  
  245. (define (compile.select-delayed-loads require)
  246.   (let ((result  '()))
  247.     (dolist (r require)
  248.       (if (not (compile.unit-load? r))
  249.       (push r result)))
  250.     (nreverse result)))
  251.  
  252. (define (compile.merge-filenames fname1 fname2 add-subdir)
  253.   (let ((place1  (filename-place fname1))
  254.     (name1   (filename-name fname1))
  255.     (type1   (filename-type fname1)))
  256.     (assemble-filename
  257.         (if (string=? place1 "")
  258.         (if add-subdir
  259.         (string-append (filename-place fname2) add-subdir)
  260.         fname2)
  261.         place1)
  262.     (if (string=? name1 "") fname2 name1)
  263.     (if (string=? type1 "") fname2 type1))))
  264.  
  265.  
  266.  
  267. ;;;=====================================================================
  268. ;;; Load operation
  269. ;;;=====================================================================
  270.  
  271. ;;; Load-unit and compile-and-load-unit are almost identical.  The only 
  272. ;;; difference is that load-unit will load source files as necessary, while
  273. ;;; compile-and-load-unit will compile them and load binaries instead.
  274.  
  275. (define (load-unit u)
  276.   (compile.update-unit-source-times u '#f (current-date))
  277.   (compile.load-unit-aux u))
  278.  
  279. (define (compile.load-unit-aux u)
  280.   (with-compilation-unit ()
  281.     (compile.load-unit-recursive u '#f)))
  282.  
  283. (define (compile-and-load-unit u)
  284.   (compile.update-unit-source-times u '#f (current-date))
  285.   (compile.compile-and-load-unit-aux u))
  286.  
  287. (define (compile.compile-and-load-unit-aux u)
  288.   (with-compilation-unit ()
  289.     (compile.load-unit-recursive u '#t)))
  290.  
  291.  
  292. ;;; Load a bunch of compilation units as a group.  This is useful because
  293. ;;; it can prevent repeated lookups of file timestamps.  Basically, the
  294. ;;; assumption is that none of the source files will change while the loading
  295. ;;; is in progress.
  296. ;;; In case of an error, store the units left to be compiled in a global
  297. ;;; variable.
  298.  
  299. (define remaining-units '())
  300.  
  301. (define (load-unit-list l)
  302.   (let ((timestamp  (current-date)))
  303.     (dolist (u l)
  304.       (compile.update-unit-source-times u '#f timestamp))
  305.     (setf remaining-units l)
  306.     (dolist (u l)
  307.       (compile.load-unit-aux u)
  308.       (pop remaining-units))))
  309.  
  310. (define (compile-and-load-unit-list l)
  311.   (let ((timestamp  (current-date)))
  312.     (dolist (u l)
  313.       (compile.update-unit-source-times u '#f timestamp))
  314.     (setf remaining-units l)
  315.     (dolist (u l)
  316.       (compile.compile-and-load-unit-aux u)
  317.       (pop remaining-units))))
  318.  
  319.  
  320. ;;; Walk the compilation unit, updating the source timestamps.
  321.  
  322. (define (compile.update-unit-source-times u newest-require timestamp)
  323.   (unless (eqv? timestamp (compile.unit-last-update u))
  324.     (setf (compile.unit-last-update u) timestamp)
  325.     (dolist (r (compile.unit-require u))
  326.       (if (compile.unit-top-level? r)
  327.       (compile.update-unit-source-times r '#f timestamp))
  328.       (setf newest-require
  329.         (compile.select-newest newest-require
  330.                    (compile.unit-source-time r))))
  331.     (let ((components  (compile.unit-components u)))
  332.       (if (not (null? components))
  333.       (let ((source-time  newest-require))
  334.         (dolist (c components)
  335.           (compile.update-unit-source-times c newest-require timestamp)
  336.           (setf source-time
  337.             (compile.select-newest source-time
  338.                        (compile.unit-source-time c))))
  339.         (setf (compile.unit-source-time u) source-time))
  340.       (setf (compile.unit-source-time u)
  341.         (compile.select-newest
  342.           newest-require
  343.           (compile.get-source-time u)))))))
  344.  
  345.  
  346. ;;; Load a compilation unit.  Do this by first loading its require list,
  347. ;;; then by recursively loading each of its components, in sequence.  
  348. ;;; Note that because of the way scoping of units works and the
  349. ;;; sequential nature of the load operation, only top-level
  350. ;;; units in the require list have to be loaded explicitly.
  351.  
  352. (define (compile.load-unit-recursive u compile?)
  353.   (let ((components       (compile.unit-components u)))
  354.     ;; First recursively load dependencies.
  355.     ;; No need to update time stamps again here.
  356.     (dolist (r (compile.unit-require u))
  357.       (if (compile.unit-top-level? r)
  358.       (compile.load-unit-aux r)))
  359.     (if (not (null? components))
  360.     ;; Now recursively load subunits.
  361.     (dolist (c components)
  362.       (unless (not (compile.unit-load? c))
  363.         (compile.load-unit-recursive c compile?)))
  364.     ;; For a leaf node, load either source or binary if necessary.
  365.     (let ((source-time  (compile.unit-source-time u))
  366.           (binary-time  (compile.get-binary-time u))
  367.           (load-time    (compile.unit-load-time u)))
  368.       (cond ((compile.newer? load-time source-time)
  369.          ;; The module has been loaded since it was last changed,
  370.          ;; but maybe we want to compile it now.
  371.          (if (and compile?
  372.               (compile.unit-compile? u)
  373.               (compile.newer? source-time binary-time))
  374.              (begin
  375.                (compile.do-delayed-loads
  376.                    (compile.unit-delayed-loads u)
  377.                    compile?)
  378.                (compile.compile-and-load u))
  379.              (compile.do-nothing u)))
  380.         ((compile.newer? binary-time source-time)
  381.          ;; The binary is up-to-date, so load it.
  382.          (compile.load-binary u))
  383.         (else
  384.          ;; The binary is out-of-date, so either load source or
  385.          ;; recompile the binary.
  386.          (compile.do-delayed-loads
  387.              (compile.unit-delayed-loads u)
  388.              compile?)
  389.          (if (and compile? (compile.unit-compile? u))
  390.              (compile.compile-and-load u)
  391.              (compile.load-source u)))
  392.         )))))
  393.  
  394.  
  395. (define (compile.do-delayed-loads units compile?)
  396.   (dolist (u units)
  397.     (compile.load-unit-recursive u compile?)))
  398.  
  399.  
  400.  
  401.  
  402. ;;;=====================================================================
  403. ;;; Extra stuff
  404. ;;;=====================================================================
  405.  
  406.  
  407. ;;; Reload a unit without testing to see if any of its dependencies are
  408. ;;; out of date.
  409.  
  410. (define (reload-unit-source u)
  411.   (let ((components  (compile.unit-components u)))
  412.     (if (not (null? components))
  413.     (dolist (c components)
  414.       (reload-unit-source c))
  415.     (compile.load-source u))))
  416.  
  417. (define (reload-unit-binary u)
  418.   (let ((components  (compile.unit-components u)))
  419.     (if (not (null? components))
  420.     (dolist (c components)
  421.       (reload-unit-binary c))
  422.     (compile.load-binary u))))
  423.  
  424.  
  425. ;;; Find a (not necessarily top-level) compilation unit with the given
  426. ;;; name.
  427.  
  428. (define (find-unit name)
  429.   (compile.find-unit-aux name compilation-units))
  430.  
  431. (define (compile.find-unit-aux name units)
  432.   (block find-unit-aux
  433.     (dolist (u units '#f)
  434.       (if (eq? name (compile.unit-name u))
  435.       (return-from find-unit-aux u)
  436.       (let* ((components (compile.unit-components u))
  437.          (result     (compile.find-unit-aux name components)))
  438.         (if result
  439.         (return-from find-unit-aux result)))))))
  440.  
  441.  
  442. ;;; Combine the two above:  reload a compilation unit.
  443.  
  444. (define-syntax (reload name)
  445.   `(reload-unit-source
  446.      (or (find-unit ',name)
  447.      (error "Couldn't find unit named ~s." ',name))))
  448.