diff options
author | Sean Whitton <spwhitton@spwhitton.name> | 2023-03-01 13:59:12 -0700 |
---|---|---|
committer | Sean Whitton <spwhitton@spwhitton.name> | 2023-03-01 14:21:37 -0700 |
commit | 6808af3becc9986d5dafdcc4412c13a3961a7e64 (patch) | |
tree | 85545c84b73d9a0d310b7c73331045a356185046 | |
parent | cc1835ff316910b8dd641dec091b41e8b5c198cd (diff) | |
download | consfigurator-6808af3becc9986d5dafdcc4412c13a3961a7e64.tar.gz |
doc/: start generating API documentation from docstrings
Signed-off-by: Sean Whitton <spwhitton@spwhitton.name>
-rw-r--r-- | .gitignore | 12 | ||||
-rw-r--r-- | consfigurator.mk | 4 | ||||
-rw-r--r-- | doc/GNUmakefile | 28 | ||||
-rw-r--r-- | doc/Makefile | 9 | ||||
-rw-r--r-- | doc/combinator.rst.in | 2 | ||||
-rw-r--r-- | doc/conf.py | 2 | ||||
-rw-r--r-- | doc/connection.rst.in (renamed from doc/connections.rst) | 0 | ||||
-rw-r--r-- | doc/data.rst.in (renamed from doc/data.rst) | 0 | ||||
-rw-r--r-- | doc/deployment.rst.in | 2 | ||||
-rw-r--r-- | doc/host.rst.in (renamed from doc/hosts.rst) | 0 | ||||
-rw-r--r-- | doc/image.rst.in | 2 | ||||
-rw-r--r-- | doc/index.rst | 43 | ||||
-rw-r--r-- | doc/property.rst.in (renamed from doc/properties.rst) | 0 | ||||
-rw-r--r-- | doc/propspec.rst.in (renamed from doc/propspecs.rst) | 0 | ||||
-rw-r--r-- | doc/tutorial/disk_image.rst | 4 | ||||
-rw-r--r-- | doc/tutorial/os_installation.rst | 4 | ||||
-rw-r--r-- | doc/util.rst.in | 2 | ||||
-rw-r--r-- | emacs/Makefile | 8 | ||||
-rw-r--r-- | src/package.lisp | 2 | ||||
-rw-r--r-- | src/property.lisp | 152 | ||||
-rw-r--r-- | src/property/chroot.lisp | 7 | ||||
-rw-r--r-- | src/property/cron.lisp | 4 | ||||
-rw-r--r-- | src/property/disk.lisp | 42 | ||||
-rw-r--r-- | src/reader.lisp | 4 |
24 files changed, 277 insertions, 56 deletions
@@ -1,3 +1,15 @@ /doc/_build/ /emacs/put-forms.el /emacs/consfigurator.el + +/doc/connection.rst +/doc/property.rst +/doc/propspec.rst +/doc/host.rst +/doc/combinator.rst +/doc/deployment.rst +/doc/data.rst +/doc/image.rst +/doc/property/*.rst +/doc/util.rst +/doc/util/*.rst diff --git a/consfigurator.mk b/consfigurator.mk new file mode 100644 index 0000000..ad72d65 --- /dev/null +++ b/consfigurator.mk @@ -0,0 +1,4 @@ +SBCL = sbcl --disable-debugger --eval '(require "asdf")' --eval \ + '(let ((asdf:*user-cache* "/tmp") \ + (asdf:*central-registry* (list (truename "..")))) \ + (asdf:load-system "consfigurator"))' diff --git a/doc/GNUmakefile b/doc/GNUmakefile new file mode 100644 index 0000000..53d9b35 --- /dev/null +++ b/doc/GNUmakefile @@ -0,0 +1,28 @@ +include ../consfigurator.mk + +LISP = $(wildcard ../src/property/*.lisp ../src/util/*.lisp) \ + ../src/connection.lisp ../src/property.lisp ../src/propspec.lisp \ + ../src/host.lisp ../src/combinator.lisp ../src/deployment.lisp \ + ../src/data.lisp ../src/image.lisp ../src/util.lisp +PAGES = $(patsubst ../src/%,%,$(LISP:lisp=rst)) + +.PHONY: all +all: html info + +.PHONY: html info +html info: $(PAGES) conf.py $(wildcard *.rst */*.rst) + sphinx-build -M $@ . _build + +$(PAGES) &: $(wildcard *.rst.in */*.rst.in) $(LISP) + $(SBCL) --eval "(mapc #'consfigurator::build-manual-rst \ + uiop:*command-line-arguments*)" --quit $(PAGES) + +.PHONY: clean +clean: + rm -rf _build + rm -f $(PAGES) + +# property.lisp contains the definition of BUILD-MANUAL-RST. +.SECONDEXPANSION: +%.rst: $$(wildcard $$*.rst.in) ../src/$$(*D)/$$(*F).lisp ../src/property.lisp + $(SBCL) --eval '(consfigurator::build-manual-rst "$@")' --quit diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index 50d9481..0000000 --- a/doc/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/doc/combinator.rst.in b/doc/combinator.rst.in new file mode 100644 index 0000000..d54fcdf --- /dev/null +++ b/doc/combinator.rst.in @@ -0,0 +1,2 @@ +Property combinators +==================== diff --git a/doc/conf.py b/doc/conf.py index 984073d..99da27e 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -18,7 +18,7 @@ # -- Project information ----------------------------------------------------- project = 'Consfigurator' -copyright = '2020-2022, Sean Whitton' +copyright = '2015-2018, 2020-2023, Sean Whitton, 2021-2022 David Bremner' author = 'Sean Whitton' # The full version, including alpha/beta/rc tags diff --git a/doc/connections.rst b/doc/connection.rst.in index b958139..b958139 100644 --- a/doc/connections.rst +++ b/doc/connection.rst.in diff --git a/doc/data.rst b/doc/data.rst.in index ae657c6..ae657c6 100644 --- a/doc/data.rst +++ b/doc/data.rst.in diff --git a/doc/deployment.rst.in b/doc/deployment.rst.in new file mode 100644 index 0000000..f61b83b --- /dev/null +++ b/doc/deployment.rst.in @@ -0,0 +1,2 @@ +Deployments +=========== diff --git a/doc/hosts.rst b/doc/host.rst.in index af18d0d..af18d0d 100644 --- a/doc/hosts.rst +++ b/doc/host.rst.in diff --git a/doc/image.rst.in b/doc/image.rst.in new file mode 100644 index 0000000..bbe9f3a --- /dev/null +++ b/doc/image.rst.in @@ -0,0 +1,2 @@ +Remote Lisp images +================== diff --git a/doc/index.rst b/doc/index.rst index 96430cf..5c2f0c8 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -3,21 +3,48 @@ Consfigurator user's manual .. toctree:: :maxdepth: 1 - :caption: Contents: introduction installation - tutorial/disk_image - tutorial/os_installation - connections - properties - hosts - propspecs - data pitfalls news ideas +.. toctree:: + :maxdepth: 1 + :caption: Tutorials + + tutorial/disk_image + tutorial/os_installation + +.. toctree:: + :maxdepth: 1 + :caption: Core + + connection + property + propspec + host + combinator + deployment + data + image + +.. toctree:: + :maxdepth: 1 + :caption: Properties packages + :glob: + + property/* + +.. toctree:: + :maxdepth: 1 + :caption: Utilities packages + :glob: + + util + util/* + Indices and search ================== diff --git a/doc/properties.rst b/doc/property.rst.in index 265e8f8..265e8f8 100644 --- a/doc/properties.rst +++ b/doc/property.rst.in diff --git a/doc/propspecs.rst b/doc/propspec.rst.in index bec4e6e..bec4e6e 100644 --- a/doc/propspecs.rst +++ b/doc/propspec.rst.in diff --git a/doc/tutorial/disk_image.rst b/doc/tutorial/disk_image.rst index d4b7a20..7001474 100644 --- a/doc/tutorial/disk_image.rst +++ b/doc/tutorial/disk_image.rst @@ -1,5 +1,5 @@ -Tutorial: building disk images -============================== +Building disk images +==================== In this tutorial we will show you what properties you need to use to build bootable disk images. diff --git a/doc/tutorial/os_installation.rst b/doc/tutorial/os_installation.rst index d89ccbf..9e6b077 100644 --- a/doc/tutorial/os_installation.rst +++ b/doc/tutorial/os_installation.rst @@ -1,5 +1,5 @@ -Tutorial: OS installation -========================= +OS installation +=============== Consfigurator implements a number of methods for installing operating systems. diff --git a/doc/util.rst.in b/doc/util.rst.in new file mode 100644 index 0000000..6d39466 --- /dev/null +++ b/doc/util.rst.in @@ -0,0 +1,2 @@ +``CONSFIGURATOR`` exported utilities +==================================== diff --git a/emacs/Makefile b/emacs/Makefile index 2226d2c..d15384c 100644 --- a/emacs/Makefile +++ b/emacs/Makefile @@ -1,7 +1,5 @@ -LOAD = '(let ((asdf:*user-cache* "/tmp") \ - (asdf:*central-registry* (list (truename "..")))) \ - (asdf:load-system "consfigurator"))' +include ../consfigurator.mk consfigurator.el: consfigurator.el.in - sbcl --disable-debugger --eval '(require "asdf")' --eval $(LOAD) \ - --eval '(consfigurator::dump-properties-for-emacs "$<" "$@")' --quit + $(SBCL) --eval '(consfigurator::dump-properties-for-emacs "$<" "$@")'\ + --quit diff --git a/src/package.lisp b/src/package.lisp index 6b72a1b..157bcfb 100644 --- a/src/package.lisp +++ b/src/package.lisp @@ -16,6 +16,7 @@ #:slurp-stream-string #:subprocess-error #:stripln + #:println #:unix-namestring #:parse-unix-namestring #:pathname-directory-pathname @@ -58,6 +59,7 @@ #:slurp-stream-string #:subprocess-error #:stripln + #:println #:unix-namestring #:parse-unix-namestring #:pathname-directory-pathname diff --git a/src/property.lisp b/src/property.lisp index 3aebe75..968b205 100644 --- a/src/property.lisp +++ b/src/property.lisp @@ -203,6 +203,152 @@ (when indent (setf (get sym 'indent) indent))))) +(defun docstring-to-rst (docstring) + ;; Unsurprisingly this gets a lot of cases wrong, so turned off for now. + ;; The block capitals already make them easy to pick out. + ;; (setq docstring + ;; (re:regex-replace-all #?/(?<!\S)[A-Z:]*[A-Z]+[A-Z:-]*(?!\w)/ + ;; docstring "``\\&``")) + + ;; Format indented code examples for rST. + (do* ((lines (lines docstring) (cdr lines)) + (line (car lines) (car lines)) + indented accum) + ((null lines) + (stripln (unlines (nreverse accum)))) + (acond + ((and (or indented (and accum (zerop (length (car accum))))) + (strip-prefix " " line)) + (unless indented + (unless (string= "" (car accum)) + (push "" accum)) + (push (if (or (char= #\( (first-char line)) + (string-prefix-p "'(" line)) + "::" ".. code-block:: none") + accum) + (push "" accum) + (setq indented t)) + (push (strcat " " it) accum)) + ((and indented (zerop (length line)) + (string-prefix-p " " (cadr lines))) + (push " " accum)) + (t (push line accum) + (setq indented nil))))) + +(defparameter *defining-form-info-alist* + (flet ((docgetf (l) (getf l :documentation)) + (docassoc (l) (cadr (assoc :documentation l))) + (numbers-to-functions (field) + (cond ((and (numberp field) (zerop field)) + (constantly nil)) + ((numberp field) + (lambda (form) + (values (nth field form) t))) + ((functionp field) + (lambda (form) + (values (funcall field form) t))) + (t field)))) + (mapcar + (lambda (entry) (mapcar #'numbers-to-functions entry)) + `((define-constant "Constant" 0 ,(compose #'docgetf #'cdddr)) + (defgeneric "Generic function" 2 ,(compose #'docassoc #'cdddr)) + (defclass "Class" 0 ,(compose #'docassoc #'cddddr)) + (define-condition "Condition class" 0 ,(compose #'docassoc #'cddddr)) + + (defvar "Variable" 0 3) + (defparameter "Variable" 0 3) + (defun "Function" 2 3) + (defmacro "Macro" 2 3) + (defprop "Property" 3 4) + (defpropspec "Property" 3 4) + (defproplist "Property" 3 4) + (define-simple-error "Simple error" 4 3) + (define-function-property-combinator "Fn. prop. combinator" 2 3))))) + +(defun build-manual-rst + (target-rst &aux (target-rst + (ensure-directories-exist + (ensure-pathname target-rst + :want-file t :want-relative t))) + (input-rst (make-pathname :type "rst.in" + :defaults target-rst)) + (input-lisp + (make-pathname :type "lisp" + :defaults (merge-pathnames target-rst + #P"../src/")))) + "Write TARGET-RST manual page based on input .rst.in and .lisp files." + (with-safe-io-syntax () + (with-open-file (*standard-input* input-lisp :if-does-not-exist :error) + (with-open-file (*standard-output* target-rst :direction :output + :if-exists :supersede) + (let* ((first-form (read)) + (second-form (read)) + (package (if (eql (car first-form) 'in-package) + (find-package (cadr first-form)) + (error "First form of ~S is not IN-PACKAGE." + input-lisp))) + (package-exts (aprog1 (make-hash-table :test #'eq) + (do-external-symbols (s package) + (setf (gethash s it) t)))) + (input-rst-p (file-exists-p input-rst)) + ;; We cannot have an anonymous first subsection of the API + ;; reference with Sphinx. + (section-heading "General")) + (labels ((println-heading (heading char) + (loop initially (println heading) + repeat (length heading) do (write-char char) + finally (terpri))) + (println-entry + (form &aux + (type (and (gethash (cadr form) package-exts) + (assoc (car form) + *defining-form-info-alist*))) + (name (and type (abbreviate-consfigurator-package + (cadr form))))) + (when type + (when section-heading + (terpri) + (println-heading section-heading #\~) + (setq section-heading nil)) + (terpri) + (println-heading (format nil "~A: ``~A``" + (cadr type) name) + #\^) + (multiple-value-bind (params paramsp) + (funcall (caddr type) form) + (when paramsp + (format t "~%``~((~A~{ ~A~})~)``~&" + name + (mapcar #'ensure-car + (ldiff params + (member '&aux params)))))) + (when-let ((docstring (funcall (cadddr type) form))) + (when (stringp docstring) + (terpri) + (println (docstring-to-rst docstring))))))) + (unless (equal second-form + '(named-readtables:in-readtable :consfigurator)) + (error "Second form of ~S is not our IN-READTABLE." input-lisp)) + (if input-rst-p + (with-open-file (input input-rst) + (copy-stream-to-stream input *standard-output*) + (terpri)) + (println-heading (format nil "``~A``" (package-name package)) + #\=)) + (println-heading "API reference" #\-) + (let ((*package* package) + (*readtable* (named-readtables:find-readtable + :consfigurator.without-read-eval))) + (loop (handler-case + ;; Read a line or a form depending on what's next. + (if (char= #\( (peek-char t)) + (println-entry (read)) + ;; Don't print section heading yet in case it + ;; doesn't contain any defns for exported symbols. + (awhen (strip-prefix ";;;; " (read-line)) + (setq section-heading it))) + (end-of-file () (return))))))))))) + (defmacro with-*host*-*consfig* (&body forms) `(progv `(,(intern "*CONSFIG*")) `(,(propspec-systems (host-propspec *host*))) @@ -632,11 +778,11 @@ PATH if PATH already has the specified CONTENT and MODE." (defmacro with-change-if-changes-file ((file) &body forms) "Execute FORMS and yield :NO-CHANGE if FILE does not change. -Since stat(1) is not POSIX, this is implemented by calling `ls -dlL' and +Since stat(1) is not POSIX, this is implemented by calling ``ls -dlL`` and cksum(1), and seeing if any of the information reported there, except for the number of links, has changed. Thus, you should not use this macro to detect -changes in properties which will change the file but not the output of `ls --dlL' and cksum(1)." +changes in properties which will change the file but not the output of +``ls -dlL`` and cksum(1)." (with-gensyms (before) `(let* ((,before (ls-cksum ,file)) (result (progn ,@forms))) diff --git a/src/property/chroot.lisp b/src/property/chroot.lisp index 3e790d3..068c86b 100644 --- a/src/property/chroot.lisp +++ b/src/property/chroot.lisp @@ -100,7 +100,7 @@ ,(propspec-props propspec)))))) (defproplist deploys :lisp (root host &optional additional-properties) - "Like DEPLOYS with first argument `((:chroot :into ,root)), but disable + "Like DEPLOYS with first argument ```((:chroot :into ,root))``, but disable starting services in the chroot, and set up access to parent hostattrs." (:desc #?"Subdeployment of ${root}") (consfigurator:deploys @@ -108,8 +108,9 @@ starting services in the chroot, and set up access to parent hostattrs." (%make-child-host (union-propspec-into-host host additional-properties)))) (defproplist deploys-these :lisp (root host properties) - "Like DEPLOYS-THESE with first argument `((:chroot :into ,root)), but disable -starting services in the chroot, and set up access to parent hostattrs." + "Like DEPLOYS-THESE with first argument ```((:chroot :into ,root))``, but +disable starting services in the chroot, and set up access to parent +hostattrs." (:desc #?"Subdeployment of ${root}") (consfigurator:deploys `((:chroot :into ,root)) diff --git a/src/property/cron.lisp b/src/property/cron.lisp index f60ecb8..2a3e7fe 100644 --- a/src/property/cron.lisp +++ b/src/property/cron.lisp @@ -21,10 +21,10 @@ ;;; A number of techniques here are from Propellor's Cron properties module. (defpropspec system-job :posix (desc when user shell-command) - "Installs a cronjob running SHELL-COMMAND as USER to /etc/cron.*. + "Installs a cronjob running SHELL-COMMAND as USER to ``/etc/cron.*``. DESC must be unique, as it will be used as a filename for a script. WHEN is either :DAILY, WEEKLY, :MONTHLY or a string formatted according to crontab(5), -e.g. \"0 3 * * *\". +e.g. ``0 3 * * *``. The output of the cronjob will be mailed only if the job exits nonzero." (:desc #?"Cronned ${desc}") diff --git a/src/property/disk.lisp b/src/property/disk.lisp index 4c650f9..f7b7b3d 100644 --- a/src/property/disk.lisp +++ b/src/property/disk.lisp @@ -998,7 +998,7 @@ filesystems will be incrementally updated when other properties change." (options host device-file &key chroot) "Install HOST to the DISK:PHYSICAL-DISK accessible at DEVICE-FILE. **THIS PROPERTY UNCONDITIONALLY FORMATS DISKS, POTENTIALLY DESTROYING DATA, - EACH TIME IT IS APPLIED.** +EACH TIME IT IS APPLIED.** Do not apply in DEFHOST. Apply with DEPLOY-THESE/HOSTDEPLOY-THESE. @@ -1046,7 +1046,7 @@ the host's actual physical disk upon first boot." (defpropspec volumes-installed-for :lisp (options host &key chroot leave-open) "Install HOST to its volumes, as specified using DISK:HAS-VOLUMES. **THIS PROPERTY UNCONDITIONALLY FORMATS DISKS, POTENTIALLY DESTROYING DATA, - EACH TIME IT IS APPLIED.** +EACH TIME IT IS APPLIED.** Do not apply in DEFHOST. Apply with DEPLOY-THESE/HOSTDEPLOY-THESE. @@ -1233,28 +1233,28 @@ specifications. This becomes the VOLUME-CONTENTS of the VOLUME. The following keys in INITARGS are handled specially: - - :VOLUME-SIZE -- may be a string like \"100M\", \"2G\", \"1T\" which will - be converted into a whole number of mebibytes. \"M\", \"G\", and \"T\" - are currently supported. + - :VOLUME-SIZE -- may be a string like \"100M\", \"2G\", \"1T\" which will + be converted into a whole number of mebibytes. \"M\", \"G\", and \"T\" + are currently supported. Example usage: - (volumes - (physical-disk - (partitioned-volume - ((partition - :partition-typecode #xef00 - (fat32-filesystem - :volume-size \"512M\" - :mount-point #P\"/boot/efi\")) - (partition - (luks-container - (lvm-physical-volume - :volume-group \"vg_laptop\")))))) - (lvm-logical-volume - :volume-group \"vg_laptop\" - :volume-label \"lv_laptop_root\" - (ext4-filesystem :mount-point #P\"/\")))" + (volumes + (physical-disk + (partitioned-volume + ((partition + :partition-typecode #xef00 + (fat32-filesystem + :volume-size \"512M\" + :mount-point #P\"/boot/efi\")) + (partition + (luks-container + (lvm-physical-volume + :volume-group \"vg_laptop\")))))) + (lvm-logical-volume + :volume-group \"vg_laptop\" + :volume-label \"lv_laptop_root\" + (ext4-filesystem :mount-point #P\"/\")))" (labels ((parse (spec) (unless (listp spec) diff --git a/src/reader.lisp b/src/reader.lisp index 39314be..240750b 100644 --- a/src/reader.lisp +++ b/src/reader.lisp @@ -21,3 +21,7 @@ (:merge :standard) (:dispatch-macro-char #\# #\? #'cl-interpol:interpol-reader) (:dispatch-macro-char #\# #\> #'cl-heredoc:read-heredoc)) + +(named-readtables:defreadtable :consfigurator.without-read-eval + (:merge :consfigurator) + (:dispatch-macro-char #\# #\. (constantly nil))) |