Skip to content

Commit

Permalink
Allow building a executable with Kiln
Browse files Browse the repository at this point in the history
  • Loading branch information
ruricolist committed Dec 14, 2024
1 parent bbafda5 commit b289066
Show file tree
Hide file tree
Showing 12 changed files with 144 additions and 58 deletions.
34 changes: 19 additions & 15 deletions bootstrap/build0.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
7 changes: 2 additions & 5 deletions bootstrap/build1.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
10 changes: 7 additions & 3 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"}"

Expand Down Expand Up @@ -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
Expand All @@ -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
1 change: 1 addition & 0 deletions dispatch.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
(:import-from :kiln/user)
(:import-from :uiop)
(:export
:*entry-point*
:dispatch
:exec
:invoke-entry-point
Expand Down
23 changes: 23 additions & 0 deletions flags.lisp
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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*)

Expand Down
56 changes: 43 additions & 13 deletions image.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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))))
Expand All @@ -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))
Expand Down
3 changes: 2 additions & 1 deletion path.lisp
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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))
Expand Down
44 changes: 28 additions & 16 deletions scripts/rebuild.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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"))))
11 changes: 11 additions & 0 deletions scripts/self-test.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions test/kiln-entry-point-system/kiln-entry-point-system-dot-asd
Original file line number Diff line number Diff line change
@@ -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")))
8 changes: 5 additions & 3 deletions test/kiln-entry-point-system/kiln-entry-point-system-dot-lisp
Original file line number Diff line number Diff line change
@@ -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~%"))
1 change: 1 addition & 0 deletions test/other-test-system/other-test-system-dot-lisp
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
;;; -*- mode: lisp -*-
(defpackage :other-test-system)

0 comments on commit b289066

Please sign in to comment.