This post is meant to be a quick start guide to setup a CL project and writing code.
If you do not have a CL Environment yet, please check out my previous post.
In Clojure you could use lein
or boot
to quickly create the skeleton of a project and you could start coding away. In common lisp we use a library called cl-project
to achieve the same
Quick Note about Project Location
Modern CommonLisp implementations use asdf
to define systems/projects. asdf
by default will look for your projects in the following places:
~/common-lisp
~/.local/share/common-lisp/source
If you do not want to keep all your CL code in one of the above directories, check out this section of the asdf
manual.
Creating a Project
Fire up a REPL (sbcl
), and use quicklisp to pull the cl-project
library first.
(ql:quickload "cl-project")
QuickLisp will load "cl-project"
into your REPL (if it can’t find it locally, it will download it and any dependencies it needs first).
Now, you can use make-project
to create a skeleton layout for your project:
(cl-project:make-project #p"~/common-lisp/first-app" :author "pvik" :license "MIT" :depends-on '(:alexandria))
You specify pathnames in CL with #p followed by the actual path enclosed in double-quotes.
The make-project
function needs the path for you project and also takes some optional keyed parameters. You can find all the options here.
For our first-app
we also included a library dependency alexandria
Now we should have the skeleton structure for our app in ~/common-lisp/first-app
$ cd ~/common-lisp/first-app
$ tree
.
├── first-app.asd
├── README.markdown
├── README.org
├── src
│ └── main.lisp
└── tests
└── main.lisp
2 directories, 5 files
The first-app.asd
defines our project (you can think of this like project.clj
or build.boot
in lein
or boot
) and it should like this:
(defsystem "first-app"
:version "0.1.0"
:author "pvik"
:license "MIT"
:depends-on ("alexandria")
:components ((:module "src"
:components
((:file "main"))))
:description ""
:in-order-to ((test-op (test-op "first-app/tests"))))
(defsystem "first-app/tests"
:author "pvik"
:license "MIT"
:depends-on ("first-app"
"rove")
:components ((:module "tests"
:components
((:file "main"))))
:description "Test system for first-app"
:perform (test-op (op c) (symbol-call :rove :run c)))
cl-project
also included a testing frameworkrove
for our tests. We will ignore this for now, and cover testing in a later blog post.
and our main.lisp
(under src
) should looks like this:
(defpackage first-app
(:use :cl))
(in-package :first-app)
;; blah blah blah.
Writing Some Code
Let us add a function to our main.lisp
(defun hello-world ()
(print "Hello World"))
Let us also update the package definition of first-app
to include (:export :hello-world)
.
Our main.lisp
should look like this:
(defpackage first-app
(:use :cl)
(:export :hello-world))
(in-package :first-app)
(defun hello-world ()
(print "Hello World"))
Running Our Project
Fire up a REPL and require
our project, as long as our project is in a location asdf
can find, you should see something like this:
* (require "first-app")
; compiling file "/home/pvik/common-lisp/first-app/src/main.lisp" (written 09 AUG 2019 11:44:00 AM):
; compiling (DEFPACKAGE FIRST-APP ...)
; compiling (IN-PACKAGE :FIRST-APP)
; compiling (DEFUN HELLO-WORLD ...)
; wrote /home/pvik/.cache/common-lisp/sbcl-1.5.3-linux-x64/home/pvik/common-lisp/first-app/src/main-tmpAAURSO1.fasl
; compilation finished in 0:00:00.001
NIL
Now we can use our export
ed functions like this:
* (first-app:hello-world)
"Hello World"
Generating a Binary
You can use asdf
to generate a binary as well. Add the following lines to you .asd
file:
:build-operation "program-op" ;; leave as is
:build-pathname "first-app.bin"
:entry-point "first-app:hello-world"
under the system definition (defsystem
) for "first-app"
.
Now you can generate the binary from the REPL like so:
* (asdf:make :first-app)
; compiling file "/home/pvik/common-lisp/first-app/src/main.lisp" (written 09 AUG 2019 11:44:00 AM):
; compiling (DEFPACKAGE FIRST-APP ...)
; compiling (IN-PACKAGE :FIRST-APP)
; compiling (DEFUN HELLO-WORLD ...)
; wrote /home/pvik/.cache/common-lisp/sbcl-1.5.3-linux-x64/home/pvik/common-lisp/first-app/src/main-tmpGHU3ALSV.fasl
; compilation finished in 0:00:00.001
[undoing binding stack and other enclosing state... done]
[performing final GC... done]
[defragmenting immobile space... (fin,inst,fdefn,code,sym)=1024+932+18740+19628+24991... done]
[saving current Lisp image into /home/pvik/common-lisp/first-app/first-app.bin:
writing 0 bytes from the read-only space at 0x50000000
writing 432 bytes from the static space at 0x50100000
writing 27492352 bytes from the dynamic space at 0x1000000000
writing 1347584 bytes from the immobile space at 0x50300000
writing 12600064 bytes from the immobile space at 0x52100000
done]
The fist-app.bin
should be in your project root.
You should be setup to start coding away in your new project!