Skip to content
extract-target-properties.lisp 7.29 KiB
Newer Older
;;; Extracting properties from the target lisp implementation
#+xcvb (module (:depends-on ("specials" "lisp-invocation" "string-escape")))

(in-package :xcvb)

(defvar *target-properties* ()
  "Properties of the target system")

(defparameter *target-properties-variables*
Francois-Rene Rideau's avatar
Francois-Rene Rideau committed
  '((*use-cfasls*
     . "(or #+sbcl (and (find-symbol \"*EMIT-CFASL*\" \"SB-C\") t))")
    (*fasl-type*
     . "(pathname-type (compile-file-pathname \"foo.lisp\"))")
    (*target-asdf-version* . "(when(member :asdf2 *features*) (funcall (find-symbol(string :asdf-version):asdf)))")
    (*implementation-identifier*
     . "(when(member :asdf2 *features*) (funcall(find-symbol(string :implementation-identifier):asdf)))")
     ;; lispworks 6.0 on linux/386 has HARP::PC386 HARP::PC386-X, so remove bad features
     . "(remove-if-not 'keywordp *features*)")
    (*target-can-dump-image-p*
Francois-Rene Rideau's avatar
Francois-Rene Rideau committed
     . "(or #+(or allegro ccl clisp cmu gcl (and lispworks (not lispworks-personal-edition)) sbcl scl) t)") ;NO: abcl cormanlisp ecl lispworks-personal-edition mcl xcl
    (*lisp-implementation-directory*
     . "(or #+sbcl (namestring(sb-int:sbcl-homedir-pathname)) #+ccl (namestring(ccl::ccl-directory)))")
    (*target-lisp-image-pathname*
     . "(or #+clozure(namestring ccl:*HEAP-IMAGE-NAME*) #+sbcl(namestring sb-ext:*core-pathname*))")
    (*target-lisp-executable-pathname*
     . "(or #+sbcl (symbol-value (find-symbol \"*RUNTIME-PATHNAME*\" :sb-ext))
            #+(and linux (or sbcl clozure)) (namestring(truename\"/proc/self/exe\"))
            #+(and clisp linux) (read-line (run-program \"readlink\" :arguments (list (format nil \"/proc/~A/exe\" (LINUX:getpid))) :output :stream)))"))
    "alist of variables and how to compute them in the target system")
(defun target-system-features ()
  (get-target-properties)
  *target-system-features*)

(defun get-target-properties ()
  (unless *target-properties*
    (read-target-properties)))

(defun target-feature-p (x)
  (get-target-properties)
  (cond
    ((member x *target-suppressed-features*)
     nil)
    ((member x *target-added-features*)
     t)
    ((member x *target-system-features*)
     t)
    (t
     nil)))
(defun tweak-features-around-eval-string (eval)
  (if (or *target-added-features* *target-suppressed-features*)
      (format nil "(progn~
  ~{~#[~;(pushnew ~S *features*)~:;(dolist(x'(~@{~S~^ ~}))(pushnew x *features*))~]~}~
  ~@[(setf *features*(remove~{~#[~; ~S~:;-if(lambda(x)(member x'(~@{~S~^ ~})))~]~} *features*))~]~
  ~A)"
              *target-added-features* *target-suppressed-features* eval)
      eval))

  (let ((string (extract-target-properties)))
    (log-format-pp 7 "Information from target Lisp:~%~A" string)
    (with-safe-io-syntax (:package :xcvb-user)
      (let ((forms (with-input-from-string (s string) (slurp-stream-forms s))))
        (unless forms
          (user-error "Failed to extract properties from your Lisp implementation"))
        (unless (and (list-of-length-p 1 forms)
                     (consp (car forms)) (eq 'setf (caar forms)))
          (user-error "Malformed target properties:~%~A" string))
        (setf *target-properties* (cdar forms))))
    (loop :for (var value) :on *target-properties* :by #'cddr :do
      (cond
        ((not (member var *target-properties-variables* :key #'car))
         (user-error "Invalid target property ~S" var))
        ((not (and (list-of-length-p 2 value) (eq 'quote (car value))))
         (user-error "Invalid target property value ~S" value))
        (t
         (set var (second value)))))
      (:sbcl (setenv "SBCL_HOME" *lisp-implementation-directory*)))))
(defun extract-target-properties-via-pipe ()
    (let ((command (query-target-lisp-command (target-properties-form))))
      (log-format-pp 8 "Extract information from target Lisp:~% ~S" command)
          (run-program command :output :string)
        (t (c) (user-error "Failed to extract properties from target Lisp:~%~
		       Command:~S~%Error:~%~A~%" command c))))))

(defun extract-target-properties-via-tmpfile ()
  (with-temporary-file (:pathname pn :prefix (format nil "~Axtp" *temporary-directory*))
    (with-safe-io-syntax (:package :xcvb-user)
      (let ((command (query-target-lisp-command
                      (format nil "(with-open-file (*standard-output* ~S :direction :output :if-exists :overwrite :if-does-not-exist :error) ~A)"
                              pn (target-properties-form))))
            (stdout nil))
        (log-format-pp 8 "Extract information from target Lisp:~% ~S" command)
        (flet ((slurp (&key if-does-not-exist)
                 (with-open-file (s pn :direction :input :if-does-not-exist if-does-not-exist)
                   (when s (slurp-stream-string s)))))
                (setf stdout (run-program command :output :string))
                (slurp :if-does-not-exist :error))
            (t (c) (user-error "Failed to extract properties from target Lisp:~%~
			   Condition: ~A~%Command:~S~%~@[stdout:~%~A~%~]~@[Output:~%~A~%~]"
                          c command stdout (slurp)))))))))

(defun extract-target-properties ()
  (case *lisp-implementation-type*
    ((:clisp :gcl :xcl :lispworks)
     (extract-target-properties-via-tmpfile))
    (otherwise
     (extract-target-properties-via-pipe))))
  (with-safe-io-syntax (:package :xcvb-user)
    (format nil "(format t \"(cl:setf~~%~{ xcvb::~(~A~) '~~S~~%~})~~%\"~{ ~A~})"
            (mapcar #'car *target-properties-variables*)
            (mapcar #'cdr *target-properties-variables*))))
(defun get-asdf-pathname ()
  (let ((build (registered-build "/asdf")))
    (when (typep build 'build-module-grain)
      (subpathname (grain-pathname build) "asdf.lisp"))))
(defun query-target-lisp-command (query-string)
  ;; When XCVB is itself compiled with SBCL, and the target is a different SBCL,
  ;; then XCVB's SBCL will export SBCL_HOME which will confuse the target.
  ;; And so, to provide deterministic behavior whatever the build implementation is,
  ;; we unset SBCL_HOME when invoking the target SBCL.
  ;; On Linux, we append ("env" "-u" "SBCL_HOME") to the arglist
  ;; when the target implementation type was sbcl, and
  ;; whichever implementation XCVB itself was using,
  ;; so the semantics of XCVB would not depend on said implementation,
  ;; but -u is apparently a GNU extension not available on other systems
  ;; (most notably BSD systems, including OS X),
  ;; so we'd instead resort to unsetting SBCL_HOME when the XCVB implementation is sbcl
  ;; (in main.lisp)
  ;; Except that sb-posix:putenv is broken before SBCL 1.0.33.21 (SBCL bug 460455).
   ;;#+(and sbcl (or linux cygwin)) '("env" "-u" "SBCL_HOME") ; we now rely on a recent SBCL
    :eval (format nil "(progn (setf *load-verbose* nil *print-readably* nil)~
 (ignore-errors (require \"asdf\"))~
 (handler-bind ((style-warning #'muffle-warning))~
   (ignore-errors
    (funcall (find-symbol(string'oos):asdf) (find-symbol(string'load-op):asdf) :asdf :verbose nil)))~
 ~@[(unless (member :asdf2 *features*) (load ~S))~] ~A (finish-output) ~A)"
                  (get-asdf-pathname) query-string (quit-form :code 0)))))