Skip to content
commands.lisp 6.9 KiB
Newer Older
;;; Shell command-line interface for XCVB

#+xcvb
(module (:depends-on ("specials" "macros")))

(in-package :xcvb)

(declaim (optimize (speed 2) (safety 3) (compilation-speed 0) (debug 3)))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Command Spec ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defmacro define-option-spec (name options)
  `(eval-now
     (defparameter ,name ,options)))

(defmacro define-command (name-keys (names args options
                                           short-description description
                                           &optional ignorable)
                          &body body)
  (let* ((name-keys (ensure-list name-keys))
         (name (first name-keys))
         (keys (rest name-keys))
         (option-spec-var (conc-symbol '+ name '-option-spec+))
         (option-spec-val (eval options))
         (option-args (mapcar (lambda (option) (intern (string-upcase (caar option))))
                              option-spec-val))
         (ignore (case ignorable ((ignore ignorable) ignorable) (otherwise 'ignore)))
         (ignored (if (consp ignorable) (set-difference option-args ignorable) option-args)))
    `(progn
       ,@(when options ;; make it available immediately as well as in further commands
           (setf (symbol-value option-spec-var) option-spec-val)
           `((define-option-spec ,option-spec-var ,options)))
       (register-command-properties
        ',name :names ',names :option-spec ,(when options option-spec-var)
        :short-description ,short-description :description ,description
        ,@(when keys `(:handle-command-line-keys ',keys)))
       (register-command ',name)
       (defun ,name
           ,(append (or args '(&key)) option-args)
         ,@(when ignorable `((declare (,ignore ,@ignored))))
         ,@body))))

(defun register-command-properties
    (name &rest keys &key
     names option-spec short-description description handle-command-line-keys)
  "Register the properties associated to a function as a command"
  (declare (ignore names option-spec short-description description handle-command-line-keys))
  (setf (get name 'command) keys))

(defun registered-command-properties (name)
  "Registered properties associated to a function as a command"
  (get name 'command))

(defvar *commands* (cons () (make-hash-table :test 'equal))
  "A registry of command-line accessible commands, as a cons cell
whose car is a list of command function symbols in reverse,
and whose cdr is a hash-table mapping command-line names to command symbols.
Each symbol must have registered 'command properties.")

(defun commands () ;; associated with current package. Meh.
  (symbol-value (conc-symbol '*commands*)))

(defun register-command (command)
  (let ((properties (registered-command-properties command))
        (commands (commands)))
    (pushnew command (car commands))
    (dolist (name (getf properties :names))
      (setf (gethash name (cdr commands)) command))))

(defun lookup-command (command-name)
  "Lookup the command spec for the given command name, or return nil if the
given command name is invalid."
  (let ((command (gethash command-name (cdr (commands)))))
    (cons command (registered-command-properties command))))

(defun interpret-command (arguments)
  (let* ((command (first arguments))
         (args (rest arguments))
         (registered (lookup-command command))
         (fun (first registered))
         (properties (rest registered))
         (option-spec (getf properties :option-spec))
         (keys (getf properties :handle-command-line-keys)))
    (cond
      (option-spec
       (apply 'handle-command-line
              option-spec fun
              :command-line args :name command
              keys))
      (fun
       (funcall fun args))
      ((not command)
       (errexit 2 "~:@(~A~) requires a command -- try '~:*~A help'." *program*))
      (t
       (errexit 2 "Invalid ~:@(~A~) command ~S -- try '~2:*~A help'." *program* command)))))

;;;; Common Option Specs

(define-option-spec +xcvb-program-option-spec+
  '((("xcvb-program" #\X) :type string :optional t
     :documentation "specify where to find the xcvb binary")
    (("required-xcvb-version" #\V) :type string :optional t
     :documentation "specify a minimum xcvb version")))
(define-option-spec +source-registry-option-spec+
  '((("source-registry" #\S) :type string :optional t
     :documentation "override your source-registry")))

(define-option-spec +lisp-implementation-option-spec+
  '((("lisp-implementation" #\l) :type string :initial-value "sbcl" :documentation "specify type of Lisp implementation")
    (("lisp-binary-path" #\p) :type string :optional t :documentation "specify path of Lisp executable")
    (("define-feature" #\D) :type string :list t :optional t :documentation "define a CL into the target")
    (("undefine-feature" #\U) :type string :list t :optional t :documentation "undefine a CL from the target")))

(define-option-spec +cfasl-option-spec+
  '((("disable-cfasl" #\d) :type boolean :optional t :documentation "disable the CFASL feature")))

(define-option-spec +verbosity-option-spec+
  '((("verbosity" #\v) :type integer :initial-value 5 :documentation "set verbosity")
    (("debugging" #\Z) :type boolean :optional t :initial-value nil :documentation "debug")))

(define-option-spec +setup-option-spec+
  '((("setup" #\s) :type string :optional t :documentation "specify a Lisp setup file")))

(define-option-spec +base-image-option-spec+
  '((("use-base-image" #\B) :type boolean :optional t :initial-value t :documentation "use a base image")))

(define-option-spec +profiling-option-spec+
  '((("profiling" #\P) :type boolean :optional t :documentation "profiling")))

(define-option-spec +workspace-option-spec+
  '((("workspace" #\W) :type string :optional t :documentation "specify workspace directory")
    (("cache" #\C) :type string :optional t :documentation "specify cache directory")
    (("object-cache" #\O) :type string :optional t :documentation "specify object-cache directory")))

(define-option-spec +install-option-spec+
  '((("install-prefix") :type string :optional t :documentation "specify install directory prefix")
    (("install-program") :type string :optional t :documentation "specify program install directory")
    (("install-configuration") :type string :optional t :documentation "specify configuration install directory")
    (("install-data") :type string :optional t :documentation "specify data install directory")
    (("install-library") :type string :optional t :documentation "specify library install directory")
    (("install-image") :type string :optional t :documentation "specify image install directory")
    (("install-lisp") :type string :optional t :documentation "specify lisp code install directory")))

(define-option-spec +build-option-spec+
  '((("build" #\b) :type string :optional nil :documentation "specify what build to process")))

(define-option-spec +multi-build-option-spec+
  '((("build" #\b) :type string :list t :optional nil :documentation "specify what builds to process")))