[asdf-devel] dependency bug?

Robert Brown robert.brown at gmail.com
Tue Jan 26 21:38:34 UTC 2010


Thanks very much for the ASDF example code.  I used your technique of
defining a custom operation to teach ASDF how to transform protocol buffer
files into Lisp code, which can then be compiled and loaded.  Usage looks
like this:

(defsystem foobar
  ...
  :components
  (
   ;; Protocol buffer file foo.proto, compiled into proto.lisp.
   (:proto-file "foo")
   ;; Protocol buffer google-protobuf/src/google/protobuf/unittest.lisp,
   ;; compiled into unittest.lisp.  The protobuf file depends on another
   ;; and the protobuf search path for the compiler is also specified.
   (:proto-file "unittest"
    :proto-pathname "google-protobuf/src/google/protobuf/unittest"
    :depends-on ("unittest_import")
    :proto-search-path ("google-protobuf/src/"))
    ))

I had to use one ASDF method that is not exported,
ASDF::COMPONENT-PARENT-PATHNAME.  Perhaps it should be exported.

I also had to override a method, ASDF::COMPONENT-SELF-DEPENDENCIES.  I don't
understand self dependencies, so I may have made a mistake here, but without
the method definition below, ASDF loads both the .lisp and .fasl files of
each PROTO-FILE component.  The method has a comment explaining why this
happens

Anyway, the code is attached below.  Thanks again.

bob

====================


;;; Teach ASDF how to convert protocol buffer definition files into Lisp.


(defparameter *protoc* #p"google-protobuf/src/protoc"
   "Pathname of the protocol buffer compiler.")

(defclass proto-file (cl-source-file)
  ((relative-proto-pathname
    :initarg :proto-pathname
    :initform nil
    :reader proto-pathname
   :documentation "Relative pathname that specifies a protobuf .proto file")
   (search-path
    :initform ()
    :initarg :proto-search-path
    :reader search-path
    :documentation "List containing directories in which the protocol buffer
compiler should search for imported protobuf files."))
  (:documentation "A protocol buffer definition file."))

(defclass proto-to-lisp (operation)
  ()
  (:documentation "An ASDF operation that compiles a file containing protocol
buffer definitions into a Lisp source code."))

(defmethod component-depends-on ((operation compile-op) (component proto-file))
  "Compiling a protocol buffer file depends on compiling dependencies and
converting the protobuf file into Lisp source code."
  (append
   `((compile-op "package" "base" "protocol-buffer" "varint" "wire-format")
     (proto-to-lisp ,(component-name component)))
   (call-next-method)))

(defmethod component-depends-on ((operation load-op) (component proto-file))
  "Loading a protocol buffer file depends on loading dependencies and
converting the protobuf file into Lisp source code."
  (append
   `((load-op "package" "base" "protocol-buffer" "varint" "wire-format")
     (proto-to-lisp ,(component-name component)))
   (call-next-method)))

(defun proto-input (proto-file)
  "Return the pathname of the protocol buffer definition file that must be
translated into Lisp source code for this PROTO-FILE component."
  (if (proto-pathname proto-file)
      ;; Path of the protobuf file was specified with :PROTO-PATHNAME.
      (merge-pathnames
       (make-pathname :type "proto")
       (merge-pathnames (pathname (proto-pathname proto-file))
                        (asdf::component-parent-pathname proto-file)))
      ;; No :PROTO-PATHNAME was specified, to the path of the protobuf
      ;; defaults to that of the Lisp file, but with a ".proto" suffix.
      (let ((lisp-pathname (component-pathname proto-file)))
        (merge-pathnames (make-pathname :type "proto") lisp-pathname))))

(defmethod input-files ((operation proto-to-lisp) (component proto-file))
  (list *protoc* (proto-input component)))

(defmethod output-files ((operation proto-to-lisp) (component proto-file))
  (list (component-pathname component)))

(defmethod perform ((operation proto-to-lisp) (component proto-file))
  (let* ((source-file (proto-input component))
         (output-file (component-pathname component))
         (search-path (cons (directory-namestring source-file)
                            (search-path component)))
         (status
          (run-shell-command "~A --proto_path=~{~A~^:~} --lisp_out=~A ~A"
                             (namestring *protoc*)
                             search-path
                             (directory-namestring output-file)
                             (namestring source-file))))
    (unless (zerop status)
      (error 'compile-failed :component component :operation operation))))

(defmethod asdf::component-self-dependencies ((op load-op) (c proto-file))
  "Remove PROTO-TO-LISP operations from self dependencies.  Otherwise, the
.lisp output files of PROTO-TO-LISP are considered to be input files for
LOAD-OP, which means ASDF loads both the .lisp file and the .fasl file."
  (remove-if (lambda (x)
               (eq (car x) 'proto-to-lisp))
             (call-next-method)))




More information about the asdf-devel mailing list