├── .gitignore ├── LICENSE.txt ├── README.txt ├── default-template ├── README.md ├── application.lisp ├── package.lisp └── system.asd ├── doc ├── LICENSE.txt ├── index.html └── style.css ├── package.lisp ├── quickproject.asd └── quickproject.lisp /.gitignore: -------------------------------------------------------------------------------- 1 | *~ -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Zachary Beane 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | Quickproject creates the skeleton of a Common Lisp project. 2 | 3 | For full documentation, see doc/index.html. 4 | 5 | Quickproject is licensed under the MIT license; see LICENSE.txt for 6 | details. 7 | 8 | For questions or comments, please email Zach Beane . 9 | -------------------------------------------------------------------------------- /default-template/README.md: -------------------------------------------------------------------------------- 1 | # (#| TMPL_VAR name |#) 2 | ### _(#| TMPL_VAR author |#)_ 3 | 4 | This is a project to do ... something. 5 | 6 | ## License 7 | 8 | (#| TMPL_VAR license |#) 9 | (#| TMPL_IF copyright |#) 10 | 11 | (#| TMPL_VAR copyright |#) 12 | (#| /TMPL_IF |#) 13 | -------------------------------------------------------------------------------- /default-template/application.lisp: -------------------------------------------------------------------------------- 1 | ;;;; (#| TMPL_VAR name |#).lisp(#| TMPL_IF copyright |#) 2 | ;; 3 | ;;;; (#| TMPL_VAR copyright |#)(#| /TMPL_IF |#) 4 | 5 | (in-package #:(#| TMPL_VAR name |#)) 6 | -------------------------------------------------------------------------------- /default-template/package.lisp: -------------------------------------------------------------------------------- 1 | ;;;; package.lisp(#| TMPL_IF copyright |#) 2 | ;; 3 | ;;;; (#| TMPL_VAR copyright |#)(#| /TMPL_IF |#) 4 | 5 | (defpackage #:(#| TMPL_VAR name |#) 6 | (:use #:cl)) 7 | -------------------------------------------------------------------------------- /default-template/system.asd: -------------------------------------------------------------------------------- 1 | ;;;; (#| TMPL_VAR name |#).asd(#| TMPL_IF copyright |#) 2 | ;; 3 | ;;;; (#| TMPL_VAR copyright |#)(#| /TMPL_IF |#) 4 | 5 | (asdf:defsystem #:(#| TMPL_VAR name |#) 6 | :description "Describe (#| TMPL_VAR name |#) here" 7 | :author "(#| TMPL_VAR author |#)" 8 | :license "(#| TMPL_VAR license |#)" 9 | :version "0.0.1" 10 | :serial t(#| TMPL_IF depends-on |#) 11 | :depends-on (#| TMPL_VAR dependencies-string |#)(#| /TMPL_IF |#) 12 | :components ((:file "package") 13 | (:file "(#| TMPL_VAR name |#)"))) 14 | -------------------------------------------------------------------------------- /doc/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Zachary Beane 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Quickproject - create a Common Lisp project skeleton 6 | 7 | 8 | 9 |
10 |

Quickproject - create a Common Lisp project skeleton

11 | 12 |

Quickproject is a library for creating a Common Lisp project 13 | skeleton. It is available under a BSD-style license; 14 | see LICENSE.txt for details. 15 | 16 | The latest version is 1.4.1, released on December 26th, 2019. 17 | 18 | 19 |

Download 20 | shortcut: http://www.xach.com/lisp/quickproject.tgz 21 | 22 |

Contents

23 | 24 | 40 | 41 |

Overview

42 | 43 |

Quickproject provides a quick way to make a Common Lisp 44 | project. After creating a project, it extends the ASDF registry so 45 | the project may be immediately loaded. 46 | 47 |

Examples

48 | 49 |
 50 | * (quickproject:make-project #p"~/src/myproject/" :depends-on '(drakma cxml))
 51 | "myproject"
 52 | 
 53 | * (asdf:load-system "myproject")
 54 | load output
 55 | 
56 | 57 |
 58 | * (quickproject:make-project #p"~/src/websnarf/" :name "cl-websnarf")
 59 | "cl-websnarf"
 60 | 
 61 | * (directory #p"~/src/websnarf/*.*")
 62 | (#p"~/src/websnarf/README.txt"
 63 |  #p"~/src/websnarf/package.lisp"
 64 |  #p"~/src/websnarf/cl-websnarf.asd"
 65 |  #p"~/src/websnarf/cl-websnarf.lisp")
 66 | 
67 | 68 |

Dictionary

69 | 70 |

The following symbols are exported from the quickproject 71 | package. 72 | 73 |

74 | 75 |
76 | make-project 77 | 78 | pathname &key 79 | depends-on 80 | author 81 | include-copyright 82 | license 83 | name 84 | template-directory 85 | template-parameters 86 | 87 | => project-name 88 |
89 | 90 |
91 |

Create the skeleton of a Common Lisp project 92 | in directory. If given, name is used as the 93 | name of the project. Otherwise, the name is taken from the last 94 | component in 95 | the pathname-directory 96 | of the pathname. For example, the last directory component 97 | of #p"src/lisp/myproject/" is "myproject". 98 |

The project skeleton consists of the following files: 99 | 100 |

    101 |
  • README.txt 102 |
  • package.lisp — defines a package named after the project 103 |
  • name.asd — defines an ASDF system named after 104 | the project, with a :depends-on list as given in the 105 | function call 106 |
  • name.lisp 107 |
108 | 109 |

If provided, author and license are used 110 | to initialize certain parts of the default files with extra 111 | information. The default values are taken 112 | from *AUTHOR* 113 | and *LICENSE*, respectively. 114 | 115 |

If provided, the boolean argument to include-copyright 116 | will determine whether copyright notices will be printed in the 117 | header of each file. 118 | 119 |

If provided, each file in template-directory is 120 | rewritten 121 | with HTML-TEMPLATE 122 | into the new directory. The options are as follows: 123 | 124 |

    125 |
  • The template markers are (#| and |#) 126 |
  • No escaping is done in template values 127 |
  • Template parameters are created by 128 | appending template-parameters with the lists returned by 129 | calling each entry 130 | in *TEMPLATE-PARAMETER-FUNCTIONS* 131 |
132 | 133 |

After rewriting templates, each element 134 | in *AFTER-MAKE-PROJECT-HOOKS* 135 | is called. 136 | 137 |

After the project has been created, its pathname is added 138 | to ASDF:*CENTRAL-REGISTRY*, so the project is immediately 139 | loadable via ASDF:LOAD-SYSTEM. 140 |

141 |
142 | 143 | 144 |
145 | 146 |
147 | *author* 148 |
149 | 150 |
151 |

This string is used to initialize the :author argument 152 | in the project system definition. The default initial value 153 | is "Your Name <your.name@example.com>". 154 |

155 |
156 | 157 |
158 | 159 |
160 | *include-copyright* 161 |
162 | 163 |
164 |

This variable is used to control whether a copyright notice (with 165 | the author's name and the current year) should appear in the header 166 | of each file.. 167 |

168 |
169 | 170 |
171 | 172 |
173 | *license* 174 |
175 | 176 |
177 |

This string is used to initialize the :description 178 | argument in the project system definition. The default initial 179 | value is "Specify license here". 180 |

181 |
182 | 183 |
184 | 185 |
186 | *template-directory* 187 |
188 | 189 |
190 |

If non-NIL, this variable should be bound to a pathname used as 191 | the default value of template-directory 192 | in MAKE-PROJECT. 193 |

194 |
195 | 196 |
197 | 198 |
199 | default-template-parameters 200 | 201 | 202 | => parameters 203 |
204 | 205 |
206 |

Return a plist with values for :name, :license, 207 | and :author for the current project being created 208 | via MAKE-PROJECT. This 209 | function is in the default value 210 | of *TEMPLATE-PARAMETER-FUNCTIONS*. 211 | 212 |

213 |
214 | 215 | 216 |
217 | 218 |
219 | *template-parameter-functions* 220 |
221 | 222 |
223 |

A list of functions that are called to produce template 224 | parameters when rewriting templates 225 | in MAKE-PROJECT. Each 226 | function is called with no arguments and should produce a list 227 | of keyword/value pairs. The resulting lists are appended 228 | together for use as template parameters 229 | in HTML-TEMPLATE:FILL-AND-PRINT-TEMPLATE. 230 | 231 |

The default value is (default-template-parameters). 232 |

233 |
234 | 235 | 236 |
237 | 238 |
239 | *after-make-project-hooks* 240 |
241 | 242 |
243 |

A list of designators for functions to be called after a 244 | project has been created. Each function should accept one required 245 | argument, the pathname given 246 | to MAKE-PROJECT, and two 247 | keyword arguments, :name and :depends-on, which 248 | correspond to the name of the project (whether explicitly supplied 249 | to MAKE-PROJECT or derived from the pathname) and the 250 | :depends-on argument, 251 | respectively. *default-pathname-defaults* is bound to the 252 | newly created project pathname when hooks are called. 253 |

254 |
255 | 256 |
257 | 258 |
259 | target-exists 260 |
261 | 262 |
263 |

When the target directory exists, this error condition is signaled. If it isn't handled, the overwrite-everything restart 264 | will be available in the debugger. 265 |

266 |
267 | 268 | 269 | 270 |

Feedback

271 | 272 |

For questions or comments about Quickproject, please email me, Zach 273 | Beane <xach@xach.com>. 274 | 275 |

276 |


277 | 278 | 279 | -------------------------------------------------------------------------------- /doc/style.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | margin-left: 4em; 4 | font: medium/1.45em "Lucida Grande", "Trebuchet MS", "Bitstream Vera Sans", Verdana, Helvetica, sans-serif; 5 | 6 | } 7 | 8 | p.copyright { 9 | font-size: 75%; 10 | font-weight: bold; 11 | } 12 | 13 | #content { 14 | margin-left: 4em; 15 | margin-right: 4em; 16 | max-width: 50em; 17 | } 18 | 19 | h2, h3 { 20 | margin-bottom: 0em; 21 | margin-top: 2em; 22 | } 23 | 24 | p.html { 25 | margin-left: 1em; 26 | font-family: monospace; 27 | } 28 | 29 | .type { 30 | color: #999; 31 | } 32 | 33 | .signature { 34 | color: #A01; 35 | margin-left: 1em; 36 | } 37 | 38 | .signature span.result { 39 | color: black; 40 | } 41 | 42 | .signature code.llkw { 43 | font-family: monospace; 44 | } 45 | 46 | .signature span.result var { 47 | color: #A01; 48 | } 49 | 50 | div.signature { 51 | margin-left: 1.5em; 52 | text-indent: -1.5em; 53 | } 54 | 55 | .signature code.name { 56 | font-weight: bold; 57 | } 58 | 59 | .signature code { 60 | font-family: sans-serif; 61 | } 62 | 63 | blockquote.description { 64 | margin-left: 1em; 65 | } 66 | 67 | a[href] { 68 | text-decoration: none; 69 | border-bottom: dotted 1px #CCC; 70 | color: #600; 71 | } 72 | 73 | a:hover[href] { 74 | text-decoration: none; 75 | border-bottom: solid 1px #F00; 76 | color: #F00; 77 | } 78 | 79 | pre.code { 80 | border: solid 1px #DDD; 81 | padding: 0.5em; 82 | background: #EEE; 83 | } -------------------------------------------------------------------------------- /package.lisp: -------------------------------------------------------------------------------- 1 | ;;;; package.lisp 2 | 3 | (defpackage #:quickproject 4 | (:documentation "The Quickproject package.") 5 | (:use #:cl) 6 | (:export #:make-project 7 | #:*after-make-project-hooks* 8 | #:*author* 9 | #:*license* 10 | #:*template-directory* 11 | #:*include-copyright* 12 | #:default-template-parameters 13 | #:*template-parameter-functions* 14 | #:target-exists) 15 | (:shadowing-import-from #:html-template 16 | #:fill-and-print-template 17 | #:*template-start-marker* 18 | #:*template-end-marker*) 19 | (:shadowing-import-from #:cl-fad 20 | #:pathname-as-directory 21 | #:walk-directory 22 | #:file-exists-p 23 | #:delete-directory-and-files)) 24 | 25 | (in-package #:quickproject) 26 | 27 | -------------------------------------------------------------------------------- /quickproject.asd: -------------------------------------------------------------------------------- 1 | ;;;; quickproject.asd 2 | 3 | (asdf:defsystem #:quickproject 4 | :description "Creates the skeleton of a new Common Lisp project" 5 | :version "1.4.1" 6 | :author "Zach Beane " 7 | :license "MIT" 8 | :depends-on (#:cl-fad 9 | #:html-template) 10 | :serial t 11 | :components ((:file "package") 12 | (:file "quickproject"))) 13 | -------------------------------------------------------------------------------- /quickproject.lisp: -------------------------------------------------------------------------------- 1 | ;;;; quickproject.lisp 2 | 3 | (in-package #:quickproject) 4 | 5 | (defvar *name*) 6 | (setf (documentation '*name* 'variable) 7 | "The name of the project currently being created.") 8 | 9 | (defvar *template-directory* (asdf:system-relative-pathname :quickproject "default-template") 10 | "A directory to use as a source of template files.") 11 | 12 | (defvar *depends-on* nil 13 | "Dependencies specified at project creation") 14 | 15 | (defvar *author* 16 | "Your Name " 17 | "Set this variable to your contact information.") 18 | 19 | (defvar *license* 20 | "Specify license here") 21 | 22 | (defvar *include-copyright* nil ; This gives default behavior. 23 | "Include a copyright notice at the top of files.") 24 | 25 | (defun pathname-project-name (pathname) 26 | "Return a project name based on PATHNAME by taking the last element 27 | in the pathname-directory list. E.g. returns \"awesome-project\" for 28 | #p\"src/awesome-project/\"." 29 | (first (last (pathname-directory pathname)))) 30 | 31 | (defun current-year () 32 | (nth-value 5 (decode-universal-time (get-universal-time)))) 33 | 34 | (defvar *after-make-project-hooks* nil 35 | "A list of functions to call after MAKE-PROJECT is finished making a 36 | project. Each function is called with the same arguments passed to 37 | MAKE-PROJECT, except that NAME is canonicalized if 38 | necessary. *DEFAULT-PATHNAME-DEFAULTS* bound to the newly created 39 | project directory.") 40 | 41 | (defun matches-template-p (pathname template) 42 | (and (equal (pathname-name pathname) (pathname-name template)) 43 | (equal (pathname-type pathname) (pathname-type template)))) 44 | 45 | (defun template-pathname->output-name (path) 46 | (if (or (matches-template-p path "system.asd") 47 | (matches-template-p path "application.lisp")) 48 | (make-pathname :name *name* :defaults path) 49 | path)) 50 | 51 | (define-condition target-exists (file-error) 52 | ((template-directory :initarg :template-directory :reader template-directory) 53 | (target-directory :initarg :target-directory :reader target-directory) 54 | (parameters :initarg :parameters :reader parameters) 55 | (if-exists :initarg :if-exists :reader if-exists)) 56 | (:report "The target directory has files in it.") 57 | (:documentation "When a target directory has files in it, throw this.")) 58 | 59 | (defun rewrite-templates (template-directory target-directory parameters 60 | &optional if-exists) 61 | "Treat every file in TEMPLATE-DIRECTORY as a template file; fill it 62 | out using PARAMETERS into a corresponding file in 63 | TARGET-DIRECTORY. The rewriting uses HTML-TEMPLATE. The template start 64 | marker is the string \"\(#|\" and the template end marker is the string 65 | \"|#)\". Template vars are not modified or escaped when written." 66 | (let ((*template-start-marker* "(#|") 67 | (*template-end-marker* "|#)") 68 | (html-template:*warn-on-creation* nil) 69 | (html-template:*string-modifier* 'identity)) 70 | (setf template-directory (truename template-directory) 71 | target-directory (truename target-directory)) 72 | (flet ((rewrite-template (pathname) 73 | (let* ((relative-namestring 74 | (enough-namestring pathname template-directory)) 75 | (target-pathname (template-pathname->output-name 76 | (merge-pathnames relative-namestring 77 | target-directory))) 78 | (if-exists if-exists)) 79 | (ensure-directories-exist target-pathname) 80 | (when (and (null if-exists) 81 | (file-exists-p target-pathname)) 82 | (error 'target-exists 83 | :template-directory template-directory 84 | :target-directory target-directory 85 | :parameters parameters 86 | :if-exists if-exists)) 87 | (when (not (and (eql if-exists :preserve-readme) 88 | (string-equal (pathname-name target-pathname) 89 | "README"))) 90 | (when (eql if-exists :preserve-readme) 91 | (setf if-exists :rename-and-delete)) 92 | (with-open-file (stream 93 | target-pathname 94 | :direction :output 95 | :if-exists if-exists) 96 | (fill-and-print-template pathname 97 | parameters 98 | :stream stream)))))) 99 | (walk-directory template-directory #'rewrite-template)))) 100 | 101 | (defun default-template-parameters () 102 | "Return a plist of :NAME, :LICENSE, and :AUTHOR parameters." 103 | (list :name *name* 104 | :license *license* 105 | :author *author* 106 | :depends-on (mapcar 107 | (lambda (sym) 108 | (list :symbol sym :uninterned (format nil "#:~(~a~)" sym))) 109 | *depends-on*) 110 | :dependencies-string (format nil "(~{#:~(~a~)~^ ~})" *depends-on*) 111 | :copyright (when *include-copyright* 112 | (format nil "Copyright (c) ~D ~A~%" (current-year) *author*)))) 113 | 114 | (defvar *template-parameter-functions* (list 'default-template-parameters) 115 | "A list of functions that return plists for use when rewriting 116 | template files. The results of calling each function are appended 117 | together to pass to FILL-AND-PRINT-TEMPLATE.") 118 | 119 | (defun template-parameters (initial-parameters) 120 | "Return all template parameters returned by calling each element in 121 | *TEMPLATE-PARAMETER-FUNCTIONS*, appended together as a single plist." 122 | (apply 'append initial-parameters 123 | (mapcar 'funcall *template-parameter-functions*))) 124 | 125 | (defun make-project (pathname &key 126 | template-parameters 127 | ((:template-directory *template-directory*) 128 | *template-directory*) 129 | ((:depends-on *depends-on*) *depends-on*) 130 | ((:author *author*) *author*) 131 | ((:license *license*) *license*) 132 | (name (pathname-project-name pathname) name-provided-p) 133 | ((:include-copyright *include-copyright*) *include-copyright*)) 134 | "Create a project skeleton for NAME in PATHNAME. If DEPENDS-ON is provided, 135 | it is used as the asdf defsystem depends-on list." 136 | (check-type *depends-on* list) 137 | (when (pathname-name pathname) 138 | (warn "Coercing ~S to directory" 139 | pathname) 140 | (setf pathname (pathname-as-directory pathname)) 141 | (unless name-provided-p 142 | (setf name (pathname-project-name pathname)))) 143 | (ensure-directories-exist pathname) 144 | (let ((*default-pathname-defaults* (truename pathname)) 145 | (*name* name)) 146 | (let (c) 147 | (restart-case (handler-case 148 | (rewrite-templates *template-directory* 149 | *default-pathname-defaults* 150 | (template-parameters 151 | template-parameters)) 152 | (target-exists (condition) 153 | (setf c condition) 154 | (error condition))) 155 | (overwrite-everything () 156 | :report "Overwrite everything." 157 | (delete-directory-and-files (target-directory c)) 158 | (ensure-directories-exist (target-directory c)) 159 | (rewrite-templates (template-directory c) 160 | (target-directory c) 161 | (parameters c) 162 | :rename-and-delete)))) 163 | (pushnew *default-pathname-defaults* asdf:*central-registry* 164 | :test 'equal) 165 | (dolist (hook *after-make-project-hooks*) 166 | (funcall hook pathname :depends-on *depends-on* :name name 167 | :allow-other-keys t))) 168 | name) 169 | --------------------------------------------------------------------------------