Skip to content
external-commands.lisp 3.88 KiB
Newer Older
;;; Lisp implementations
#+xcvb (module (:depends-on ("macros")))

(in-package :xcvb)

;; ECL is very special, with its link model.
(defun target-ecl-p ()
  (eq *lisp-implementation-type* :ecl))

(defun xcvb-driver-commands-to-shell-token (env commands)
  (with-output-to-string (s)
    (write-string "(xcvb-driver::run " s)
    (dolist (c commands)
      (write-string (text-for-xcvb-driver-command env c) s))
    (write-string ")" s)))

;; Renaming of targets ensures reasonable atomicity
;; whereas CL implementations may create bad invalid stale output files
;; when interrupted in the middle of their computation,
;; -- whether a bad bug is found in the way the user stresses the compiler,
;; or the process is killed in the midst of an unsuccessful debug attempt,
;; or the plug is simply pulled on the computer.
;; This isn't done in the target Lisp side, because
;; CL implementations don't usually do that for you implicitly, and
;; while we could do it explicitly for :compile-lisp,
;; doing it for :create-image would be a pain in at least SBCL,
;; where we would have to fork and wait for a subprocess to SAVE-LISP-AND-DIE
;; which would make the target driver much more complex than desired.

(defvar *renamed-targets* ()
  "alist of targets really desired, and the temporary names under which the XCVB driver commands
will create the desired content. An atomic rename() will have to be performed afterwards.")
(makunbound '*renamed-targets*) ; catch those who try to use it outside of proper context!

(defun register-renamed-target (target tempname)
  (push (cons target tempname) *renamed-targets*)
  t)

(defun rename-target (target tempname)
  (register-renamed-target target tempname)
  tempname)

(defun tempname-target (target)
  (let* ((target (pathname target))
         (tempname (make-pathname :name (strcat (pathname-name target) "__temp")
                                   :defaults target)))
    (rename-target target tempname)))

(define-simple-dispatcher external-commands-for-computation #'external-commands-for-computation-atom)

(defun external-commands-for-computation-atom (env computation-command)
  (declare (ignore env))
  (if (null computation-command)
      nil ;; nothing to do!
      (error "Invalid computation ~S" computation-command)))

(defun external-commands-for-computation (env computation-command)
  ;; We rename secondary targets first, according to the theory that
  ;; in case of interruption, the primary target will be re-built which will
  ;; cause the secondary targets to be implicitly re-built before success.
  (let* ((*renamed-targets* nil)
         (commands (external-commands-for-computation-dispatcher env computation-command)))
    (append commands
	    (loop :for (target . tempname) :in *renamed-targets*
		  :collect (list "mv" (native-namestring tempname) (native-namestring target))))))

(define-external-commands-for-computation :xcvb-driver-command (env keys &rest commands)
  (list
   (lisp-invocation-for env keys (xcvb-driver-commands-to-shell-token env commands))))

(define-external-commands-for-computation :compile-file-directly
    (env fullname &key cfasl lisp-object)
  (list
   (lisp-invocation-for env ()
    (compile-file-directly-shell-token env fullname :cfasl cfasl :lisp-object lisp-object))))

(define-external-commands-for-computation :progn (env &rest commands)
  (loop :for command :in commands
    :append (external-commands-for-computation env command)))

#|
(define-external-commands-for-computation :exec-command (env &rest argv)
  (declare (ignore env))
|#

(define-external-commands-for-computation :make-manifest (env manifest &rest commands)
           :output (pseudo-effective-namestring env manifest)
           :spec (let ((manifest-spec (commands-to-manifest-spec env commands)))
                   (with-safe-io-syntax ()
                     (write-to-string manifest-spec :case :downcase))))))