Skip to content
lisp-invocation.lisp 10.9 KiB
Newer Older
;;; Lisp implementations
#+xcvb (module (:build-depends-on ("/asdf" "/xcvb/driver")))
(defpackage :lisp-invocation
  (:use :cl :xcvb-driver)
  (:export
   #:get-lisp-implementation
   #:ensure-path-executable
   #:lisp-implementation-fullname
   #:lisp-implementation-name
   #:lisp-implementation-feature
   #:lisp-implementation-flags
   #:lisp-implementation-eval-flag
   #:lisp-implementation-load-flag
   #:lisp-implementation-arguments-end
   #:lisp-implementation-image-flag
   #:lisp-implementation-image-executable-p
   #:lisp-implementation-standalone-executable
   #:lisp-implementation-argument-control
   #:lisp-implementation-disable-debugger
   #:lisp-implementation-directory-variable
   #:lisp-environment-variable-name
   #:lisp-invocation-arglist
   #:quit-form
   #:save-image-form))

(in-package :lisp-invocation)
(defvar *lisp-implementations* (make-hash-table :test 'equal)
  "Dictionary of known Lisp implementations")

(defstruct (lisp-implementation)
  fullname
  name
  feature
  flags
  eval-flag
  load-flag
  arguments-end
  image-flag
  image-executable-p
  standalone-executable
  argument-control
  disable-debugger
  ;; fasl-type cfasl-type
  quit-format
  dump-format)

(defmacro define-lisp-implementation (key () &rest keys)
  `(setf (gethash ,key *lisp-implementations*)
    (apply #'make-lisp-implementation ',keys)))
(defun get-lisp-implementation (&optional (implementation-type *lisp-implementation-type*))
  (or (gethash implementation-type *lisp-implementations*)
      (error "Unknown Lisp implementation type ~S" implementation-type)))

(define-lisp-implementation :abcl ()
  :fullname "Armed Bear Common Lisp"
  :name "abcl"
  :feature :abcl
  :flags ("--noinform" "--noinit" "--nosystem")
  :eval-flag "--eval"
  :image-executable-p t
  :disable-debugger ("--batch") ;; ???
  :quit-format "(ext:quit :status ~A)"
  :dump-format nil)
(define-lisp-implementation :allegro ()
  :fullname "Allegro CL"
  :name "alisp"
  :feature :allegro
  :flags ("-qq") ; on windows, +c ? On Allegro 5 and earlier, -Q and/or -QQ ?
  :eval-flag "-e"
  :load-flag "-L"
  ; :quit-flags ("-kill")
  :arguments-end "--"
  :image-executable-p nil
  :standalone-executable nil
  :argument-control t
  :disable-debugger ("-batch") ; see also -#D -#C -#!
  :quit-format "(excl:exit ~A :quiet t)"
  :dump-format "(progn (sys:resize-areas :global-gc t :pack-heap t :sift-old-areas t :tenure t) (excl:dumplisp :name ~A :suppress-allegro-cl-banner t))")
(define-lisp-implementation :ccl () ;; demand 1.4 or later.
  ;; formerly OpenMCL, forked from MCL, formerly Macintosh Common Lisp, nee Coral Common Lisp
  ;; Random note: (finish-output) is essential for ccl, that won't do it by default,
  ;; unlike the other lisp implementations tested.
  :name "ccl"
  :feature :clozure
  :flags ("--no-init" "--quiet")
  :eval-flag "--eval" ; -e
  :load-flag "--load"
  :image-flag "--image-name" ; -I
  :image-executable-p t
  :standalone-executable t
  :arguments-end "--"
  :argument-control t ;; must be fixed now, but double-checking needed.
  :disable-debugger ("--batch")
  :quit-format "(let ((x ~A)) (finish-output *standard-output*) (finish-output *error-output*) (ccl:quit x))"
  :dump-format "(save-application ~S :prepend-kernel t)")

(define-lisp-implementation :clisp ()
  :fullname "GNU CLISP"
  :name "clisp"
  :feature :clisp
  :flags ("-norc" "--quiet" "--quiet")
  :eval-flag "-x"
  :load-flag "-i"
  :arguments-end "--"
  :image-executable-p t
  :image-flag "-M"
  :standalone-executable t ;; requires clisp 2.48 or later
  :argument-control t ;; *BUT* even a standalone-executable always accepts --clisp-x and such.
  :disable-debugger ("-on-error" "exit") ;; otherwise, -on-error debug
  :quit-format "(ext:quit ~A)"
  :dump-format "(ext:saveinitmem ~S :quiet t :executable t)")

(define-lisp-implementation :cmucl ()
  :fullname "CMU CL"
  :name "cmucl"
  :feature :cmu
  :flags ("-quiet" "-noinit")
  :eval-flag "-eval"
  :load-flag "-load"
  :arguments-end "--"
  :image-executable-p t
  :image-flag "-core"
  :argument-control t
  :disable-debugger ("-batch")
  :quit-format "(unix:unix-exit ~A)"
  :dump-format "(extensions:save-lisp ~S :executable t)")
(define-lisp-implementation :corman () ;; someone please add more complete support
  :name () ;; There's a clconsole.exe, but what are the options?
(define-lisp-implementation :ecl () ;; demand 10.4.2 or later.
  :fullname "Embeddable Common-Lisp"
  :name "ecl"
  :feature :ecl
  :flags ("-norc")
  :eval-flag "-eval" ; -e
  :load-flag "-load"
  :image-flag nil
  :image-executable-p t
  :arguments-end "--"
  :argument-control t ;; must be fixed now, but double-checking needed.
  :disable-debugger ()
  :quit-format "(si:quit ~A)"
  :dump-format nil) ;; Cannot dump with ECL. Link instead.

(define-lisp-implementation :gcl () ;; Demand 2.7.0, if it is ever released. In ANSI mode.
  :name "gcl" ;; we might export GCL_ANSI=t or something
  :feature :gcl
  :flags ()
  :eval-flag "-eval" ; -e
  :load-flag "-load"
  :image-flag nil
  :image-executable-p t
  :arguments-end "--" ;; -f ?
  :disable-debugger ("-batch")
  :quit-format "(lisp:quit ~A)"
  :dump-format "(progn (si::set-hole-size 500) (si::gbc nil) (si::sgc-on t) (si::save-system ~A))")

(define-lisp-implementation :lispworks ()
  :fullname "LispWorks"
  :name "lispworks" ;; This assumes you dumped a proper image for batch processing...
  :feature :lispworks
  :flags ("-site-init" "-" "-init" "-")
  :eval-flag "-eval"
  :load-flag "-load" ;; Is this what we want? See also -build as magic load.
  :arguments-end nil ; What's the deal with THIS? "--"
  :image-flag nil
  :image-executable-p t
  :standalone-executable t
  :argument-control t
  :disable-debugger ()
  :quit-format "(lispworks:quit :status ~A :confirm nil :return nil :ignore-errors-p t)"
  ;; when you dump, you may also have to (system::copy-file ".../lwlicense" (make-pathname :name "lwlicense" :type nil :defaults filename))
  :dump-format "(lispworks:deliver 'xcvb-driver:resume ~A 0 :interface nil)") ; "(hcl:save-image ~A :environment nil)"

(define-lisp-implementation :lispworks-personal ()
  :fullname "LispWorks Personal Edition"
  :name () ;; In LispWorks Personal, the slave worker executes you!
  :feature :lispworks-personal-edition)

(define-lisp-implementation :mkcl ()
  :fullname "ManKai Common-Lisp"
  :name "mkcl"
  :feature :mkcl
  :flags ("-norc")
  :eval-flag "-eval" ; -e
  :load-flag "-load"
  :image-flag nil
  :image-executable-p t
  :arguments-end "--"
  :argument-control t ;; must be fixed now, but double-checking needed.
  :disable-debugger ()
  :quit-format "(mk-ext:quit :exit-code ~A)"
  :dump-format nil) ;; Cannot dump with ECL. Link instead.

(define-lisp-implementation :sbcl ()
  :fullname "Steel Bank Common Lisp"
  :name "sbcl"
  :feature :sbcl
Peter Keller's avatar
Peter Keller committed
  :flags ("--noinform" "--no-userinit" "--no-sysinit") ;; minimize non-determinism form user's env
  :eval-flag "--eval" ;; Note: SBCL's eval can only handle one form per argument.
  :load-flag "--load"
  :arguments-end "--end-toplevel-options"
  :image-flag "--core"
  :image-executable-p t
  :standalone-executable t ;; requires sbcl 1.0.21.24 or later.
  :argument-control t
  :disable-debugger ("--disable-debugger")
  :quit-format "(sb-ext:quit :unix-status ~A)"
  :dump-format "(sb-ext:save-lisp-and-die ~S :executable t)")

(define-lisp-implementation :scl ()
  :fullname "Scieneer Common Lisp" ; use 1.3.9 or later
  :name "scl"
  :feature :scl
  :flags ("-quiet" "-noinit")
  :eval-flag "-eval"
  :load-flag "-load"
  :arguments-end "--"
  :image-flag "-core"
  :argument-control nil ;; cmucl will always scan all the arguments for -eval... EVIL!
  :disable-debugger ("-batch")
  :quit-format "(unix:unix-exit ~A)"
  :dump-format "(extensions:save-lisp ~S)")

(define-lisp-implementation :xcl ()
  :fullname "XCL"
  :name "xcl"
  :feature :xcl
  :flags ("--no-userinit")
  :eval-flag "--eval"
  :load-flag "--load"
  :arguments-end "--"
  :image-flag nil
  :image-executable-p nil
  :standalone-executable nil
  :disable-debugger ()
  :quit-format "(ext:quit :status ~A)"
  :dump-format nil)
(defun ensure-path-executable (x)
  (when x
    (let ((n (native-namestring x)))
      (cond
	((asdf:absolute-pathname-p x) n)
	((asdf::os-unix-p) (format nil "./~A" n))
	(t n)))))
(defun lisp-environment-variable-name (&key (type *lisp-implementation-type*) prefix)
  (if (eq prefix t) (setf prefix "X"))
  (format nil "~@[~A~]~:@(~A~)" prefix type))
(defun lisp-invocation-arglist
    (&key (implementation-type *lisp-implementation-type*)
	  (lisp-path *lisp-executable-pathname*)
	  (lisp-flags :default)
	  (image-path *lisp-image-pathname*)
Francois-Rene Rideau's avatar
Francois-Rene Rideau committed
          load
	  eval
	  arguments
	  (debugger *lisp-allow-debugger*)
          (cross-compile t))
Francois-Rene Rideau's avatar
Francois-Rene Rideau committed
  (with-slots (name flags disable-debugger load-flag eval-flag
	       image-flag image-executable-p standalone-executable
	       arguments-end argument-control)
      (get-lisp-implementation implementation-type)
    (append
     (when (or (null image-path) (not image-executable-p))
       (list (or
              (when (consp lisp-path) lisp-path)
              (ensure-path-executable lisp-path)
              (getenv (lisp-environment-variable-name
                       :type implementation-type :prefix (when cross-compile "X")))
              name)))
     (when (and image-path (not image-executable-p))
       (list image-flag))
     (when image-path
       (list
        (if image-executable-p
          (ensure-path-executable image-path)
          image-path)))
     (if (eq lisp-flags :default)
	 flags
	 lisp-flags)
     (unless debugger
       disable-debugger)
     (mapcan (if load-flag
                 (lambda (x) (list load-flag x))
                 (lambda (x) (list eval-flag (format nil "(load ~S)" x))))
             (if (listp load) load (list load)))
     (when eval
       (list eval-flag eval))
     (when arguments
       (unless argument-control
	 (error "Can't reliably pass arguments to Lisp implementation ~A" implementation-type))
       (cons arguments-end arguments)))))

;;; Avoiding use of a compiled-in driver in the build process

(defun quit-form (&key code (implementation-type *lisp-implementation-type*))
  "Returns the correct form to quit lisp, based on the value of lisp-implementation.
Can optionally be given a unix status CODE to exit with"
  (format nil (slot-value (get-lisp-implementation implementation-type) 'quit-format)

(defun save-image-form (filepath &optional (implementation-type *lisp-implementation-type*))
  "Returns the lisp form to save the lisp image to the given filepath"
  (format nil (slot-value (get-lisp-implementation implementation-type) 'dump-format)
	  filepath))