license templates

This commit is contained in:
2026-06-28 02:31:34 +03:00
parent dd1f3745de
commit 509f4a44bb
5 changed files with 163 additions and 6 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) {{current-year}} {{project-author}}
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+5 -1
View File
@@ -1,8 +1,12 @@
(asdf:defsystem "lspack" (defsystem "lspack"
:author "Emre Akan" :author "Emre Akan"
:description "Quickstart Common Lisp projects" :description "Quickstart Common Lisp projects"
:license "GPL-3.0" :license "GPL-3.0"
:version "0.0.1" :version "0.0.1"
:depends-on ("uiop")
:pathname "src/" :pathname "src/"
:serial t
:components ((:file "package") :components ((:file "package")
(:file "util")
(:file "license")
(:file "starter"))) (:file "starter")))
+109
View File
@@ -0,0 +1,109 @@
(in-package :lspack)
(defparameter *licenses* (make-hash-table))
(defparameter *license-default-filename* "LICENSE")
(defclass <license> ()
((template-name :initarg :license-template-name
:accessor license-template-name)
(file-name :initarg :license-file-name
:accessor license-file-name
:initform *license-default-filename*)
(writer :initform nil
:accessor license-writer)))
(defun get-license (key)
(gethash key *licenses*))
(eval-always
(defun set-license (key value)
(setf (gethash key *licenses*) value)))
(eval-always
(defmacro deflicense (keys template-name &optional file-name)
(let ((sym (gensym "LICENSE")))
`(let ((,sym (make-instance '<license>
:license-template-name ,template-name
,@(unless (null file-name)
`(:license-file-name file-name)))))
,@(loop for key in (uiop:ensure-list keys)
collect `(set-license ,key ,sym))))))
(deflicense :mit "mit.txt")
(defmethod license-find-template ((license <license>))
(with-slots (template-name) license
(or (uiop:file-exists-p
(asdf:system-relative-pathname
:lspack
(uiop:merge-pathnames* template-name #P"data/")))
(uiop:file-exists-p
(uiop:xdg-data-home #P"lspack/data/" template-name)))))
(defmethod license-read-template ((license <license>))
(uiop:if-let ((name (license-find-template license)))
(uiop:read-file-string name)))
(defun template-find-args (template)
(loop with i = 0
while (< i (length template))
for arg = (uiop:if-let ((begin (search "{{" template :start2 i)))
(uiop:if-let ((end (search "}}" template :start2 begin)))
(prog1 (list (subseq template (+ begin 2) end) begin (+ end 2))
(setf i (+ end 2)))))
while arg
collect arg))
(defparameter *template-args* (make-hash-table :test 'equal))
(defun define-template-arg (argument-name expander)
(setf (gethash argument-name *template-args*) expander))
(defun get-arg-expander (arg)
(gethash arg *template-args*))
(define-template-arg "current-year"
(lambda (p s)
(declare (ignore p))
`(format ,s "~D" (get-current-year))))
(define-template-arg "project-author"
(lambda (p s)
`(format ,s "~A" (project-author ,p))))
(defun generate-argument-writer (arg project-sym stream-sym)
(uiop:if-let ((arg-expander (get-arg-expander arg)))
(funcall arg-expander project-sym stream-sym)))
(defun generate-writer-body (args project-sym template-sym stream-sym
&optional (prev-end 0))
(if (null args)
`((write-string ,template-sym ,stream-sym :start ,prev-end))
(let ((arg (car args)))
(destructuring-bind (name begin end) arg
(cons `(write-string ,template-sym ,stream-sym :start ,prev-end
:end ,begin)
(cons (generate-argument-writer name project-sym stream-sym)
(generate-writer-body (cdr args) project-sym template-sym
stream-sym end)))))))
(defun template-generate-writer (template-string)
(let ((args (template-find-args template-string))
(template-sym (gensym "TEMPLATE"))
(project-sym (gensym "PROJECT"))
(stream-sym (gensym "STREAM")))
`(lambda (,project-sym ,stream-sym)
(let ((,template-sym ,template-string))
,@(generate-writer-body args project-sym template-sym stream-sym)))))
(defmethod license-generate-writer ((license <license>))
(uiop:if-let (template (license-read-template license))
(setf (license-writer license)
(compile nil (template-generate-writer template)))))
(defmethod license-ensure-writer ((license <license>))
(with-slots (writer) license
(if (functionp writer)
writer
(license-generate-writer license))))
+19 -5
View File
@@ -1,8 +1,8 @@
(in-package :lspack) (in-package :lspack)
(defparameter *default-author* "") (defparameter *default-author* "")
(defparameter *default-license* "") (defparameter *default-license* nil)
(defparameter *default-pathname* "") (defparameter *default-pathname* "src")
(defparameter *default-version* "0.0.0") (defparameter *default-version* "0.0.0")
(defclass <project> () (defclass <project> ()
@@ -24,7 +24,7 @@
(license :initarg :project-license (license :initarg :project-license
:accessor project-license :accessor project-license
:initform *default-license* :initform *default-license*
:type string) :type (or keyword null))
(version :initarg :project-version (version :initarg :project-version
:accessor project-version :accessor project-version
:initform *default-version* :initform *default-version*
@@ -40,14 +40,14 @@
:project-name "my-project" :project-name "my-project"
:project-author "name" :project-author "name"
:project-description "a project" :project-description "a project"
:project-pathname "")) :project-license :mit))
(defmethod project-defsystem-write ((project <project>) stream) (defmethod project-defsystem-write ((project <project>) stream)
(with-slots (name author description license version pathname) (with-slots (name author description license version pathname)
project project
(fresh-line stream) (fresh-line stream)
(write-char #\( stream) (write-char #\( stream)
(format stream "asdf:defsystem ~S" name) (format stream "defsystem ~S" name)
(format stream "~& :author ~S" author) (format stream "~& :author ~S" author)
(format stream "~& :description ~S" description) (format stream "~& :description ~S" description)
(format stream "~& :license ~S" license) (format stream "~& :license ~S" license)
@@ -99,9 +99,23 @@
(format out "(defpackage :~a~% (:use :cl))~%" name)) (format out "(defpackage :~a~% (:use :cl))~%" name))
(format *error-output* "~&Created ~S" path)))) (format *error-output* "~&Created ~S" path))))
(defmethod project-license-create ((project <project>))
(let ((license (get-license (project-license project))))
(unless (null license)
(let ((writer (license-ensure-writer license))
(license-file (uiop:merge-pathnames* (license-file-name license)
(project-root project))))
(unless (null writer)
(with-open-file (out license-file :direction :output
:if-exists :supersede
:if-does-not-exist :create)
(funcall writer project out))
(format *error-output* "~&Created ~S" license-file))))))
(defmethod project-start ((project <project>)) (defmethod project-start ((project <project>))
(project-ensure-root project) (project-ensure-root project)
(project-defsystem-create project) (project-defsystem-create project)
(project-license-create project)
(project-ensure-source-directory project) (project-ensure-source-directory project)
(project-create-package project)) (project-create-package project))
+9
View File
@@ -0,0 +1,9 @@
(in-package :lspack)
(defmacro eval-always (&body body)
`(eval-when (:compile-toplevel :load-toplevel :execute)
,@body))
(defun get-current-year ()
(let ((now (get-universal-time)))
(nth-value 5 (decode-universal-time now))))