Newer
Older
Francois-Rene Rideau
committed
;;; Extracting properties from the target lisp implementation
#+xcvb (module (:depends-on ("specials" "lisp-invocation" "string-escape")))
Francois-Rene Rideau
committed
(in-package :xcvb)
(defvar *target-properties* ()
"Properties of the target system")
(defparameter *target-properties-variables*
. "(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)))")
(*target-system-features*
;; lispworks 6.0 on linux/386 has HARP::PC386 HARP::PC386-X, so remove bad features
. "(remove-if-not 'keywordp *features*)")
. "(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")
Francois-Rene Rideau
committed
(defun target-system-features ()
(get-target-properties)
*target-system-features*)
Francois-Rene Rideau
committed
(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))
Francois-Rene Rideau
committed
(defun read-target-properties ()
(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))))
Francois-Rene Rideau
committed
(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)))))
Francois-Rene Rideau
committed
(case *lisp-implementation-type*
(:sbcl (setenv "SBCL_HOME" *lisp-implementation-directory*)))))
Francois-Rene Rideau
committed
(defun extract-target-properties-via-pipe ()
Francois-Rene Rideau
committed
(with-safe-io-syntax (:package :xcvb-user)
Francois-Rene Rideau
committed
(let ((command (query-target-lisp-command (target-properties-form))))
(log-format-pp 8 "Extract information from target Lisp:~% ~S" command)
(handler-case
(run-program command :output :string)
Peter Keller
committed
(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)))))
(handler-case
(setf stdout (run-program command :output :string))
(slurp :if-does-not-exist :error))
Peter Keller
committed
(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))))
Francois-Rene Rideau
committed
(defun target-properties-form ()
Francois-Rene Rideau
committed
(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*))))
Francois-Rene Rideau
committed
(defun get-asdf-pathname ()
(let ((build (registered-build "/asdf")))
(when (typep build 'build-module-grain)
(subpathname (grain-pathname build) "asdf.lisp"))))
Francois-Rene Rideau
committed
(defun query-target-lisp-command (query-string)
Francois-Rene Rideau
committed
(assert *lisp-implementation-type*)
Francois-Rene Rideau
committed
;; 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.
Francois-Rene Rideau
committed
;; 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).
Francois-Rene Rideau
committed
(append
;;#+(and sbcl (or linux cygwin)) '("env" "-u" "SBCL_HOME") ; we now rely on a recent SBCL
Francois-Rene Rideau
committed
(lisp-invocation-arglist
:cross-compile t
: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)))))