Re: Literate files and ASDF

Richard M Kreuter <[email protected]> Thu, 21 Jun 2007 23:30:02 -0400
Newsgroups gmane.lisp.cclan.general
Message-ID <[email protected]>
C Y <[email protected]> writes:

> In the world of literate programming, there are cases where two or more
> source code files are contained in one literate document, and are
> extracted by specifying the different root nodes corresponding to the
> top level chunk of the respective files.  
>
> In practice, that means things like the following can occur:
>
> (defsystem #:lisp-env
>   :depends-on (:asdf-literate)
>   :components
>   ((:module "ENVIRONMENT-SETUP"
>             :pathname #.(make-pathname :directory '(:relative "src"
>             "lisp-env"))
>             :components
>             ((:cl-pamphlet "axiom-lisp" :chunk "axiom-package.lisp")
>              (:cl-pamphlet "axiom-lisp")))))
>
> While this is a valid literate pamphlet arrangement, it turns out to
> break (for starters) ASDF's method of identifying duplicate component
> definitions.  
>
> If I'm not mistaken, this is a rather serious collision with
> assumptions made in ASDF.

I think it's less an assumption than a design decision: components are
uniquely identified within a module by their names.  But within this
constraint, you can still attach enough additional information to
components to do what you need to do, I think.

Below is an implementation of an extension for working with CL source
files contained in an archive file, that permits the system author to
declare both the archive file and the CL sources contained within it
as components in the system.  The example is contrived in the hope
that it's suitably similar to your literate documents.  There's an
example defsystem instance at the end, showing how to use such a
peculiar ASDF extension.

Hope that helps,
RmK

(defpackage "CL-SOURCE-FILE-ARCHIVE"
  (:use "CL" "ASDF")
  (:export "EXTRACT"
	   "EXTRACT-OP"
           "CL-SOURCE-FILE-ARCHIVE"
	   "CL-SOURCE-FILE-TARBALL"
	   "CL-SOURCE-FILE-IN-ARCHIVE"))

(in-package  "CL-SOURCE-FILE-ARCHIVE")

(defclass cl-source-file-archive (source-file)
  ()
  (:documentation "Abstract class for archives containing CL
                   source files."))

(defgeneric extract (archive-component member-component)
  (:documentation "Extract MEMBER-COMPONENT from
                   ARCHIVE-COMPONENT.  Instantiable subclasses of
                   CL-SOURCE-FILE-ARCHIVE must define a method
                   for this function."))

;; Prevent COMPILE-OP from being PERFORMed on an archive file, by
;; declaring that the COMPILE-OP on an archive has no inputs.  Note
;; that it would be possible to define a PERFORM on COMPILE-OP and an
;; archive, if that were desired.
(defmethod input-files ((op compile-op) (c cl-source-file-archive))
  nil)

(defclass cl-source-file-in-archive (cl-source-file)
  ((archive :initarg :archive :accessor archive-of
	    :documentation "Names the archive file containing the
 	                    CL source file, which archive must be
 	                    a named component in the same module
 	                    as the instance of this class, and
 	                    which archive must contain a
 	                    suitably-named file at the top
 	                    level."))
  (:documentation "Class for all CL source files in archives."))

(defclass extract-op (operation)
  ()
  (:documentation "An operation class for extracting an individual
                   member of an archive."))

(defmethod input-files ((op extract-op) (c cl-source-file-in-archive))
  "The input file of an EXTRACT-OP on a CL-SOURCE-FILE-IN-ARCHIVE
   is the archive file."
  (list (component-pathname
	 (find-component (component-parent c) (archive-of c)))))

(defmethod output-files ((op extract-op) (c cl-source-file-in-archive))
  "The output file of an EXTRACT-OP on a
   CL-SOURCE-FILE-IN-ARCHIVE is the component's file in the
   archive."
  (list (component-pathname c)))

(defmethod component-depends-on ((op compile-op) (c cl-source-file-in-archive))
  (cons (list 'extract-op (component-name c)) (call-next-method)))

(defmethod perform ((op extract-op) (c cl-source-file-in-archive))
  "Extract the CL source file from the archive."
  (extract (find-component (component-parent c) (archive-of c)) c)
  ;; In order to avoid re-extracting or recompiling files, update the
  ;; extracted file's file-write-date, and wait a couple seconds so
  ;; that the file-write-date of the fasl is greater than the
  ;; source's.
  (run-shell-command "touch ~A" (namestring (component-pathname c)))
  (sleep 2))

(defclass cl-source-file-tarball (cl-source-file-archive)
  ()
  (:documentation "Instantiable class for tar archives containing
                   CL source files."))

(defmethod source-file-type ((c cl-source-file-tarball) (s module)) "tar")

(defmethod extract ((archive cl-source-file-tarball)
		    (file cl-source-file-in-archive))
  ;; Portability: GNU and BSD tar support -C, but it's not in SUSv3.
  ;; And of course there's no tar by default on Windows.
  (run-shell-command "tar -x -f ~A -C ~A ~A"
		     ;; Kludge: bogus use of namestring as native
		     ;; filename syntax.
		     (namestring (component-pathname archive))
		     (namestring (component-pathname (component-parent file)))
		     (namestring (component-relative-pathname file))))

;; Assume some archive named arch.tar from with Lisp files foo.lisp
;; and bar.lisp at the top level.  The pristine source tree for this
;; system consists of the .asd file and one .tar file.
#|
 (defpackage #:zzz-system (:use #:cl #:asdf #:cl-source-file-archive))
 (in-package #:zzz-system)

 (defsystem zzz
  :components
  ((:cl-source-file-tarball "arch")
   (:cl-source-file-in-archive "foo" :archive "arch")
   (:cl-source-file-in-archive "bar" :archive "arch")))
|#


-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/