;;; generate and use autoloads
;;;
;;; Copyright (C) 2000 by Sam Steingold
;;; This is Free Software, covered by the GNU GPL (v2)
;;; See http://www.gnu.org/copyleft/gpl.html
;;;
;;; $Id: autoload.lisp,v 1.10 2001/11/02 22:31:15 sds Exp $
;;; $Source: /cvsroot/clocc/clocc/src/cllib/autoload.lisp,v $

(eval-when (compile load eval)
  (require :cllib-base (translate-logical-pathname "clocc:src;cllib;base"))
  ;; `skip-to-line', `timestamp'
  (require :cllib-fileio (translate-logical-pathname "cllib:fileio")))

(in-package :cllib)

(export '(autoload autoload-generate *autoload-cookie*))

;;;
;;; autoload
;;;

(defparameter *autoload-defun* '
  ;; this will be written into `auto.lisp'
(defun autoload (symb file &optional comment)
  "Declare symbol SYMB to call a function defined in FILE."
  (declare (symbol symb) (type (or simple-string null) comment))
  (export (list symb))
  (unless (fboundp symb)
    (setf (fdefinition symb)
          (lambda (&rest args)
            (setf (documentation symb 'function) nil)
            (fmakunbound symb)
            (format t "; ~s is being autoloaded from `~a'~%" symb file)
            (require file (translate-logical-pathname
                           (concatenate 'string "cllib:" file)))
            (apply (fdefinition symb) args))
          (documentation symb 'function)
          (format nil "Autoloaded (from ~a)~@[:~%~a~]" file comment))))

)

(defcustom *autoload-cookie* simple-string ";;;###autoload"
  "*The cookie preceeding a function to be autoloaded.")

(defgeneric autoload-stream (in out log)
  (:documentation "Generate the autoloads writing into a stream.")
  (:method ((in string) (out stream) (log t))
    (autoload-stream (pathname in) out log))
  (:method ((in sequence) (out stream) (log t))
    (reduce #'+ in :key (lambda (i0) (autoload-stream i0 out log))))
  (:method ((in pathname) (out stream) (log t))
    (with-open-file (ins in :direction :input)
      (format out "~&;;; file ~s, ~:d bytes~%" in (file-length ins))
      (format log "~&~s [~:d bytes] --> " in (file-length ins)) (force-output)
      (loop :while (ignore-errors (skip-to-line ins *autoload-cookie*))
            :count t :into total :do
            (let* ((*package* (find-package :cllib))
                   (form (read ins)) (name (second form))
                   (doc (case (car form)
                          ((defun defsubst)
                           (when (stringp (fourth form)) (fourth form)))
                          (defgeneric
                           (cadr (assoc :documentation (cdddr form))))
                          (t (error 'case-error :proc 'autoload-stream :args
                                    (list 'form (car form) 'defun 'defsubst
                                          'defgeneric))))))
              (format out "(export '(~s))~%(autoload '~s ~s ~s)~%"
                      name name (pathname-name in) doc))
            :finally (progn (format log "~d autoload~:p~%" total) (terpri out)
                            (return total))))))

(defun autoload-generate (in out &optional (log t))
  "Generate the autoloads, indicated by `*autoload-cookie*'."
  (with-open-file (outs out :direction :output :if-exists :supersede
                        :if-does-not-exist :create)
    (format outs ";;; autoloads generated on ~a~%;;; by ~a [~a]~2%~
                  (in-package :cllib)~%(export '(autoload))~2%~s~2%"
            (timestamp) (lisp-implementation-type)
            (lisp-implementation-version) *autoload-defun*)
    (let ((tot (autoload-stream in outs log)))
      (format log "wrote ~d autoload~:p to ~s (~:d bytes)~%"
              tot out (file-length outs))
      tot)))

(provide "autoload")
;;; file autoload.lisp ends here
