Qlot (pronounced kyü-'lät
, like culotte) is a project-local library installer using Quicklisp facility. This aims to be like Bundler of Ruby or Carton of Perl.
- Usage
- What Qlot is trying to solve
- Requirements
- Installation
- Optional settings
- Tutorial
- qlfile syntax
- Priorities of distributions
- Working with SLIME
- Working with local git repositories
# "qlfile" of "myapp"
git clack https://github.com/fukamachi/clack.git
github fukamachi/datafly :branch v0.7.x
ql log4cl 2014-03-17
ql mito :upstream
$ cd /path/to/myapp
# Installing libraries project-locally.
$ qlot install
# Updating specific libraries
$ qlot update mito
# Updating all depending libraries of a project.
$ qlot update
# Execute a command with a project-local Quicklisp
$ qlot exec ros -S . run
$ qlot exec clackup app.lisp
We have Quicklisp, the central library registry. It made installation of libraries damn easy.
However, what only you can specify is the month of distribution. Which means you have to use all libraries of the same moment and you cannot use a newer/older version of a library for your project.
"local-projects/" or ASDF configurations may be a solution to this problem, but there are a couple of problems.
-
They are not project-local. If you have multiple projects that use different versions of the same library, it would be a problem.
-
They are difficult to fix the version or to update them. If your project needs to work on other than your machine, for instance on other people's machines or on servers, the version of depending libraries should be the same.
This is what Qlot is trying to solve.
- Roswell or SBCL
- OpenSSL (Unix only)
- [Ubuntu/Debian]
apt install libssl-dev
- [macOS]
brew install openssl
- [Ubuntu/Debian]
- git (for installation from git repositories)
$ curl -L https://qlot.tech/installer | bash
It also requires curl
(or wget
) and tar
.
To uninstall Qlot, run a Qlot uninstaller like:
$ ~/.qlot/qlot/scripts/qlot-uninstaller.sh
If you're already using Roswell, Qlot can be installed by ros install
.
# Install from the Quicklisp dist
$ ros install qlot
# Or, install the latest version from the git repository
$ ros install fukamachi/qlot
# For older Roswell (Not required since v23.10.14.114 or above)
$ ros -e '(ql:quickload :qlot/distify)'
Roswell adds an executable script under $HOME/.roswell/bin
. Make sure the directory exists in $PATH
.
$ which qlot
/Users/fukamachi/.roswell/bin/qlot
Run ros update qlot
to update Qlot.
If Quicklisp is set up on your home directory, Qlot can be installed by ql:quickload
.
$ sbcl --noinform --eval '(ql:quickload (list :qlot :qlot/distify))' --quit
$ sudo printf '#!/bin/sh\nexec sbcl --noinform --non-interactive --eval "(ql:quickload :qlot/cli :silent t)" --eval "(qlot/cli:main)" "$@"\n' > /usr/local/bin/qlot
$ sudo chmod u+x /usr/local/bin/qlot
To update Qlot, run (ql:update-all-dists)
in the REPL.
If you don't use both Roswell and Quicklisp for some reason, Qlot also can be installed manually.
The advantage of this method is no dependencies are required other than sbcl
and OpenSSL
.
# Getting the latest version
$ curl -sL https://api.github.com/repos/fukamachi/qlot/releases/latest | jq -rM '.name'
1.2.16
$ curl https://github.com/fukamachi/qlot/releases/download/1.2.16/qlot-1.2.16.tar.gz -o qlot.tar.gz
$ tar xfz qlot.tar.gz
$ cd qlot
$ scripts/setup.sh
$ sudo printf '#!/bin/sh\nexec '`pwd`'/scripts/run.sh "$@"\n' > /usr/local/bin/qlot
$ sudo chmod u+x /usr/local/bin/qlot
$ git clone --recursive https://github.com/fukamachi/qlot
$ cd qlot
$ scripts/setup.sh
$ sudo printf '#!/bin/sh\nexec '`pwd`'/scripts/run.sh "$@"\n' > /usr/local/bin/qlot
$ sudo chmod u+x /usr/local/bin/qlot
To update Qlot, run git pull && scripts/setup.sh
.
WARNING: Don't add the Qlot source directory to any ASDF loadable directories, such as ~/common-lisp
or ~/quicklisp/local-projects
. ASDF accidentally loads dependencies of Qlot in a REPL even in case you don't need it. (See also ASDF configuration section)
$ docker run --rm -it fukamachi/qlot
ASDF loads any ASD files under a directory ~/common-lisp
including its subdirectories. It is easily understandable and convenient. However, it will lead to a problematic situation which ASDF accidentally loads libraries under ".qlot/" even in case you don't need it.
To avoid the situation, we recommend not to use ~/common-lisp
directory, or add the following lines to your Lisp's init file such as ~/.sbclrc
for SBCL.
;; .sbclrc
(push ".qlot" asdf::*default-source-registry-exclusions*)
(asdf:initialize-source-registry)
Roswell doesn't require this setting since it ignores directories starting with a dot.
$ qlot init
It creates 2 files "qlfile" and "qlfile.lock", and a directory ".qlot/" at the root of your project directory.
qlfile
is a file clarifying the project dependencies. See qlfile syntax for the details.
qlfile.lock
is similar to qlfile
except the library versions are clarified. This will ensure that other developers or your deployment environment use exactly the same versions of libraries you just installed.
Make sure you add qlfile
and qlfile.lock
to your version-controlled repository.
$ git add qlfile qlfile.lock
$ git commit -m 'Start using Qlot.'
$ qlot add <library name> # Add a new library from Quicklisp explicitly
$ qlot add <library name> --upstream # Add an upstream version of a Quicklisp library
$ qlot add <username/repository> # Add a new library from a GitHub repository
You can also edit a qlfile
file directly. See qlfile syntax section to know how to write it. Be sure to run qlot install
to install new dependencies.
You can update the version of dependencies via:
$ qlot update
# Update a specific project
$ qlot update mito
$ qlot update mito sxql
It will also overwrite qlfile.lock
.
qlot install
will install Quicklisp and libraries that are declared in qlfile
project-locally. qlfile.lock
will be used with precedence if it exists.
$ qlot install
$ qlot install /path/to/myapp/qlfile
qlot update
will update the project-local .qlot/
directory using qlfile
.
$ qlot update
# Update a specific project
$ qlot update mito
$ qlot update mito sxql
qlot add
will add a line to qlfile
and invoke qlot install
internally. It replaces an existing line if a library with the same name already exists.
Its arguments are same as the qlfile syntax.
$ qlot add mito # ql mito
$ qlot add mito --latest # ql mito :latest
$ qlot add mito --upstream # ql mito :upstream
$ qlot add fukamachi/datafly # github datafly fukamachi/datafly
$ qlot add fukamachi/datafly --branch stable # github datafly fukamachi/datafly :branch stable
$ qlot add ultralisp egao1980-cl-idna # ultralisp egao1980-cl-idna
qlot remove
will remove libraries from qlfile
and invoke qlot install
internally.
$ qlot remove mito
$ qlot remove mito datafly # can specify multiple names
qlot exec
does following:
- configures ASDF's source registries;
- adds Roswell's
bin
directory to thePATH
environment variable; - executes given command with arguments.
Here are few useful commands:
-
qlot exec ros emacs
- starts Emacs for development. Inferior lisp will use only systems, installed byqlot install
. If you want to use systems from directories other than current and./.qlot/
, then setCL_SOURCE_REGISTRY
variable before startingqlot
. This can be useful in case, if you have development versions of some systems, for example, in~/common-lisp/
directory and want to use them during project development:CL_SOURCE_REGISTRY=~/common-lisp// qlot exec ros emacs
Read more about
CL_SOURCE_REGISTRY
in asdf's documentation. -
qlot exec ros build some-app.ros
- another command, useful, to build a binary from systems, fixed inqlfile
andqlfile.lock
. This way you can be sure that your builds are stable.
NOTE: qlot exec
only affects ros
, sbcl
or Roswell scripts.
Since it's the same as loading .qlot/setup.lisp
, you can still use the project-local Quicklisp by loading it, like:
$ ccl --load .qlot/setup.lisp
"qlfile" is a collection of Quicklisp dist declarations. Each line of that represents a dist.
<source> <project name> [arg1, arg2..]
Currently, <source>
must be one of dist
, ql
, ultralisp
, http
, git
or github
.
ql <project name> <version>
ql <project name>
ql
source will download libraries from Quicklisp official dist.
If you want to use Clack in Quicklisp dist of January 13, 2014, qlfile would be like this.
ql clack 2014-01-13
If :latest
is specified for the version, the latest Quicklisp dist version will be used.
If :upstream
is specified, Qlot downloads the latest code from the upstream git repository.
ultralisp <project name> <version>
ultralisp <project name>
ultralisp
is same as ql
except downloading from Ultralisp.
http <project name> <url> [<file md5>]
http
source will download a tarball.
http yason http://netzhansa.com/yason.tar.gz
git <project name> <repos url>
git <project name> <repos url> :ref <commit ref>
git <project name> <repos url> :branch <branch name>
git <project name> <repos url> :tag <tag name>
git
source will download libraries from a public git repository.
git clack https://github.com/fukamachi/clack.git
You can also specify :ref
, :branch
or :tag
.
git clack https://github.com/fukamachi/clack.git :branch develop
git datafly https://github.com/fukamachi/datafly.git :tag v0.7.4
git cl-dbi https://github.com/fukamachi/cl-dbi.git :ref 54928984e5756e92ba298aae51de8b95a6b0cf4b
Qlot doesn't authenticate itself, but retrieving from private repository can be done via git's SSH key authentication. This means, if the current user can git clone
, Qlot also would be possible to do it.
git myapp git@github.com:somewrite-adtech/myapp
github <username/repository>
github <username/repository> :ref <commit ref>
github <username/repository> :branch <branch name>
github <username/repository> :tag <tag name>
github
source is similar to git
, but it is specifically for GitHub. As it uses GitHub API and tarballs GitHub serves, it doesn't require "git" command.
github fukamachi/datafly
github fukamachi/datafly :branch develop
local <project name> <directory path>
Add a directory to the ASDF's source registry.
local rove ~/Programs/lib/rove
dist <dist name> <distribution URL> [<dist version>]
dist
allows to use a custom Quicklisp dist, like Ultralisp.
dist quicklisp http://beta.quicklisp.org/dist/quicklisp.txt
dist ultralisp http://dist.ultralisp.org/
If multiple distributions provide the same library, lower ones would take priority over higher ones.
SLIME is the most popular development environment in Common Lisp. However, its REPL always loads the global Quicklisp, not the project-local one.
Here's quick steps to start project-local REPL with SLIME for each text editor:
- Add lem-project/micros to
.qlot/local-projects
.
$ git clone https://github.com/lem-project/micros .qlot/local-projects/micros
- Add the following function to
~/.lem/init.lisp
.
(define-command slime-qlot-exec (directory) ((prompt-for-directory (format nil "Project directory (~A): " (buffer-directory))))
(let ((command (first (lem-lisp-mode/implementation::list-roswell-with-qlot-commands))))
(when command
(lem-lisp-mode:run-slime command :directory directory))))
-
Relaunch the Lem, or reload the init file with
M-x lisp-load-file RET ~/.lem/init.lisp
. -
Invoke
M-x slime-qlot-exec RET /path/to/project/
.
- Add one of the following functions to
init.el
.
(defun slime-qlot-exec (directory)
(interactive (list (read-directory-name "Project directory: ")))
(slime-start :program "sbcl"
:program-args `("--no-userinit" "--no-sysinit" "--load" ,(concat (file-name-as-directory directory) ".qlot/setup.lisp"))
:directory directory
:name 'qlot
:env (list (concat "PATH=" (mapconcat 'identity exec-path ":")))))
(defun slime-qlot-exec (directory)
(interactive (list (read-directory-name "Project directory: ")))
(slime-start :program "qlot"
:program-args '("exec" "ros" "-S" "." "run")
:directory directory
:name 'qlot
:env (list (concat "PATH=" (mapconcat 'identity exec-path ":")))))
NOTE for sly users: Define sly-qlot-exec
as the above code replaced slime-start
with sly-start
.
- Relaunch the Emacs.
- Invoke
M-x slime-qlot-exec RET /path/to/project/
.
- Install vlime/vlime.
- Add the following code to your Vim/Neovim init.vim.
let g:vlime_cl_use_terminal = v:true
let s:vlime_path = '/path/to/vlime'
function! VlimeBuildServerCommandFor_qlot(vlime_loader, vlime_eval)
return ["qlot", "exec", "ros", "run",
\ "--load", s:vlime_path . "/lisp/load-vlime.lisp",
\ "--eval", vlime_eval]
endfunction
function! VlimeQlotExec()
call vlime#server#New(v:true, get(g:, "vlime_cl_use_terminal", v:false), v:null, "qlot")
endfunction
- Relaunch the Vim/Neovim.
- Change the directory by
:cd /path/to/project/
and invoke:call VlimeQlotExec()
.
PROJECT_ROOT/.qlot/local-projects
can be used for local git repositories. Symbolic links are also accessible in Qlot environment.
- Eitaro Fukamachi (e.arrows@gmail.com)
See LICENSE.txt.
The license of bundled Quicklisp installer can be found at quicklisp/LICENSE.txt.