Search code examples
lispcommon-lispsbclquicklispasdf

Can't use lisp packages defined in a system


I was trying to make executable file using lisp code. But I can't compile lisp file at all because there is no hellowolrd package before loading helloworld system

;; test.lisp
(asdf:load-system :helloworld)

(defun main()
  (helloworld:start))


Of course, I made the helloworld system and put It in ~/quicklisp/local-projects/. helloworld system is successfully loaded without errors.

;; ~/quicklisp/local-projects/helloworld/helloworld.asd
(asdf:defsystem helloworld 
  :version "1.0"
  :components ((:file "package")))

;; ~/quicklisp/local-projects/helloworld/package.lisp 
(defpackage :helloworld
  (:use :common-lisp :asdf)
  (:export :start))

(in-package :helloworld)
(defun start()
  (format t "Welcome, ASDF"))


I want to compile test.lisp without explicit loading. I also tried use-package and defpackage but failed.

;; test.lisp
(asdf:load-system :helloworld)
(use-package :helloworld)

(defun main()
  (helloworld:start))


;; test.lisp
(asdf:load-system :helloworld)

(defpackage :test
  (:use :cl :asdf)
  (:export :main))

(in-package :test)

(defun main()
  (helloworld:start))


How can I use helloworld package defined in helloworld system without loading it? Should I have to make a new system using helloworld system?


Solution

  • In this code, there's something interesting going on:

    ;; test.lisp
    (asdf:load-system :helloworld)
    
    (defun main()
      (helloworld:start))
    

    You can't compile it as a whole because, as you've noted trying to read the symbol hellowworld:start is a problem, because there's no helloworld package yet. To read the symbol, you at least need to have the package defined. But then, why don't we get the same problem with (asdf:load-system :helloworld)? Simply, the ASDF package has already been defined (either the implementation includes it, or you loaded it already, or something else. One thing you could do, then, is to make sure at compilation time that you've already loaded your helloworld system:

    ;; test.lisp
    (eval-when (:compile-toplevel)
      (asdf:load-system :helloworld))
    
    (defun main()
      (helloworld:start))
    

    That should let you compile the file; since you'll evaluate the loading form when compiling, and then the package will be defined by the time you define main.

    Of course, now you'll have a compiled file, but what will happen if you load it into a fresh instance of Lisp where the helloworld system hasn't been loaded? You'll have a problem. So you really want to load that system when you load the file too, and probably if you're just executing forms from the file too (e.g., if you were reading forms one at a time and evaluating them). So you probably want to evaluate that load-system in two more contexts:

    ;; test.lisp
    (eval-when (:compile-toplevel :load-toplevel :execute)
      (asdf:load-system :helloworld))
    
    (defun main()
      (helloworld:start))
    

    All that said, be sure to consider whether this is the most appropriate way to load your system in this case. It may make sense if, for some reason, you're trying to keep all the code in one file, or making a delivery script. If, on the other hand, you're making another ASDF loadable system, then you'd probably just include helloworld as a dependency and let ASDF handle loading the dependencies.