#|| sh asdf-install.lisp will compile this file to an exe called asdf-install sbcl <= response 400) (error 'download-error :url url :response response)) (let ((length (parse-integer (or (cdr (assoc :content-length headers)) "") :junk-allowed t))) (format t "Downloading ~A bytes from ~A ..." (if length length "some unknown number of") url) (force-output) (with-open-file (o file-name :direction :output) (if length (let ((buf (make-array length :element-type (stream-element-type stream) ))) (read-sequence buf stream) (write-sequence buf o)) (sb-executable:copy-stream stream o)))) (close stream) (terpri) ;; seems to have worked. let's try for a detached gpg signature too (when *verify-gpg-signatures* (verify-gpg-signature url file-name))))) (defun verify-gpg-signature (url file-name) (destructuring-bind (response headers stream) (url-connection (concatenate 'string url ".asc")) (declare (ignore headers)) (unwind-protect (if (= response 200) ;; sadly, we can't pass the stream directly to run-program, ;; because (at least in sbcl 0.8) that ignores existing buffered ;; data and only reads new fresh data direct from the file ;; descriptor (let ((data (make-string (parse-integer (cdr (assoc :content-length headers)) :junk-allowed t)))) (read-sequence data stream) (let ((ret (process-exit-code (sb-ext:run-program "/usr/bin/gpg" (list "--verify" "-" (namestring file-name)) :output t :input (make-string-input-stream data) :wait t)))) (unless (zerop ret) (error 'signature-error :cause (make-condition 'simple-error :format-control "GPG returned exit status ~A" :format-arguments (list ret)))))) (error 'signature-error :cause (make-condition 'download-error :url (concatenate 'string url ".asc") :response response))) (close stream)))) (defun where () (format t "Install where?~%") (loop for (source system name) in *locations* for i from 1 do (format t "~A) ~A: ~% System in ~A~% Files in ~A ~%" i name system source)) (format t " --> ") (force-output) (let ((response (read))) (when (> response 0) (elt *locations* (1- response))))) (defun install (source system packagename) "Returns a list of asdf system names for installed asdf systems" (ensure-directories-exist source ) (ensure-directories-exist system ) (let* ((tar (with-output-to-string (o) (or (sb-ext:run-program "/bin/tar" (list "-C" (namestring source) "-xzvf" (namestring packagename)) :output o :wait t) (error "can't untar")))) (dummy (princ tar)) (pos-slash (position #\/ tar)) (*default-pathname-defaults* (merge-pathnames (make-pathname :directory `(:relative ,(subseq tar 0 pos-slash))) source))) (loop for asd in (directory (make-pathname :name :wild :type "asd")) do (let ((target (merge-pathnames (make-pathname :name (pathname-name asd) :type (pathname-type asd)) system))) (when (probe-file target) (sb-posix:unlink target)) (sb-posix:symlink asd target)) collect (pathname-name asd)))) (defvar *temporary-files*) (defun temp-file-name (p) (let* ((pos-slash (position #\/ p :from-end t)) (pos-dot (position #\. p :start (or pos-slash 0)))) (merge-pathnames (make-pathname :name (subseq p (if pos-slash (1+ pos-slash) 0) pos-dot) :type "asdf-install-tmp")))) (defun run (&optional (packages (cdr *posix-argv*))) (destructuring-bind (source system name) (where) (labels ((one-iter (packages) (dolist (asd (loop for p in packages unless (probe-file p) do (let ((tmp (temp-file-name p))) (pushnew tmp *temporary-files*) (download p tmp) (setf p tmp)) end do (format t "Installing ~A in ~A,~A~%" p source system) append (install source system p))) (handler-case (asdf:operate 'asdf:load-op asd) (asdf:missing-dependency (c) (format t "Downloading package ~A, required by ~A~%" (asdf::missing-requires c) (asdf:component-name (asdf::missing-required-by c))) (one-iter (list (symbol-name (asdf::missing-requires c))))))))) (one-iter packages)))) (handler-case (let ((*temporary-files* nil)) (unwind-protect (run) (dolist (l *temporary-files*) (when (probe-file l) (delete-file l))))) (error (c) (princ "Install failed due to error:") (terpri) (princ c) (terpri) (quit :unix-status 1))) ;(quit)