~ubuntu-branches/ubuntu/edgy/slime/edgy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
;;;
;;; swank-loader.lisp --- Compile and load the Slime backend.
;;;
;;; Created 2003, James Bielman <jamesjb@jamesjb.com>
;;;
;;; This code has been placed in the Public Domain.  All warranties
;;; are disclaimed.
;;;

;; If you want customize the source- or fasl-directory you can set
;; swank-loader:*source-directory* resp. swank-loader:*fasl-directory*
;; before loading this files. (you also need to create the
;; swank-loader package.)
;; E.g.:
;;
;;   (make-package :swank-loader)
;;   (defparameter swank-loader::*fasl-directory* "/tmp/fasl/")
;;   (load ".../swank-loader.lisp")


(cl:defpackage :swank-loader
  (:use :cl)
  (:export :load-swank 
           :*source-directory*
           :*fasl-directory*))

(cl:in-package :swank-loader)

(defvar *source-directory* 
  (make-pathname :name nil :type nil 
                 :defaults (or *load-pathname* *default-pathname-defaults*))
  "The directory where to look for the source.")

(defparameter *sysdep-files*
  (append 
   '("nregex")
   #+cmu '("swank-source-path-parser" "swank-source-file-cache" "swank-cmucl")
   #+scl '("swank-source-path-parser" "swank-source-file-cache" "swank-scl")
   #+sbcl '("swank-sbcl" "swank-source-path-parser"
            "swank-source-file-cache" "swank-gray")
   #+openmcl '("metering" "swank-openmcl" "swank-gray")
   #+lispworks '("swank-lispworks" "swank-gray")
   #+allegro '("swank-allegro" "swank-gray")
   #+clisp '("metering" "swank-clisp" "swank-gray")
   #+armedbear '("swank-abcl")
   #+cormanlisp '("swank-corman" "swank-gray")
   #+ecl '("swank-ecl" "swank-gray")
   ))

(defparameter *implementation-features*
  '(:allegro :lispworks :sbcl :openmcl :cmu :clisp :ccl :corman :cormanlisp 
    :armedbear :gcl :ecl :scl))

(defparameter *os-features*
  '(:macosx :linux :windows :mswindows :win32 :solaris :darwin :sunos :hpux
    :unix))

(defparameter *architecture-features*
  '(:powerpc :ppc :x86 :x86-64 :amd64 :i686 :i586 :i486 :pc386 :iapx386
    :sparc64 :sparc :hppa64 :hppa))

(defun lisp-version-string ()
  #+cmu       (substitute-if #\_ (lambda (x) (find x " /"))
                             (lisp-implementation-version))
  #+scl       (lisp-implementation-version)
  #+sbcl      (lisp-implementation-version)
  #+ecl       (lisp-implementation-version)
  #+openmcl   (format nil "~d.~d"
                      ccl::*openmcl-major-version* 
                      ccl::*openmcl-minor-version*)
  #+lispworks (lisp-implementation-version)
  #+allegro   (concatenate 'string (if (eq 'h 'H) "A" "M")     ; ANSI vs MoDeRn
                           excl::*common-lisp-version-number*)
  #+clisp     (let ((s (lisp-implementation-version)))
                (subseq s 0 (position #\space s)))
  #+armedbear (lisp-implementation-version)
  #+cormanlisp (lisp-implementation-version))
  
(defun unique-directory-name ()
  "Return a name that can be used as a directory name that is
unique to a Lisp implementation, Lisp implementation version,
operating system, and hardware architecture."
  (flet ((first-of (features)
           (loop for f in features
                 when (find f *features*) return it))
         (maybe-warn (value fstring &rest args)
           (cond (value)
                 (t (apply #'warn fstring args)
                    "unknown"))))
    (let ((lisp (maybe-warn (first-of *implementation-features*)
                            "No implementation feature found in ~a." 
                            *implementation-features*))
          (os   (maybe-warn (first-of *os-features*)
                            "No os feature found in ~a." *os-features*))
          (arch (maybe-warn (first-of *architecture-features*)
                            "No architecture feature found in ~a."
                            *architecture-features*))
          (version (maybe-warn (lisp-version-string)
                               "Don't know how to get Lisp ~
                                implementation version.")))
      (format nil "~(~@{~a~^-~}~)" lisp version os arch))))

(defun file-newer-p (new-file old-file)
  "Returns true if NEW-FILE is newer than OLD-FILE."
  (> (file-write-date new-file) (file-write-date old-file)))

;; Currently just use the modification time of the ChangeLog.  We
;; could also try to use one of those CVS keywords.
(defun slime-version-string ()
  "Return a string identifying the SLIME version.
Return nil if nothing appropriate is available."
  (let* ((changelog (merge-pathnames "ChangeLog" *source-directory*))
         (date (file-write-date changelog)))
    (cond (date (multiple-value-bind (_s _m _h date month year)
                    (decode-universal-time date)
                  (declare (ignore _s _m _h))
                  (format nil "~D-~2,'0D-~2,'0D" year month date)))
          (t nil))))

(defun default-fasl-directory ()
  (merge-pathnames
   (make-pathname  
    :directory `(:relative ".slime" "fasl" 
                 ,@(if (slime-version-string) (list (slime-version-string)))
                 ,(unique-directory-name)))
   (user-homedir-pathname)))

(defun binary-pathname (source-pathname binary-directory)
  "Return the pathname where SOURCE-PATHNAME's binary should be compiled."
  (declare (ignore binary-directory))
  (let ((cfp (compile-file-pathname source-pathname)))
       (merge-pathnames (make-pathname 
                          :directory
                           `(:relative "swank" "fasl" ,(unique-directory-name))
       	                  :name (pathname-name cfp)
                                    :type (pathname-type cfp))
           (clc:calculate-fasl-root))))

(defun compile-files-if-needed-serially (files fasl-directory)
  "Compile each file in FILES if the source is newer than
its corresponding binary, or the file preceding it was 
recompiled."
  (with-compilation-unit ()
    (let ((needs-recompile nil))
      (dolist (source-pathname files)
        (let ((binary-pathname (binary-pathname source-pathname
                                                fasl-directory)))
          (handler-case
              (progn
                (when (or needs-recompile
                          (not (probe-file binary-pathname))
                          (file-newer-p source-pathname binary-pathname))
                  (ensure-directories-exist binary-pathname)
                  (compile-file source-pathname :output-file binary-pathname
                                :print nil :verbose t)
                  (setq needs-recompile t))
                (load binary-pathname :verbose t))
            #+(or)
            (error ()
              ;; If an error occurs compiling, load the source instead
              ;; so we can try to debug it.
              (load source-pathname))
            ))))))

#+(or cormanlisp ecl)
(defun compile-files-if-needed-serially (files fasl-directory)
  "Corman Lisp and ECL have trouble with compiled files."
  (declare (ignore fasl-directory))
  (dolist (file files)
    (load file :verbose t)
    (force-output)))

(defun load-user-init-file ()
  "Load the user init file, return NIL if it does not exist."
  (load (merge-pathnames (user-homedir-pathname)
                         (make-pathname :name ".swank" :type "lisp"))
        :if-does-not-exist nil))

(defun load-site-init-file (directory)
  (load (make-pathname :name "site-init" :type "lisp"
                       :defaults directory)
        :if-does-not-exist nil))

(defun swank-source-files (source-directory)
  (mapcar (lambda (name)
            (make-pathname :name name :type "lisp"
                           :defaults source-directory))
          `("swank-backend" ,@*sysdep-files* "swank")))

(defvar *fasl-directory* (default-fasl-directory)
  "The directory where fasl files should be placed.")

(defun load-swank (&key 
                   (source-directory *source-directory*)
                   (fasl-directory *fasl-directory*))
  (compile-files-if-needed-serially (swank-source-files source-directory) 
                                    fasl-directory)
  (funcall (intern (string :warn-unimplemented-interfaces) :swank-backend))
  (load-site-init-file source-directory)
  (load-user-init-file))

(load-swank)