Lisp Project Framework Generators

Project Framework Generators

I looked at two project framework generators: quickproject and cl-project.

Quickproject creates a quick and simple project, particularly good for things that are only going to be one or two source code files in size.

Cl-project goes a bit further and automatically created a test package as well and created source and testing directories. Cl-project also creates a tiny asd package to  avoid interning any symbols in the asd file to cl-user package.  (This was explained to me by the author, Eitarow Fukamachi, I did not figure this out on my own.)

Cl-project pushes the package file down into the source directory. As a personal preference, I prefer the package file (the file that provides the defpackage, export and use declarations) up at the top directory level, next to the asd file. But I think that is personal preference and you can easily move it to one place or the other. In creating the test-package, cl-project automatically pulls in cl-test-more as the testing package.

My wish list for the next iteration for either package is (1) a :testing-framework keyword allowing you to specify the testing framework (and, in the case of quickproject, automatically setting up a testing package and both a source and testing directory), (2) automatically adding a docs directory (a reminder to programmers that library users get a lot out of documentation such as tutorials - do not expect them to simply glance at the source code and understand all the wonderfull work you have done), (3) automatically adding an :export entry for completion (the author uses @export annotation of cl-annot, so he does not use :export the way I use it), (4) a :use keyword allowing you to specify what other packages are used, if any and (5) since it creates a gitignore file, can it take a :version-control keyword and and actually create the initial base, either in git, svn, mercury or darcs. Oh, but we don't have the libraries to do that - so Sabra, what are you going to do about that? Stop complaining and start coding.


Author: Zach Beane of quicklisp fame.

Sample Usage:

(quickproject:make-project #p"/Users/sabra/data/lisp/systems/test-project2/" :name "Test-project2" :author "Sabra Crolleton" :license "MIT" :depends-on '("hunchentoot" "cl-who" "cl-ppcre" "regex" "cl-irregsexp"))

results in 4 files: README.txt Test-project2.asd Test-project2.lisp and package.lisp.

After the project has been created, quickproject also adds its pathname to ASDF:*CENTRAL-REGISTRY*, so the project is immediately loadable via <tt>ASDF:LOAD-SYSTEM

Note the fact that I used initial caps in the name for the project and that was passed into the Name, but the defsystem declarations are all lower case. Both quickproject and cl-project handled this the same way.

The asd file reads as:

(asdf:defsystem #:test-project2 :serial t :description "Describe Test-project2 here" :author "Sabra Crolleton" :license "MIT" :depends-on (#:hunchentoot #:cl-who #:cl-ppcre #:regex #:cl-irregsexp) :components ((:file "package") (:file "test-project2")))

The package file reads as:

(defpackage #:test-project2 (:use #:cl))

The lisp file reads as:

(in-package #:test-project2) ;;; "Test-project2" goes here. Hacks and glory await!

As expected, the README.txt file is just a stub.

Redditor Joekarma posted the following:

"I use quickproject. I added the following to my .sbclrc file to take care of the git repo initialization problem. You'll notice it also sets the README format to markdown--this is my preferred format because it works well with Github.

(setf quickproject:*after-make-project-hooks*
  (lambda (pathname &rest args)
    (declare (ignore args))
    (nix:chdir (fad:pathname-as-directory pathname))
    (rename-file "README.txt" "README.markdown")
    (external-program:run "git"
                          (list "init" "."))
    (external-program:run "git"
                          (list "add" "."))
    (external-program:run "git"
                          (list "commit" "-m" "Initial commit")))))

You'll have to quickload some libraries first: '(:osicat :external-program :quickproject :cl-fad)

I also like to set defaults for the quickproject *licence* and *author* variables."


Author: Eitarow Fukamachi, of clack and caveman (web application framework) fame.


(cl-project:make-project #p"/Users/sabra/data/lisp/systems/test-project1" :name "Test-project1" :author "Sabra Crolleton" :license "MIT" :description "Test Project1" :email "" :depends-on '("hunchentoot" "cl-who" "cl-ppcre" "regex" "cl-irregsexp")) writing /Users/sabra/data/lisp/systems/test-project1/.gitignore writing /Users/sabra/data/lisp/systems/test-project1/README.markdown
writing /Users/sabra/data/lisp/systems/test-project1/ writing /Users/sabra/data/lisp/systems/test-project1/test-project1-test.asd writing /Users/sabra/data/lisp/systems/test-project1/test-project1.asd writing /Users/sabra/data/lisp/systems/test-project1/src/test-project1.lisp writing /Users/sabra/data/lisp/systems/test-project1/t/test-project1.lisp

A few things to note here. First, the system actually creates a .gitignore file which immediately fills itself with various fasl extensions to ignore. Second, it creates two readme files: one is markdown, the other is orgmode rather than text. Third, cl-project creates source and test directories and automatically creates a test package as well as your intended package. Fourth, cl-project accepts a description keyword, which quickproject does not.

Unlike quickproject, cl-project doesn't create the link in ~/quickload/local-projects on new generated projects . If you create your projects under ~/quickload/local-projects, quicklisp will be able to quickload it, but if you put it somewhere else, you should create a link to your project.

The README.markdown file is slightly more than a stub, providing a bit more than quickproject:

# Test-Project1 - Test Project1 ## Usage ## Installation ## Author * Sabra Crolleton ## Copyright Copyright (c) 2012 Sabra Crolleton # License Licensed under the MIT License.

A few things to note in the asd file. First, it provides a version and long-description for the defsystem.The asd file pulls the README.markdown file into the long-description of the asd file. Unlike quickproject, the generated defsystem does not provide :serial t.

#| This file is a part of Test-project1 project. Copyright (c) 2012 Sabra Crolleton |# #| Test Project1 Author: Sabra Crolleton |# (in-package :cl-user) (defpackage Test-project1-asd (:use :cl :asdf)) (in-package :Test-project1-asd) (defsystem Test-project1 :version "0.1" :author "Sabra Crolleton" :email " :license "MIT" :depends-on (:hunchentoot :cl-who :cl-ppcre :regex :cl-irregsexp) :components ((:module "src" :components ((:file "Test-project1")))) :description "Test Project1" :long-description #.(with-open-file (stream (merge-pathnames #p"README.markdown" (or *load-pathname* *compile-file-pathname*)) :if-does-not-exist nil :direction :input) (when stream (let ((seq (make-array (file-length stream) :element-type 'character :fill-pointer t))) (setf (fill-pointer seq) (read-sequence seq stream)) seq))) :in-order-to ((test-op (load-op Test-project1-test))))

cl-project also does not provide a separate package.lisp file. The defpackage information is, rather, put into the named lisp file in the source directory. E.g.:

#| This file is a part of Test-project1 project. Copyright (c) 2012 Sabra Crolleton |# (in-package :cl-user) (defpackage Test-project1 (:use :cl)) (in-package :Test-project1) ;; blah blah blah.

Now looking at the test package created, cl-project creates both a test package and a test directory. As expected, the test package depends on and uses your new package, but it also depends on and uses the package prove. You can, of course, substitute the testing package of your choice. (Why do we have 27 different testing frameworks?)

Finally, cl-project also provides a roswell command line ability to generate your project.