Confused about ``ql:quickload`` and executable scripts in SBCL
Asked Answered
U

2

15

I've been trying to use Quicklisp packages in an executable script of mine. A (trivial) working example is:

#!/usr/bin/sbcl --script

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload "lisp-unit")) ;as explained by another question

(defpackage :test
  (:use :cl :lisp-unit))

(format t "This is a test.")

After chmoding the file with this code in it (called test.lisp), I tried to execute it. However, I then got the following error message:

Unhandled SB-C::INPUT-ERROR-IN-LOAD in thread #<SB-THREAD:THREAD
                                            "main thread" RUNNING
                                             {1002C16923}>:
  READ error during LOAD:

    Package QL does not exist.

      Line: 4, Column: 15, File-Position: 95

      Stream: #<SB-SYS:FD-STREAM
            for "file /home/koz/Documents/Programming/CL/trees/test.lisp"
            {1002C19A93}>

Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {1002C16923}>
0: ((LAMBDA NIL :IN SB-DEBUG::FUNCALL-WITH-DEBUG-IO-SYNTAX))
1: (SB-IMPL::CALL-WITH-SANE-IO-SYNTAX #<CLOSURE (LAMBDA NIL :IN SB-DEBUG::FUNCALL-WITH-DEBUG-IO-SYNTAX) {1002C2498B}>)
2: (SB-IMPL::%WITH-STANDARD-IO-SYNTAX #<CLOSURE (LAMBDA NIL :IN SB-DEBUG::FUNCALL-WITH-DEBUG-IO-SYNTAX) {1002C2495B}>)
3: (PRINT-BACKTRACE :STREAM #<SB-SYS:FD-STREAM for "standard error" {1002C14CF3}> :START 0 :FROM :INTERRUPTED-FRAME :COUNT NIL :PRINT-THREAD T :PRINT-FRAME-SOURCE NIL :METHOD-FRAME-STYLE NIL)
4: (SB-DEBUG::DEBUGGER-DISABLED-HOOK #<SB-C::INPUT-ERROR-IN-LOAD {1002C1CB93}> #<unavailable argument>)
5: (SB-DEBUG::RUN-HOOK *INVOKE-DEBUGGER-HOOK* #<SB-C::INPUT-ERROR-IN-LOAD {1002C1CB93}>)
6: (INVOKE-DEBUGGER #<SB-C::INPUT-ERROR-IN-LOAD {1002C1CB93}>)
7: (ERROR #<SB-C::INPUT-ERROR-IN-LOAD {1002C1CB93}>)
8: (SB-C:COMPILER-ERROR SB-C::INPUT-ERROR-IN-LOAD :CONDITION #<SB-INT:SIMPLE-READER-PACKAGE-ERROR "Package ~A does not exist." {1002C1CAA3}> :STREAM #<SB-SYS:FD-STREAM for "file /home/koz/Documents/Programming/CL/trees/test.lisp" {1002C19A93}>)
9: (SB-C::READ-FOR-COMPILE-FILE #<SB-SYS:FD-STREAM for "file /home/koz/Documents/Programming/CL/trees/test.lisp" {1002C19A93}> 25 SB-C::INPUT-ERROR-IN-LOAD)
10: (SB-INT:LOAD-AS-SOURCE #<SB-SYS:FD-STREAM for "file /home/koz/Documents/Programming/CL/trees/test.lisp" {1002C19A93}> :VERBOSE NIL :PRINT NIL :CONTEXT "loading")
11: ((FLET SB-FASL::LOAD-STREAM :IN LOAD) #<SB-SYS:FD-STREAM for "file /home/koz/Documents/Programming/CL/trees/test.lisp" {1002C19A93}> NIL)
12: (LOAD #<SB-SYS:FD-STREAM for "file /home/koz/Documents/Programming/CL/trees/test.lisp" {1002C19A93}> :VERBOSE NIL :PRINT NIL :IF-DOES-NOT-EXIST T :EXTERNAL-FORMAT :DEFAULT)
13: ((FLET SB-IMPL::LOAD-SCRIPT :IN SB-IMPL::PROCESS-SCRIPT) #<SB-SYS:FD-STREAM for "file /home/koz/Documents/Programming/CL/trees/test.lisp" {1002C19A93}>)
14: ((FLET #:WITHOUT-INTERRUPTS-BODY-140 :IN SB-IMPL::PROCESS-SCRIPT))
15: (SB-IMPL::PROCESS-SCRIPT "./test.lisp")
16: (SB-IMPL::TOPLEVEL-INIT)
17: ((FLET #:WITHOUT-INTERRUPTS-BODY-89 :IN SAVE-LISP-AND-DIE))
18: ((LABELS SB-IMPL::RESTART-LISP :IN SAVE-LISP-AND-DIE))

unhandled condition in --disable-debugger mode, quitting

I'm unsure what's going on here - if I try to do something similar from the toplevel (i.e., start SBCL, load lisp-unit using ql:quickload, etc), I don't get anything like this.

Unalterable answered 15/9, 2014 at 23:9 Comment(1)
I would make sure in the script that quicklisp is actually loaded. Do it in a separate EVAL-WHEN before using it...Vidrine
A
19

In SBCL, --script does not load your init files. If you want to use Quicklisp, you have to arrange to load it. That usually means something like (load "~/quicklisp/setup.lisp") before using anything related to Quicklisp.

I don't think SBCL and Quicklisp are a great fit for scripting tasks. When I write scripts, I don't normally expect them to fetch stuff from the Internet like ql:quickload does. Quicklisp is also pretty verbose. SBCL's FASL loading is pretty slow. Taken all together it does not make for a very good scripting experience.

I very strongly prefer to load everything I need to use into a Common Lisp session and then call functions to get stuff done. When I can't use that (like for running stuff from cron or from a Makefile), I often use buildapp to make executables.

Albertalberta answered 15/9, 2014 at 23:23 Comment(3)
Can you use buildapp together with Quicklisp? I've got a bunch of things from Quicklisp I want to use (mostly stuff like Alexandria), but I'm not sure how that interacts with buildapp.Unalterable
Yes. There are a few steps involved, but it's possible. I use ql:write-asdf-manifest-file in quicklisp together with --manifest-file in buildapp to make sure they're on the same page. In Makefiles, I usually do that by building the project in a preliminary pass to make sure all the prerequisites are installed, then building it again via buildapp after writing out the manifest file.Albertalberta
I agree that quicklisp (download from the internet) is scary. Do you think the same about using asdf to load a system after download?Divinity
U
1

You should consider using cl-launch, which is made for this purpose. You can enable Quicklisp by passing the option -Q. Your example code would be something like this:

#!/usr/bin/cl -Q -p lisp-unit

(eval-when (:compile-toplevel :load-toplevel :execute)
  (ql:quickload "lisp-unit")) ;as explained by another question
(defpackage :test (:use :cl :lisp-unit))

(defun main (argv) "Code goes here")
Unrighteous answered 16/9, 2014 at 7:57 Comment(1)
I've tried building it by cloning the repo and calling make, but it didn't work, failing with an impressive error message.Unalterable

© 2022 - 2024 — McMap. All rights reserved.