diff --git a/bootstrap/build0.lisp b/bootstrap/build0.lisp index a180c48..8a344d4 100644 --- a/bootstrap/build0.lisp +++ b/bootstrap/build0.lisp @@ -18,20 +18,24 @@ required-version) (finish-output *error-output*) (uiop:quit 1))) -(defparameter *target-system* (uiop:getenv "KILN_TARGET_SYSTEM")) -(assert (stringp *target-system*)) -(assert (not (= 0 (length *target-system*)))) -(if (find-package :ql) - (progn - (format *error-output* "Found Quicklisp~%") - (uiop:symbol-call :ql :register-local-projects) - (multiple-value-call #'uiop:symbol-call - :ql :quickload *target-system* - (if (uiop:getenvp "KILN_DEBUG") (values) - (values :silent t)))) - (progn - (format *error-output* "Quicklisp not found~%") - (asdf:load-system *target-system*))) -(kiln/image:load-all-script-systems) +(defun load-system (system) + (assert (stringp system)) + (assert (not (= 0 (length system)))) + (if (find-package :ql) + (progn + (format *error-output* "Found Quicklisp~%") + (uiop:symbol-call :ql :register-local-projects) + (multiple-value-call #'uiop:symbol-call + :ql :quickload system + (if (uiop:getenvp "KILN_DEBUG") (values) + (values :silent t)))) + (progn + (format *error-output* "Quicklisp not found~%") + (asdf:load-system system)))) +(load-system "kiln/build") +(let ((target-system (uiop:getenvp "KILN_TARGET_SYSTEM"))) + (if target-system + (load-system target-system) + (kiln/image:load-all-script-systems))) (finish-output *error-output*) (uiop:quit) diff --git a/bootstrap/build1.lisp b/bootstrap/build1.lisp index 0b4037f..4bc9850 100644 --- a/bootstrap/build1.lisp +++ b/bootstrap/build1.lisp @@ -19,14 +19,11 @@ "2.2.6") -7 1))) -(defparameter *target-system* (uiop:getenv "KILN_TARGET_SYSTEM")) -(assert (stringp *target-system*)) -(assert (not (= 0 (length *target-system*)))) (setf (asdf/system:component-build-pathname - (asdf:find-system *target-system*)) + (asdf:find-system "kiln/build")) (let ((string (uiop:getenvp "KILN_TARGET_FILE"))) (if string (uiop:parse-unix-namestring string) #p"kiln"))) -(asdf:make *target-system* :type :program :monolithic t) +(asdf:make "kiln/build" :type :program :monolithic t) (uiop:quit) diff --git a/build.sh b/build.sh index 0672b03..a7aa23f 100755 --- a/build.sh +++ b/build.sh @@ -6,13 +6,14 @@ set -eu : "${KILN_LISP:=sbcl}" : "${KILN_HEAP_SIZE:=32768}" : "${KILN_STACK_SIZE:=}" +: "${KILN_TARGET_PACKAGE:=}" +: "${KILN_TARGET_SYSTEM:=}" if test -n "$KILN_DEBUG"; then set -x + env | grep ^KILN_ >&2 fi -export KILN_TARGET_SYSTEM="${KILN_TARGET_SYSTEM:-"kiln/build"}" - # We will rebind KILN_TARGET_FILE to a tmpfile during the build. real_target_file="${KILN_TARGET_FILE:-"kiln"}" @@ -59,6 +60,9 @@ fi : "${CL_SOURCE_REGISTRY:="$(pwd):"}" export CL_SOURCE_REGISTRY +export KILN_TARGET_PACKAGE +export KILN_TARGET_SYSTEM + # Load once, then dump to avoid serializing foreign pointers. echo "Updating fasls" >&2 @@ -70,7 +74,7 @@ ${LISP_CMD} --load bootstrap/build1.lisp chmod +x "$tmpfile" test -n "$("$tmpfile" version)" mv -f "$tmpfile" "$real_target_file" -if test -z "${NO_PRINT_VERSION:-}"; then +if test -z "${KILN_NO_PRINT_VERSION:-}"; then # real_target_file may be a relative path. PATH=$(pwd):$PATH "${real_target_file}" version fi diff --git a/dispatch.lisp b/dispatch.lisp index 467decd..03cd6f5 100644 --- a/dispatch.lisp +++ b/dispatch.lisp @@ -10,6 +10,7 @@ (:import-from :kiln/user) (:import-from :uiop) (:export + :*entry-point* :dispatch :exec :invoke-entry-point diff --git a/flags.lisp b/flags.lisp index a46e897..2557c8e 100644 --- a/flags.lisp +++ b/flags.lisp @@ -1,6 +1,17 @@ (defpackage :kiln/flags (:use :cl :serapeum :alexandria) (:export + :+kiln-debug+ + :+kiln-heap-size+ + :+kiln-lisp+ + :+kiln-no-print-version+ + :+kiln-stack-size+ + :+kiln-path-systems+ + :+kiln-quicklisp+ + :+kiln-target-file+ + :+kiln-target-package+ + :+kiln-target-system+ + :+kiln-tolerant+ :*flags* :dbg? :dbg @@ -16,6 +27,18 @@ (defvar *exit-code* 0) +(def +kiln-debug+ "KILN_DEBUG") +(def +kiln-heap-size+ "KILN_HEAP_SIZE") +(def +kiln-lisp+ "KILN_LISP") +(def +kiln-no-print-version+ "KILN_NO_PRINT_VERSION") +(def +kiln-stack-size+ "KILN_STACK_SIZE") +(def +kiln-path-systems+ "KILN_PATH_SYSTEMS") +(def +kiln-quicklisp+ "KILN_QUICKLISP") +(def +kiln-target-file+ "KILN_TARGET_FILE") +(def +kiln-target-package+ "KILN_TARGET_PACKAGE") +(def +kiln-target-system+ "KILN_TARGET_SYSTEM") +(def +kiln-tolerant+ "KILN_TOLERANT") + (defplace exit-code () *exit-code*) diff --git a/image.lisp b/image.lisp index c5ffd77..80c7bea 100644 --- a/image.lisp +++ b/image.lisp @@ -6,12 +6,17 @@ (:local-nicknames (:bt :bordeaux-threads)) (:shadowing-import-from :closer-mop :ensure-finalized) (:import-from :cffi) - (:import-from :kiln/dispatch) + (:import-from :kiln/dispatch :*entry-point*) + (:import-from + :kiln/flags + :dbg + :+kiln-target-package+ + :+kiln-target-system+) (:import-from :kiln/script-cache :populate-script-cache) (:import-from :kiln/script-cache :populate-script-cache) (:import-from :kiln/utils :setpgrp) (:export - :load-all-script-systems)) + :load-all-script-systems)) (in-package :kiln/image) (let (lib-names) @@ -33,6 +38,10 @@ script-systems :test #'equal))) +(defun mark-all-systems-immutable () + (map nil 'asdf:register-immutable-system + (asdf:already-loaded-systems))) + (defun record-builtins () (setf *builtins-by-system* (list-all-scripts-by-system) @@ -54,6 +63,14 @@ (unless (typep cls 'built-in-class) (ensure-finalized cls))))) +(defparameter *target-system* + (uiop:getenvp +kiln-target-system+)) + +(defparameter *target-package* + (or (uiop:getenvp +kiln-target-package+) + (and *target-system* + (string-invert-case *target-system*)))) + (defun kiln-before-dump-image () (setf uiop/image::*lisp-interaction* nil) #+sbcl (setf sb-ext:*derive-function-types* t) @@ -65,14 +82,27 @@ (record-builtins) ;; NB Quicklisp doesn't work if it's called inside of the ASDF ;; build-op. So we run it in a separate thread. (Is this still true?) - (let* ((subsystems (list-builtin-script-subsystems))) - (load-all-script-systems :script-systems subsystems) - ;; Mark systems immutable twice: first anything loaded by the - ;; package scripts (so the shebang scripts load faster), then - ;; again for anything loaded after the shebang scripts. - (mark-other-systems-immutable :script-systems subsystems) - (populate-script-cache) - (mark-other-systems-immutable :script-systems subsystems)) + (if *target-system* + (let ((package-name + (or *target-package* + (error "No target package name in environment")))) + (load-system *target-system*) + (mark-all-systems-immutable) + (let ((package + (or (find-package package-name) + (error "No such package as ~a" package-name)))) + (setf *entry-point* + (or (find-symbol (string 'main) package) + (error "No main function for package ~a" + package-name))))) + (let* ((subsystems (list-builtin-script-subsystems))) + (load-all-script-systems :script-systems subsystems) + ;; Mark systems immutable twice: first anything loaded by the + ;; package scripts (so the shebang scripts load faster), then + ;; again for anything loaded after the shebang scripts. + (mark-other-systems-immutable :script-systems subsystems) + (populate-script-cache) + (mark-other-systems-immutable :script-systems subsystems))) (finalize-all-classes) (asdf:clear-configuration) (unload-all-foreign-libraries)))) @@ -83,12 +113,12 @@ #+sbcl (defvar *sbcl-home* (sb-int:sbcl-homedir-pathname)) - (defun kiln-after-restore-image () #+sbcl (sb-ext:disable-debugger) ;; TODO Would it be better to preload them all? - #+sbcl (setf sb-sys::*sbcl-homedir-pathname* *sbcl-home*) - #+sbcl (setf sb-ext:*derive-function-types* nil) + (unless *target-system* + #+sbcl (setf sb-sys::*sbcl-homedir-pathname* *sbcl-home*) + #+sbcl (setf sb-ext:*derive-function-types* nil)) (setf uiop/image::*lisp-interaction* nil) (setpgrp) (reload-all-foreign-libraries)) diff --git a/path.lisp b/path.lisp index 0fafa04..6fe5a83 100644 --- a/path.lisp +++ b/path.lisp @@ -1,5 +1,6 @@ (defpackage :kiln/path (:use :cl :alexandria :serapeum :kiln/stamp) + (:import-from :kiln/flags :+kiln-path-systems+) (:import-from :uiop :file-exists-p :getenv :getenvp :parse-unix-namestring) (:import-from :trivia :match) @@ -56,7 +57,7 @@ (-> scripts-path () path-list) (defun scripts-path () (append *local-scripts-path* - (when-let (env (getenvp "KILN_PATH_SYSTEMS")) + (when-let (env (getenvp +kiln-path-systems+)) (split-sequence-if (op (find _ ",;:")) env :remove-empty-subseqs t)) diff --git a/scripts/rebuild.lisp b/scripts/rebuild.lisp index c7fb9df..b5f052f 100644 --- a/scripts/rebuild.lisp +++ b/scripts/rebuild.lisp @@ -16,17 +16,23 @@ :string :description "Lisp implementation" :long-name "lisp" - :env-vars '("KILN_LISP") + :env-vars (list +kiln-lisp+) :key :lisp) (cli:make-option :string :description "Target system" :long-name "target-system" :key :target-system) + (cli:make-option + :string + :description "Target package (defaults to system)" + :long-name "target-package" + :key :target-package) (cli:make-option :string :description "Executable to generate" :long-name "target-file" + :short-name #\o :key :target-file) (cli:make-option :flag @@ -39,28 +45,28 @@ :long-name "heap-size" :description "Lisp heap size (MB)" :initial-value nil - :env-vars '("KILN_HEAP_SIZE") + :env-vars (list +kiln-heap-size+) :key :heap-size) (cli:make-option :integer :long-name "stack-size" :description "Lisp stack size (MB)" :initial-value nil - :env-vars '("KILN_STACK_SIZE") + :env-vars (list +kiln-stack-size+) :key :stack-size) (cli:make-option :flag :description "Use Quicklisp" :long-name "quicklisp" :initial-value :false - :env-vars '("KILN_QUICKLISP") + :env-vars (list +kiln-quicklisp+) :key :quicklisp) (cli:make-option :flag :description "Skip systems that fail to compile" :long-name "tolerant" :initial-value :false - :env-vars '("KILN_TOLERANT") + :env-vars (list +kiln-tolerant+) :key :tolerant))) (def command @@ -75,28 +81,34 @@ (cli:getopt opts :target-file)) (force-output *error-output*) (when-let (lisp (cli:getopt opts :lisp)) - (setf (getenv "KILN_LISP") lisp)) - (when-let (target-system (cli:getopt opts :target-system)) - (setf (getenv "KILN_TARGET_SYSTEM") target-system)) + (setf (getenv +kiln-lisp+) lisp)) + (when-let* ((target-system (cli:getopt opts :target-system)) + (target-package + (or (cli:getopt opts :target-package) + (string-invert-case target-system)))) + (setf (getenv +kiln-target-system+) target-system + (getenv +kiln-target-package+) target-package)) + (when (cli:getopt opts :target-package) + (error "Cannot provide --target-package without --target-system")) (when-let (target-file (cli:getopt opts :target-file)) - (setf (getenv "KILN_TARGET_FILE") target-file)) - (when (cli:getopt opts :no-version) - (setf (getenv "NO_PRINT_VERSION") "1")) + (setf (getenv +kiln-target-file+) target-file)) + (when (or (cli:getopt opts :no-version) + (cli:getopt opts :target-system)) + (setf (getenv +kiln-no-print-version+) "1")) (when-let (heap-size (cli:getopt opts :heap-size)) - (setf (getenv "KILN_HEAP_SIZE") + (setf (getenv +kiln-heap-size+) (princ-to-string heap-size))) (when-let (stack-size (cli:getopt opts :stack-size)) - (setf (getenv "KILN_STACK_SIZE") + (setf (getenv +kiln-stack-size+) (princ-to-string stack-size))) (when (cli:getopt opts :quicklisp) - (setf (getenv "KILN_QUICKLISP") + (setf (getenv +kiln-quicklisp+) (if (find-package :ql) (asdf:system-relative-pathname "quicklisp" "../setup.lisp") (error "Quicklisp requested but not available")))) (when (cli:getopt opts :tolerant) - (setf (getenv "KILN_TOLERANT") "1")) - + (setf (getenv +kiln-tolerant+) "1")) (let ((path (asdf:system-relative-pathname "kiln" ""))) (uiop:chdir (namestring path)) (exec "sh build.sh")))) diff --git a/scripts/self-test.lisp b/scripts/self-test.lisp index 32c6d98..5b73a45 100644 --- a/scripts/self-test.lisp +++ b/scripts/self-test.lisp @@ -220,6 +220,17 @@ This is useful when we need to test the exact output." (is (equal (fmt "Before exec~%Unwinding happened~%exec happened") result)))) +(5am:test entry-point + (with-templated-test-system (:name "kiln-entry-point-system" + :path path + :kiln-path nil) + (uiop:with-temporary-file (:pathname tmp) + (cmd *self* + "rebuild --target-file" tmp + "--target-system kiln-entry-point-system") + (is (uiop:file-exists-p tmp)) + (is (equal "Hello, world" ($cmd tmp)))))) + (defun main (args) (destructuring-bind (&optional (test 'test)) args (when (stringp test) diff --git a/test/kiln-entry-point-system/kiln-entry-point-system-dot-asd b/test/kiln-entry-point-system/kiln-entry-point-system-dot-asd index 6b4509f..b458fed 100644 --- a/test/kiln-entry-point-system/kiln-entry-point-system-dot-asd +++ b/test/kiln-entry-point-system/kiln-entry-point-system-dot-asd @@ -1,3 +1,3 @@ +;;; -*- mode: lisp -*- (defsystem "kiln-entry-point-system" - :build-operation "asdf:program-op" - :entry-point "kiln-entry-point-system:entry-point") + :components ((:file "kiln-entry-point-system"))) diff --git a/test/kiln-entry-point-system/kiln-entry-point-system-dot-lisp b/test/kiln-entry-point-system/kiln-entry-point-system-dot-lisp index a5e0844..e188e27 100644 --- a/test/kiln-entry-point-system/kiln-entry-point-system-dot-lisp +++ b/test/kiln-entry-point-system/kiln-entry-point-system-dot-lisp @@ -1,6 +1,8 @@ +;;; -*- mode: lisp -*- (defpackage :kiln-entry-point-system - (:use :cl)) + (:use :cl) + (:export :main)) (in-package :kiln-entry-point-system) -(defun entry-point () - (format *error-output* "Hello, world")) +(defun main () + (format t "Hello, world~%")) diff --git a/test/other-test-system/other-test-system-dot-lisp b/test/other-test-system/other-test-system-dot-lisp index 63b535b..3a14bc2 100644 --- a/test/other-test-system/other-test-system-dot-lisp +++ b/test/other-test-system/other-test-system-dot-lisp @@ -1 +1,2 @@ +;;; -*- mode: lisp -*- (defpackage :other-test-system)