;-*- Mode: Common-lisp; Package: ytools; Readtable: ytools; -*- (in-package :ytools) ;;;$Id: depend.lisp,v 2.2 2006/05/25 14:17:12 airfoyle Exp $ ;;; Copyright (C) 1976-2005 ;;; Drew McDermott and Yale University. All rights reserved ;;; This software is released under the terms of the Modified BSD ;;; License. See file COPYING for details. (eval-when (:compile-toplevel :load-toplevel) (export '(depends-on module scan-depends-on self-compile-dep in-header end-header))) (eval-when (:compile-toplevel :load-toplevel :execute) (defstruct (Scan-depends-on-state (:conc-name Sds-) (:print-object (lambda (sdo-state srm) (format srm "#" (chunk-name-abbrev-pathnames (Chunk-name (Sds-file-chunk sdo-state))) (mapcar #'Sub-file-type-name (Sds-sub-file-types sdo-state)))))) file-chunk ; for the file whose basis is being found sub-file-types) ;;; -- A list of sub-file types L such that all antecedents found ;;; after this will be slurped with respect to every element of L. ;;; Always includes at least macros-sub-file-type*. ;;; State for task is an Sdo-state for the file chunk we're scanning. ;;; We're scanning it to figure out its basis (and callees) (defvar header-slurp-count* 0) (defvar headers-slurped* !()) (def-slurp-task scan-depends-on :default (\\ (_ _) true) ;;; -- The idea is that anything we didn't anticipate takes us ;;; out of the header. :file->state-fcn (\\ (pn) (incf header-slurp-count*) (incf (alref headers-slurped* pn 0 :test #'equal)) (make-Scan-depends-on-state :file-chunk (place-Code-file-chunk pn) ;;;; :expect-only-run-time-dependencies false :sub-file-types (list macros-sub-file-type*) ))) ) (setq hidden-slurp-tasks* (adjoin 'scan-depends-on hidden-slurp-tasks*)) (defclass Code-file-dep (Code-dep-chunk) ;; Keeps a File-scanned-for-deps chunk to reread the file's ;; header when necessary -- ((scanner :accessor Code-file-dep-scanner))) (defclass File-scanned-for-deps (Chunk) ((file :accessor File-scanned-for-deps-file :initarg :file) (loaded-file :accessor File-scanned-for-deps-loaded-file :initarg :loaded-file))) (defmethod create-loaded-controller ((file-ch Code-file-chunk) (loaded-ch Loaded-file-chunk)) (let ((controller (chunk-with-name `(:code-file-dep ,(Chunk-name file-ch)) (\\ (exp) (make-instance 'Code-file-dep :name exp :controllee loaded-ch)))) (scanner (chunk-with-name `(:file-header-scanned ,(Chunk-name file-ch)) (\\ (exp) (make-instance 'File-scanned-for-deps :name exp :file file-ch :loaded-file loaded-ch))))) (setf (Code-file-dep-scanner controller) scanner) controller)) (defmethod Code-dep-chunk-meta-clock-val ((file-dep Code-file-dep)) file-op-count*) (defmethod derive-date ((file-dep Code-file-dep)) (cond ((= (Chunk-date file-dep) file-op-count*) file-op-count*) (t +no-info-date+))) (defmethod derive ((file-dep Code-file-dep)) ;;;;(trace-around Code-file-dep/derive ;;;; (:> "(Code-file-dep/derive: " file-dep ")") (cond ((< (Chunk-date file-dep) file-op-count*) (let ((scanner (Code-file-dep-scanner file-dep))) (chunk-request-mgt scanner) (chunk-update scanner false false)))) file-op-count* ;;;; (:< (val &rest _) "Code-file-dep/derive: " val)) ) (defmethod derive-date ((fb File-scanned-for-deps)) (let ((pn (Code-file-chunk-pathname (File-scanned-for-deps-file fb)))) (cond ((or (not (probe-file pn)) (>= (Chunk-date fb) (file-write-date pn))) (Chunk-date fb)) (t +no-info-date+ )))) (defmethod derive ((fb File-scanned-for-deps)) (let ((cached-file-ch (File-scanned-for-deps-file fb))) (cond ((probe-file (Code-file-chunk-pathname cached-file-ch)) (let* ((loaded-file-ch (File-scanned-for-deps-loaded-file fb)) (source-file-ch (Loaded-file-chunk-source loaded-file-ch))) (cond ((and source-file-ch (< (Chunk-date fb) (file-write-date (Code-file-chunk-pathname source-file-ch)))) (set-deps-by-slurping source-file-ch loaded-file-ch) (get-universal-time)) (t ;; No need to do anything (max 0 (Chunk-date fb)))))) (t (max 0 (Chunk-date fb)))))) (defun set-deps-by-slurping (source-ch loaded-file-ch) (setf (Code-chunk-callees source-ch) !()) (setf (Code-chunk-depends-on source-ch) !()) (let ((dep-chunk (verify-loaded-chunk-controller loaded-file-ch false)) ;; -- A Code-file-dep (file-pn (Code-file-chunk-pathname source-ch))) ;; In what follows we are setting the derivees of 'dep-chunk'. ;; However, we can't set them directly, because (see chunk.lisp) ;; they are maintained only as the inverse of 'Chunk-basis'. ;; One purpose of the slurp is to reconstruct the derivees of ;; 'dep-chunk'. So we clean the slate first -- (dolist (dc (Chunk-derivees dep-chunk)) (setf (Chunk-basis dc) (remove dep-chunk (Chunk-basis dc)))) (file-slurp file-pn (list scan-depends-on*) (\\ (srm) (let ((readtab (modeline-extract-readtab srm))) (cond (readtab (setf (Code-file-chunk-readtable source-ch) readtab)))))) (loaded-chunk-set-basis loaded-file-ch) (let ((new-controller-derivees (nodup (mapcar (\\ (fc) (verify-loaded-chunk-controller (place-Loaded-chunk fc false) false)) (Code-chunk-depends-on source-ch))))) (dolist (nc-derivee new-controller-derivees) (on-list-if-new dep-chunk (Chunk-basis nc-derivee))) (loaded-chunk-set-basis loaded-file-ch)))) ;;; 'srm' is stream of freshly opened file. Try to get readtable name ;;; from first line, returning false if it can't be found. (defun modeline-extract-readtab (srm) (let ((c (peek-char false srm false eof*))) (cond ((and (not (eq c eof*)) (char= #\;)) ;; Got comment. Try to parse as ;; mode line with readtable spec (string-extract-readtab (read-line srm))) (t false)))) (defun string-extract-readtab (str) (let ((rpos (search "readtable: " (string-downcase str))) (strlen (length str))) (labels ((find-readtable-string () (let ((pos (+ rpos (length "Readtable: ")))) (loop (cond ((and (< pos strlen) (is-whitespace (elt str pos))) (setq pos (+ pos 1))) (t (return)))) (cond ((< pos strlen) (let ((end (position-if (\\ (ch) (or (char= ch #\;) (is-whitespace ch))) str :start pos))) (values pos end))) (t (format *error-output* "Can't find readtable name in mode line ~ ~% \"...~a\"~%" (subseq str rpos)) (values false false))))) (read-readtable-name (pos end) (let ((readtab-name (handler-case (read-from-string str false false :start pos :end end) (error () false)))) (cond ((and readtab-name (symbolp readtab-name)) (setq readtab-name (intern (symbol-name readtab-name) keyword-package*)) (let ((readtab (named-readtable readtab-name))) (or readtab (progn (format *error-output* "Undefined readtable ~s~%" readtab-name) false)))) (t (format *error-output* !"Can't find end of readtable name in ~ mode line~ ~% \"...~a\"~%" (subseq str rpos))))))) (cond (rpos (multiple-value-bind (pos end) (find-readtable-string) (cond (pos (read-readtable-name pos end)) (t false)))) (t false))))) (defvar depends-on-enabled* true) (defvar depends-on-loads-files* false) (defun cl-user::depends-on-disable () (setq depends-on-enabled* false)) ;;; Syntax (depends-on *) ;;; where is [] --floadable-filespecs-- ;;; is (:at