|
Subject: "Standalone executables" Newsgroups: gmane.lisp.cmucl.devel Date: 2003-05-23 15:56:50 GMT (6 years, 5 weeks, 6 days, 22 hours and 19 minutes ago) It can be useful to be able to deliver an application developed with CMUCL as a single file, that looks like a native executable and only has runtime dependencies on the platform's standard shared libraries. [though this is less useful than many newcomers to Lisp seem to think, in my opinion.] I have implemented this capability for CMUCL on Linux and Solaris, by piggybacking the lisp image in an ELF section of the runtime. On startup, the runtime looks inside itself to see whether it has an image in its belly, and if so it extracts it to a temporary file. That file is mapped into memory as usual, and is deleted when CMUCL exits. The scanning of ELF sections in the runtime is implemented by using the platform libelf (standard on Solaris, libelfg0-dev package on Debian Linux). Adding a section is implemented using objcopy from the GNU binutils. The Lisp interface to this is (ext:save-application filename &rest save-lisp-args) which forks a child CMUCL that does SAVE-LISP to obtain an image. The parent process uses objcopy to combine the runtime and the image into a shiny Standalone Executable. You can repeat the process several times; each time you save an application the old image is replaced by the new. The downside to this approach is that multiple invocations of the same application will no longer share the read-only bits of the lisp image, since to the operating system they look like different files. Note also that there's no effort to do any "tree shaking" to remove unused bits of code in the image, as per certain commercial implementations. I have attached a small patch to the runtime, a file elf_delivery.c that has to be built with the runtime (and linked with -lelf or elf.a), and a file save-application.lisp.
Index: lisp/core.h
===================================================================
RCS file: /home/CVS-cmucl/src/lisp/core.h,v
retrieving revision 1.3
diff -c -u -r1.3 core.h
--- lisp/core.h 27 Mar 1994 15:17:54 -0000 1.3
+++ lisp/core.h 23 May 2003 15:51:42 -0000
@@ -36,4 +36,6 @@
extern lispobj load_core_file(char *file);
+extern char * search_elf_section(char **);
+
#endif
Index: lisp/lisp.c
===================================================================
RCS file: /home/CVS-cmucl/src/lisp/lisp.c,v
retrieving revision 1.31
diff -c -u -r1.31 lisp.c
--- lisp/lisp.c 28 Apr 2003 23:07:38 -0000 1.31
+++ lisp/lisp.c 23 May 2003 15:51:42 -0000
@@ -427,6 +427,10 @@
}
}
+ if (NULL == core) {
+ core = search_elf_section(argv);
+ }
+
/*
* If no core file specified, search for it in CMUCLLIB
*/
;;; save-application.lisp
;;;
;;; Author: Eric Marsden <emarsden <at> laas.fr>
;;; Time-stamp: <2003-05-23 emarsden>
;;
;;
;; This code was written as part of the CMUCL project and has been
;; placed in the public domain.
(in-package :ext)
(defun copy-file (from-pathname to-pathname
&key link preserve-symbolic-links
element-type preserve-time)
(declare (ignorable preserve-symbolic-links preserve-time element-type
preserve-time))
(when (eq :hard link)
(multiple-value-bind (failed errno)
(unix:unix-link (unix-namestring from-pathname)
(unix-namestring to-pathname))
(when failed
(error "Couldn't create hard link: errno ~d" errno))
(return-from copy-file to-pathname)))
(when link
(multiple-value-bind (failed errno)
(unix:unix-symlink (unix-namestring from-pathname)
(unix-namestring to-pathname))
(when failed
(error "Couldn't create symbolic link: errno ~d" errno))
(return-from copy-file to-pathname)))
;; copy the octets
(with-open-file (in from-pathname
:direction :input
:element-type '(unsigned-byte 8))
(with-open-file (out to-pathname
:direction :output
:element-type '(unsigned-byte 8)
:if-exists :supersede)
(loop
:with buffer = (make-array 1024 :element-type '(unsigned-byte 8))
:for n = (read-sequence buffer in)
:until (= n 0)
:do (write-sequence buffer out :end n))))
to-pathname)
(defun make-temporary-file-name (&optional directory prefix)
(let* ((directory (or directory
(cdr (assoc :tmpdir ext:*environment-list*))
"tmp"))
(private (format nil "cmucl-~D" (unix:unix-getpid)))
(file (make-pathname :name (or prefix "cmucltmp")
:directory `(:absolute ,directory ,private)
:type (symbol-name (gensym)))))
(ensure-directories-exist file)
(unix:unix-chmod (unix-namestring (directory-namestring file)) #o700)
(namestring file)))
(defmacro with-temporary-file (file &body body)
`(let ((,file (make-temporary-file-name)))
(unwind-protect
(progn ,@body)
(delete-file ,file))))
;; fork a child process that saves the lisp image, then create a
;; "standalone executable" as a copy of the runtime that has the image
;; as a special .cmuclimg ELF section.
(defun save-application (filename &rest save-lisp-args)
(let* ((image (make-temporary-file-name))
(runtime-copy nil)
(runtime (cond ((probe-file "/proc/self/exe")
(truename "/proc/self/exe"))
;; on Solaris, this isn't a symbolic link, so
;; we have to copy the contents of the runtime
;; image to a temporary file, and return that.
((probe-file "/proc/self/object/a.out")
(setq runtime-copy (make-temporary-file-name))
(copy-file "/proc/self/object/a.out" runtime-copy)
runtime-copy)
(t
(error "Can't find path to runtime")))))
(when (zerop (unix:unix-fork))
(apply #'ext:save-lisp image save-lisp-args))
;; the father waits for the child lisp to terminate
(alien-funcall (extern-alien "wait" (function unsigned unsigned)) 0)
(ext:run-program "objcopy"
(list "--add-section"
(concatenate 'string ".cmuclimg=" image)
(namestring runtime)
filename))
(delete-file image)
(when runtime-copy (delete-file runtime-copy))
filename))
;; EOF
-- Eric Marsden <URL:http://www.laas.fr/~emarsden/> |
|
|