Proper way of defining packages using asdf:defsystem and quickproject
Asked Answered
B

1

9

I'm a Lisp beginner trying to understand how to properly use Lisp package system while learning LTK for GUI programming, using SBCL 1.0.55.0.debian and Limp 0.3.4 (and Debian Wheezy if that matters). I have installed ASDF using aptitude package manager (packages cl-asdf & cl-common-lisp-controller), then I installed Quicklisp using the instructions on Quicklisp website (http://www.quicklisp.org/beta/index.html) (not from Debian repository) and then I have installed LTK with (ql:quickload 'ltk) in SBCL console.

hello-1.lisp (directly from LTK tutorial):

(defun hello-1()
    (with-ltk ()
              (let ((b (make-instance ’button
                                      :master nil
                                      :text "Press Me"
                                      :command (lambda ()
                                                       (format t "Hello World!~&")))))
                   (pack b))))

If I compile this straight on in a new SBCL Lisp image, I get the message that WITH-LTK and PACK are undefined functions and 'BUTTON is undefined variable.

So, I found out that I need to load 'ltk first and then use in-package.I to be able to run it, I first have to use (ql:quickload 'ltk) and (in-package :ltk) in SBCL console. However, I still an error message that 'BUTTON is undefined variable.

* (ql:quickload 'ltk)
To load "ltk":
  Load 1 ASDF system:
    ltk
; Loading "ltk"

(LTK)
* (in-package :ltk)

#<PACKAGE "LTK">
* (compile-file "/home/user/code/lisp/hello-1.lisp")

; caught WARNING:
;   undefined variable: ’BUTTON
; 
; compilation unit finished
;   Undefined variable:
;     ’BUTTON
;   caught 1 WARNING condition

; /home/user/code/lisp/hello-1.fasl written
; compilation finished in 0:00:00.009
#P"/home/user/code/lisp/hello-1.fasl"
T
T
* 

Then, as this didn't work out as I wanted, I also attempted to define my own package definitions according to the answers of another question (Problems with ltk (common lisp)), Xach's blog entry "Making a small Lisp project with quickproject and Quicklisp" http://xach.livejournal.com/278047.html?thread=674335 and ASDF Manual (http://common-lisp.net/project/asdf/asdf/The-defsystem-form.html) using quickproject:make-project, but without success. Currently I have the following files:

package.lisp (compiles cleanly if I first (ql:quickload 'ltk) SBCL REPL):

(defpackage :hello-world-ltk-system 
  (:use :cl :asdf :ltk))

hello-world-ltk.asd (compiles cleanly after I have first compiled package.lisp):

(in-package :hello-world-ltk-system)
(asdf:defsystem :hello-world-ltk
  :serial t
  :description "Describe hello-world-ltk here"
  :author "Your Name <[email protected]>"
  :license "Specify license here"
  :depends-on (:cl :asdf :ltk)
  :components ((:file "package")
            (:file "hello-world-ltk")))

hello-world-ltk.lisp (I get compile error The name "HELLO-WORLD-LTK" does not designate any package).

(require 'hello-world-ltk)
(in-package :hello-world-ltk)
(defun hello-world-1 () 
  (with-ltk () 
            (let ((b (make-instance 'button 
                                    :master nil 
                                    :text "Press me!" 
                                    :command (lambda () 
                                                     (format t "Hello world!~&"))))) 
                 (pack b))))          

When I attempt to compile this hello-world-ltk.lisp after successfully compiling package.lisp and hello-world-ltk.asd (which all reside in the same directory) I get the following error:

; compiling (IN-PACKAGE :HELLO-WORLD-LTK)
debugger invoked on a SB-KERNEL:SIMPLE-PACKAGE-ERROR in thread
#<THREAD "initial thread" RUNNING {10029A0FA3}>:
  The name "HELLO-WORLD-LTK" does not designate any package.

Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [ABORT] Exit debugger, returning to top level.

(SB-INT:%FIND-PACKAGE-OR-LOSE "HELLO-WORLD-LTK")
0]
(load "/home/user/code/lisp/hello-world-ltk/hello-world-ltk")

debugger invoked on a SIMPLE-ERROR in thread
#<THREAD "initial thread" RUNNING {10029A0FA3}>:
  attempt to load an empty FASL file:
  "/home/user/code/lisp/hello-world-ltk/hello-world-ltk.fasl"

Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [ABORT] Reduce debugger level (to debug level 1).
  1:         Exit debugger, returning to top level.

(SB-FASL::LOAD-AS-FASL
 #<SB-SYS:FD-STREAM
   for "file /home/user/code/lisp/hello-world-ltk/hello-world-ltk.fasl"
   {1005291233}>
 NIL
 #<unavailable argument>)
0[2]

So, I'm quite lost here with all different ways to define packages, ASDF, Quicklisp, package.lisp, quickproject, asdf:defsystem, require and ql:quickload... quickproject:make-project looks promising, but I really don't know what's still wrong with my source files. I'm looking for a solution that should handle all the compilations and package loadings preferibly in one single command for the whole project and that should be extendable for bigger projects too.

Thank you for any help :)

Bountiful answered 11/4, 2012 at 19:41 Comment(4)
This is not an answer, but a possible resource: I found the chapter on packaging from the book Practical Common Lisp to be helpful in giving me a greater understanding of how these things work. Maybe it'll be helpful for you and/or others who find this question, as well.Homan
I have been reading that chapter too, and I find it useful. However, Practical Common Lisp does not deal with systems created with asdf:defsystem nor the usage of quickproject. Vsevolod's answer (below) helped me understand the difference between packages and systems.Bountiful
indeed, it is only for help with understanding the package side of things. So only part of the puzzle. Glad it's a useful part, though, at least. :)Homan
Another primmer that I found useful was weitz.de/packages.html, the question is answered in the last paragraphDogbane
R
13

The first problem in your code is that you use apostrophe () instead of tick ('). That's why you get undefined variable error, as ’button is read as variable name (it's not quoted).

Now regarding packages and systems. A package is defined with defpackage and it is a collection of symbols, which are used after the in-package form inside a file (or in interactive session). A package has internal and external (exported) symbols, that can be accessed as package::internal-symbol and package:external-symbol respectively. Packages can also import symbols from other packages. If you use-package, you import all its external symbols. While in-package switches the current package to the specified one and you start to define symbols in it (and it is not desirable to do such things in 3rd-party packages, like LTK). So if you want to use LTK symbols, like with-ltk or button, you just need to either use-package LTK or import these symbols from LTK in your defpackage form:

(defpackage :hello-world-ltk-system 
  (:use :cl)
  (:import-from :ltk :with-ltk :button))

or simply import all LTK symbols (with use clause):

(defpackage :hello-world-ltk-system 
  (:use :cl :ltk))

Finally, systems and packages are totally unrelated things. A system is an instance of a class ASDF:SYSTEM, which holds information about physical files and their relations, so that they can be compiled and loaded appropriately. For your hello-world application I would suggest, that you don't bother about systems for now, and write all your code in one file. This file should start with a defpackage form, followed by in-package, and then the rest of your code.

When this file will grow large enough, that you'll see clear parts in it, you can factor out those parts into separate files. Then you'll have to create a system definition file, that will look like this:

(asdf:defsystem :hello-world
  :depends-on (:ltk)
  :serial t
  :components ((:file "package")
               (:file "first")
               (:file "second")
               ...))

The "package.lisp" file will now hold your package definition.

Religiose answered 11/4, 2012 at 20:24 Comment(6)
I moved package definitions to same hello-world-ltk.lisp together with rest of the source and fixed package definitions, as you suggested and fixed package definitions: (defpackage :hello-world-ltk-system (:use :cl) (:import-from :ltk :with-ltk :button :pack) (:export :hello-world-1)) (in-package :hello-world-ltk-system) It still requires (qt:quickload 'ltk) before compiling but now everything works correctly. Thank you for detailed answer.Bountiful
Yes, that's true. I forgot to mention, that you have to somehow load your dependencies (which are systems) before you load your own code. So you have to insert (qt:quickload 'ltk) at the beginning of your file. The same applies to other dependencies. As your application will grow, and you'll use more libraries (which are systems), you'll anyway have a system definition .asd file, in which you'll specify those dependencies.Religiose
(qt:quickload 'ltk) doesn't seem to work, because it does not get evaluated upon compiling, but this works: (eval-when (:compile-toplevel) (ql:quickload 'ltk)) link. I think this is a working solution for tests and small projects.Bountiful
Yes, you're right. You need to wrap it in (eval-when (:compile-toplevel :load-toplevel :execute) ...) to ensure, that its always run, irregardless of the file being compiled or loaded.Religiose
The (qt:quickload...) part should not be needed if things are properly set up. If you load quicklisp, it will register all of its components with ASDF. This means that if you have used (qt:quickload ...) in the past, then the line :depends-on (:ltk) should be enough for ASDF to load the dependency. Otherwise there is something misconfigure in your systemKipkipling
I added (eval-when (:compile-toplevel :load-toplevel :execute) (ql:quickload 'ltk)) for small test and learning programs for which using ASDF and proper systems definitions don't bring that much advantage. I understood that for bigger projects using ASDF without any (ql:quickload ...) code would be the correct way. (There's a typo in previous comments, it should be "ql:quickload", not "qt:quickload", that may confuse someone reading these comments.)Bountiful

© 2022 - 2024 — McMap. All rights reserved.