(solved, see the comments)
Recently I've been working on an API that has to interface with an already existing service. Everything seems to be working quite well, and my project is just starting to get large enough that I would see some benefit from throwing things into a package. As this is my first "real" project in CL, I think I'm not fully understanding the packaging/loading mechanisms going on here.
My basic problem is that I have a bunch of code that uses macros to generate functions/classes, interns them into my package, and then exports certain functions/accessors that people will eventually use to interact with the API. If I load the files individually like so:
(load "~/src/lisp/cl-bitcoin/bitcoin.lisp")
(load "~/src/lisp/cl-bitcoin/classes.lisp")
(load "~/src/lisp/cl-bitcoin/functions.lisp")
Everything works well. The functions declared by my macros are interned and exported correctly and I'm able to call them to interact with the API. However, if I attempt to do the following:
(ql:quickload :btc)
Quicklisp tells me that everything was loaded properly - and it seems that most of the loading process happened as I expected since all of my dependencies are loaded and available for use. The problem is that everything related to my package is not available. This includes functions that are exported directly from my package.lisp file. For reference, here are my .asd and package files:
package.lisp
(defpackage #:btc
(:use #:cl)
(:export #:set-connection-parameters
#:reset-rpc-id
#:with-connection-parameters
#:btc-base-err
#:btc-base-id))
btc.asd
(asdf:defsystem #:btc
:serial t
:depends-on (#:drakma
#:flexi-streams
#:cl-json)
:components ((:file "package")
(:file "bitcoin")
(:file "classes")
(:file "functions")))
I feel like I'm missing something fairly obvious here - I've been looking into eval-when
and other related loading functions but haven't been able to figure this out. Can someone explain to me what's going on here?
Thanks for your help.
Edit: Here's what my REPL looks like:
; SLIME 2011-02-04
CL-USER> (ql:quickload :btc)
To load "btc":
Load 1 ASDF system:
btc
; Loading "btc"
..
(:BTC)
CL-USER> (btc:help "getinfo")
Invoking restart: Return to SLIME's top level.
; Evaluation aborted on #<SIMPLE-ERROR #x30200175DF0D>.
CL-USER> ; Reader error: No external symbol named "HELP" in package #<Package "BTC"> .
; No value
CL-USER> (load "/Users/jordan/src/lisp/cl-bitcoin/bitcoin.lisp")
#P"/Users/jordan/src/lisp/cl-bitcoin/bitcoin.lisp"
CL-USER> (load "/Users/jordan/src/lisp/cl-bitcoin/classes.lisp")
#P"/Users/jordan/src/lisp/cl-bitcoin/classes.lisp"
CL-USER> (load "/Users/jordan/src/lisp/cl-bitcoin/functions.lisp")
#P"/Users/jordan/src/lisp/cl-bitcoin/functions.lisp"
CL-USER> (btc:help "getinfo")
#<BTC::BTC-SINGLE #x3020017DBDDD>
CL-USER>
And the code to generate functions:
;;;; package information
(in-package #:btc)
;;; externally visible functions - this class contains the public api for
;;; cl-bitcoin as well as the function building framework that we need to
;;; easily handle the multiple return types that are possible from the
;;; bitcoind server methods
;; function building framework - resolving function return types into
;; specific btc objects (as defined in classes.lisp)
(defun create-btc-obj (fn result err id)
(case fn
((:getbalance :help) (make-btc-single result err id))
(otherwise (error "Unable to parse function ~S to a btc object" fn))))
(defmacro defbtcfun (name &rest args)
(let ((g (gensym)) (result (gensym)) (err (gensym)) (id (gensym)))
`(progn
(defun ,name ,args
(let ((,g (intern (string ',name) :keyword)))
(multiple-value-bind (,result ,err ,id) (get-bitcoind-result ,g ,@args)
(create-btc-obj ,g ,result ,err ,id))))
(export ',name 'btc))))
;; function definitions (each of these functions should have a corresponding case in
;; create-btc-obj above, otherwise a condition will be signaled
(defbtcfun help method)
(defbtcfun getbalance account minconf)
Jordan Kaye posted this as a comment, but never turned it into an answer: