1ambda
1ambda

Reputation: 1155

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?

Upvotes: 2

Views: 494

Answers (3)

Stanislav Kondratyev
Stanislav Kondratyev

Reputation: 664

If you want to compile a binary, you can use a simple Lisp script for that, e. g. (assuming SBCL)

;;;; build.lisp
(require "asdf")
(asdf:operate 'asdf:load-op "helloworld")
(sb-ext:save-lisp-and-die "helloworld"
                          :toplevel helloworld:start
                          :executable t)

Makefile:

all:
    sbcl --load build.lisp

When loading a file, Lisp consequently executes its forms, so by the time it arrives at save-lisp-and-die it already knows about the helloworld package.

There are also dedicated tools for making executables, buildapp and cl-launch. The latter can produce executable scripts and work with saved cores.

UPD

Yes, the executable built in this way will contain the whole Lisp, so 1) it will be huge; 2) having two or more such executables means that you've got (probably superfluous) copies of the whole Lisp.

Another implementation can produce smaller binaries, e. g. CLISP; they say commercial ones are even better.

For Lisp-less target computers this is the only possible solution. On the contrary, if a target computer has a Lisp compiler, there are are two other options: scripts and saved cores.

You can make a shell script invoking sbcl --load <file>or use cl-launch to obtain a sophisticated shell wrapper, which can be as portable as you want it to. SBCL and CLISP support shebang, too.

However, if startup time matters, loading libraries will be a bottleneck, even though they are compiled only once. In this case you can work around by using a custom core, or image. A core can be thought of as a saved state of the Lisp world, and saving a core is the primary task of sb-ext:save-lisp-and-die and its counterparts. Loading a core is fast, and you immediately have all the libraries compiled in it. But then again the size becomes considerable, though not as huge as in the case of a standalone executable. Naturally, one core can be used for several applications. Cl-launch can work with cores as well; in particular, it allows to conveniently define the entry function.

Upvotes: 1

Far&#233;
Far&#233;

Reputation: 960

1- To have a file that can be both compiled and loaded, use eval-when. See my article http://fare.livejournal.com/146698.html

2- I recommend using cl-launch:

cl-launch -sp helloworld -r start -o helloworld -d !

3- If you don't want to waste 60MB in space, but instead can stand half a second to a few seconds of startup latency, use a #!/usr/bin/cl script.

Upvotes: 2

Joshua Taylor
Joshua Taylor

Reputation: 85843

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.

Upvotes: 5

Related Questions