I am writing a (SBCL) Common Lisp program that has the following line at the top of the file:
(defpackage :my-package
(:use :cl :cl-who :hunchentoot :parenscript))
I am running Emacs 25, SBCL and SLIME on MacOS.
Whenever I evaluate the line above, I always get this error at first:
The name "CL-WHO" does not designate any package.
[Condition of type SB-KERNEL:SIMPLE-PACKAGE-ERROR]
Then, I run (ql:quickload "cl-who") and watch the CL-WHO package install. I repeat for the other two packages. Once I have done that, the form can be evaluated successfully.
The problem is that I need to do this each time I restart the Emacs (or Lisp process, which I assume is roughly equivalent in this case). Why is it that when I install something with Quicklisp it is not "remembered" for the next session? Am I doing something wrong?
Is this a problem with configurations or a general misunderstanding of how it is supposed to work?
Then, I run (ql:quickload "cl-who") and watch the CL-WHO package install. I repeat for the other two packages. Once I have done that, the form can be evaluated successfully.
You can quickload more than one system at once:
(ql:quickload '(:cl-who :hunchentoot :parenscript))
[...] each time I restart the Emacs (or Lisp process, which I assume is roughly equivalent in this case).
That's the case here, but notice that you can start a Lisp process from the shell and connect to it from Emacs. In that case, you could exit and restart Emacs without killing the Lisp process.
Start a new REPL from the shell and create a server:
(ql:quickload :swank)
(swank:create-server :port 5000)
And then, call slime-connect from Emacs (with localhost and 5000 for the host and port parameters). That can be used also to monitor a running application.
Why is it that when I install something with Quicklisp it is not "remembered" for the next session?
The system is fetched, compiled and installed on your machine, which explains why the second time you quickload a project, it is faster. But a system is only loaded in your environment when you request it, either with Quicklisp or ASDF.
Define a system
See ยง6. Defining systems with defsystem for an introduction on how to define a system. Suppose you name your system stackoverflow. The simplest way to get started is to create the following file:
~/quicklisp/local-projects/stackoverflow/stackoverflow.asd
which contains:
(defsystem "stackoverflow"
:depends-on ("cl-who" "hunchentoot" "parenscript"))
When you execute (ql:quickload "stackoverflow"), Quicklisp will load all its dependencies.
Instead of loading all the required systems, you only need to load a single one.
Either automatically load this system...
Lisp implementations can execute code at startup. One possible way for this is to execute the code from a configuration file: [.]ccl-init.lisp, .eclrc, .lispworks, .sbclrc (your case), etc. Execute quickload only if Quicklisp itself is available:
#+quicklisp
(ql:quickload "stackoverflow")
... or dump an image with all systems preloaded
You can also load all the required systems, and dump an executable image.
Start a fresh SBCL from a terminal (not from Slime), call (ql:quickload "stackoverflow"), and then:
(sb-ext:save-lisp-and-die "my-env"
:executable t
:compression 9)
(compression is optional)
Then, an executable file named "my-env" should be created in the same directory. Each time you start it, you have a fresh Lisp environment containing the systems you loaded before saving the image.
Related
I have a SBCL package called frosty.
I am using SLIME with emacs. When I close emacs for the day, and then open it the next day, it's as if I am "starting again".
All my quicklisp packages need to be reloaded. And my frosty package needs to be compiled manually.
Is there a way to have the REPL automatically understand the packages/QL packages from the start?
Thanks
You can define a system to group your files as a unit that can be compiled as a whole, and once it is compiled once, the resulting object files are stored in cache and loaded faster the next time (without compilation).
defsystem
At the root of your frosty directory, you can add an ASDF system definition. It contains the following at minimum:
(defsystem "frosty"
;; system dependencies:
;; those are system names, not package names; they often are
;; identical but in general a system can define zero, one or
;; more packages
:depends-on ("alexandria"
"cl-ppcre"
;; etc.
)
;; this declares a list of component, here there is a single entry,
;; namely (:file "frosty") which represents a Lisp source file.
;; If you organized your files differently, e.g. under a src/ directory,
;; you need to add a :pathname option.
;; See https://asdf.common-lisp.dev/asdf.html#The-defsystem-grammar
:components ((:file "frosty"))
)
What we want to do is be able to call:
(ql:quickload "frosty")
And have Quicklisp install all the dependencies and your own system.
In order for quicklisp to know about your .asd file, your project needs to be visible in the quicklisp/local-projects directory. Alternatively, you can choose to link your project under ~/common-lisp/ directory, or any other directory searched by the ASDF central registry (see documentation).
.sbclrc
In your ~/.sbclrc init file, you can add expressions to be evaluated, and in particular you can write (ql:quickload "frosty") here so that it is executed each time SBCL is started.
One of the following will work.
Don't quit the running lisp / emacs. Only an option if you're able to leave processes running overnight on the machine you're using, but that usually is the case now.
Save a core image and restart that: See here and the --core option.
Write a little Lisp source file which just reloads everything, perhaps by using a system definition.
The first two are superficially attractive but have the consequence that, over time, you end up running in a world which you only may be able to recreate and which gradually becomes infested by elves (anyone who has used d machines seriously will remember this problem). The last option means you know how to recreate the state of the world, but you have to pay the cost of doing so.
Once upon a time when computers were slower most people did one of the first two. Now they're quick the last seems preferable in many cases.
I've been playing around with Quicklisp lately, and have this minor problem working with scripts with Shebangs.
Setup
I did the following:
Downloaded quicklisp with curl https://beta.quicklisp.org/quicklisp.lisp -o /tmp/quicklisp.lisp
Installed it with (quicklisp-quickstart:install) while having /tmp/quicklisp loaded in the environment.
Added Quicklisp to init file using (ql:add-to-init-file)
Problem
For a script that needs Quicklisp (specifically ASDF), I can run it with sbcl --load ~/quicklisp/setup.lisp --script start.lisp just fine. However, when I run it as a standalone script with the Shebang /usr/bin/env sbcl --script, it fails with an error saying that it isn't able to find things like UIOP, etc. unless I place the below two lines in the script itself:
(load "~/quicklisp/setup.lisp")
(ql:quickload "asdf")
You can find my stupid experiment here, and the script in question here.
PS: Any pointers would be really helpful.
PPS: I'm sure it's a stupid mistake on my end, forgive me.
In that case you need:
(require :asdf)
TBH, I don't know exactly why. --script equals to --no-sysinit --no-userinit --disable-debugger --end-toplevel-options, so it's a lot we ignore. (thus loading quicklisp's setup.lisp seems required too, because it won't be loaded by your .sbclrc, which is where Quicklisp adds this little snippet)
It's a setting I have needed in other environments, such as a CI.
I would use roswell - which makes standalone scripts available which use Common Lisp code.
I described setting up roswell here. It is super easy.
I describe there system-wide installation of roswell or also how to locally install roswell in ubuntu, mac and windows.
Or you could also directly lookup on roswell's site.
Using roswell would have the advantage that you can use any roswell-installable Common Lisp implementations, which are:
Candidates impls for installation are:
abcl-bin
allegro
ccl-bin
clasp-bin
clasp
clisp
cmu-bin
ecl
mkcl
sbcl-bin
sbcl
sbcl-source
not only sbcl alone.
And roswell allows scripts which are call-able directly from the shell while written in Common Lisp.
From inside roswell $ ros ... commands , quicklisp is available. So $ ros install xxx uses usually quicklisp to install xxx.
Using roswell, you can make any Common Lisp program callable from the bash by a single command - including your script - written in common lisp.
Look at e.g. here:
https://roswell.github.io/Roswell-as-a-Scripting-Environment.html
I am using the Nix package manager under macOS to install much of my software, including dynamic libraries. And I would like to make them accessible to CFFI. That means adding a path to cffi:*foreign-library-directories*. Fine, but how can I do this
globally for my system (should work for packages loaded via Quicklisp, for example)
without loading CFFI every time I start sbcl?
Ignoring the second criterion, I can just add a few lines to ~/.sbclrc:
(ql:quickload "CFFI")
(pushnew (merge-pathnames ".nix-profile/lib/" (user-homedir-pathname))
cffi:*foreign-library-directories*
:test #'equal)
What I am looking for is a way to add the path after CFFI is loaded. A bit like eval-after-load in Emacs Lisp. Is that possible?
I think that you should try to use the mechanisms of the underlying system instead, i. e. on Linux ldconfig (resp. ld.so.conf), on MacOS DYLD_LIBRARY_PATH. The CFFI manual says that the *foreign-library-directories* are only used as a fallback if the system mechanism fails.
I am trying to get Emacs, SLIME and quicklisp to work together properly. I have the following environment set up:
Installed Emacs
Installed SLIME in Emacs
Installed SBCL
Installed quicklisp
Run (quicklisp-quickstart:install) in SBCL
Run (ql:add-to-init-file) in SBCL
Run (ql:quickload "quicklisp-slime-helper") in SBCL
Installed package cl-csv using (ql:quickload "cl-csv") in SBCL
Added (setq inferior-lisp-program "sbcl") to .emacs
Added (load (expand-file-name "~/quicklisp/slime-helper.el")) to .emacs
Restarted everything
I have a test.lisp file that starts with (ql:quickload "cl-csv") to load the package and use it. When I load the file into Emacs and run SLIME, then try to compile it using slime-compile-and-load-file, I get the error in SBCL that Package CL-CSV does not exist.
What have I missed to get these pieces working together properly?
If you compile a file which contains a statement
(ql:quickload "cl-csv")
then this call will be compiled, but not executed. That's what a compiler usually does: it compiles, but does not execute. Thus if you use some Lisp package (a namespace) later in the same file - a package which would be introduced in the system cl-csv - then it might not be present, if you have not loaded it before, by loading the system.
There are two typical solutions to this:
put the loading command in a file which one compiles (optionally) and/or loads before
use EVAL-WHEN with :compile-toplevel, :load-toplevel and :execute
Note that cl-csv is a system, organizing source files. A package is a Lisp namespace for organizing Lisp symbols. A package and a system can have the same name, but they really are two different things.
Calling ql:quickload directly is good for tests and while interacting with your development environment. If you use some systems a lot, you may quickload them inside your initialization file (in your case, ~/.sbclrc).
However, your purpose when programming is generally to develop a new system (a library, or an application, but as far as Common Lisp is concerned, this is a "system"). You generally do not need to have explicit (ql:quickload ...) calls inside your source code. Instead, you define a system where you declare dependencies and your code assumes that the appropriate dependencies are loaded.
Create and enter directory "my-system"
Create and edit "my-system.asd"
(in-package :asdf-user)
(defsystem :my-system
:depends-on (:cl-csv))
Quicklisp can find your system if you link the directory inside ~/quicklisp/local-projects/.
So the next time you restart your Lisp and want to develop on my-system, you can perform (ql:quickload :my-system) which will load all the declared dependencies.
Reinstall quicklisp at the Emacs' SLIME REPL.
The problem is that Emacs Slime (REPL) doesn't know neither whether quicklisp is installed nor where it is installed. All you have to do to fix the problem is to reinstall quicklisp at the Emacs' slime repl. ("quicklisp.lisp" file should be in the SLIME REPL's current working folder.)
M-x slime
CL-USER> (load "quicklisp.lisp")
CL-USER> (quicklisp-quickstart:install)
CL-USER> (ql:add-to-init-file)
CL-USER> (ql:quickload "quicklisp-slime-helper")
e.g.) My "~/.emacs" file:
(use-package slime
:ensure t
:defer t
:init
(setq inferior-lisp-program "clisp")
:config
(slime-setup '(slime-fancy slime-company))
:hook
(slime-mode . slime-company)
(slime-mode . (lambda ()
(load (expand-file-name "D:/util/emacs27/quicklisp/slime-helper.el"))
(add-to-list 'slime-contribs 'slime-fancy)
(add-to-list 'slime-contribs 'inferior-slime))))
(use-package slime-company
:after slime
:bind-keymap
("C-n" . company-select-next)
("C-p" . company-select-previous)
("C-d" . company-show-doc-buffer)
("M-." . company-show-location)
:config (setq slime-company-completion 'fuzzy
slime-company-after-completion 'slime-company-just-one-space))
Then, quicklisp will be reinstalled in the Emacs's HOME directory ("~/"). Now, you have installed the same quicklisp in the (a) Windows 10's HOME and (a) the Emacs' HOME. If you use quicklisp only at the Emacs' Slime REPL, the copy in the Windows 10' HOME is useless, and you can delete it.
I'm working on a project in Common Lisp which makes use of a package installed with quickload. I'm making a bash script in the root of the project which tests if the necessary programs are installed, and if it all checks out, it runs a lisp script which loads my project. I want some way of testing if quicklisp is installed, so that I can have it can ask the user for permission to download and install quicklisp automatically. Is there a way to test for this? Quicklisp is installed within clisp, not as a package on the OS, so using the bash builtins for testing if a program's installed won't work.
From inside Lisp: Quicklisp puts :quicklisp onto the cl:*features* list.
If Quicklisp is already loaded into Lisp, then this symbol is on the *features* list.
To test that:
(member :quicklisp *features*)
In Lisp code you can also use the conditional reader:
#+quicklisp (print "quicklisp installed")
or
#-quicklisp (print "quicklisp not installed")