[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