diff --git a/BUILD b/BUILD
index a3ddec9..194cd1c 100644
--- a/BUILD
+++ b/BUILD
@@ -25,7 +25,10 @@ license(
licenses(["notice"])
-exports_files(["LICENSE"])
+exports_files([
+ "LICENSE",
+ "imagesave.lisp",
+])
bzl_library(
name = "build_rules",
diff --git a/doc/rules.md b/doc/rules.md
index edaee33..532c45e 100755
--- a/doc/rules.md
+++ b/doc/rules.md
@@ -9,8 +9,9 @@
lisp_binary(name, deps, srcs, data, add_features, allow_save_lisp, block_compile,
- block_compile_specified_only, cdeps, compile_data, image, instrument_coverage, main,
- malloc, nowarn, order, precompile_generics, runtime, save_runtime_options, stamp, verbose)
+ block_compile_specified_only, cdeps, compile_data, helper_script, image,
+ instrument_coverage, main, malloc, nowarn, order, precompile_generics, runtime,
+ save_runtime_options, stamp, verbose)
Supports all of the same attributes as [`lisp_library`](#lisp_library), plus
@@ -43,6 +44,7 @@ Example:
| block_compile_specified_only | If true, block compilation only considers multiple top-level forms together if those are between explicit (START-BLOCK) and (END-BLOCK). | Boolean | optional | `False` |
| cdeps | C++ dependencies (generally [`cc_library`](https://docs.bazel.build/versions/master/be/c-cpp.html#cc_library)). | List of labels | optional | `[]` |
| compile_data | Data available to this target and its consumers at build time, added to the inputs of LispCompile and LispCore actions. | List of labels | optional | `[]` |
+| helper_script | - | Label | optional | `None` |
| image | Lisp binary used as Bazel compilation image. This should be a binary with the main function `#'bazel:main` defined in `main.lisp`. | Label | optional | `"//third_party/lisp/bazel:image"` |
| instrument_coverage | Force coverage instrumentation. Possible values:
`0`: Never instrument this target. Should be used if thetarget compiles generated source files or does not compilewith coverage instrumentation.
`1`: Always instrument this target. Generally should not be used outside of tests for the coverage implementation.
`-1` (default): If coverage data collection is enabled, instrument this target per [`--instrumentation_filter](https://docs.bazel.build/versions/master/command-line-reference.html#flag--instrumentation_filter).` | Integer | optional | `-1` |
| main | Name of function (by default in the `cl-user` package) or snippet of Lisp code to run when starting the binary. `"nil"` or `"t"` to start the default REPL. Can be overridden by naming a function (or `nil` or `t`) in the `LISP_MAIN` environment variable. | String | optional | `"main"` |
@@ -105,8 +107,9 @@ Example:
lisp_test(name, deps, srcs, data, add_features, allow_save_lisp, block_compile,
- block_compile_specified_only, cdeps, compile_data, image, instrument_coverage, main, malloc,
- nowarn, order, precompile_generics, runtime, save_runtime_options, stamp, verbose)
+ block_compile_specified_only, cdeps, compile_data, helper_script, image,
+ instrument_coverage, main, malloc, nowarn, order, precompile_generics, runtime,
+ save_runtime_options, stamp, verbose)
Like [`lisp_binary`](#lisp_binary), for defining tests to be run with the
@@ -142,6 +145,7 @@ Example:
| block_compile_specified_only | If true, block compilation only considers multiple top-level forms together if those are between explicit (START-BLOCK) and (END-BLOCK). | Boolean | optional | `False` |
| cdeps | C++ dependencies (generally [`cc_library`](https://docs.bazel.build/versions/master/be/c-cpp.html#cc_library)). | List of labels | optional | `[]` |
| compile_data | Data available to this target and its consumers at build time, added to the inputs of LispCompile and LispCore actions. | List of labels | optional | `[]` |
+| helper_script | - | Label | optional | `None` |
| image | Lisp binary used as Bazel compilation image. This should be a binary with the main function `#'bazel:main` defined in `main.lisp`. | Label | optional | `"//third_party/lisp/bazel:image"` |
| instrument_coverage | Force coverage instrumentation. Possible values:
`0`: Never instrument this target. Should be used if thetarget compiles generated source files or does not compilewith coverage instrumentation.
`1`: Always instrument this target. Generally should not be used outside of tests for the coverage implementation.
`-1` (default): If coverage data collection is enabled, instrument this target per [`--instrumentation_filter](https://docs.bazel.build/versions/master/command-line-reference.html#flag--instrumentation_filter).` | Integer | optional | `-1` |
| main | Name of function (by default in the `cl-user` package) or snippet of Lisp code to run when starting the binary. `"nil"` or `"t"` to start the default REPL. Can be overridden by naming a function (or `nil` or `t`) in the `LISP_MAIN` environment variable. | String | optional | `"main"` |
diff --git a/imagesave.lisp b/imagesave.lisp
new file mode 100644
index 0000000..48803f2
--- /dev/null
+++ b/imagesave.lisp
@@ -0,0 +1,68 @@
+#|(in-package sb-impl)
+(defun safe-gethash3 (key table default)
+ (let ((layout (sb-kernel:%instance-layout table)))
+ (if (/= 0 (sb-kernel:get-lisp-obj-address layout))
+ (funcall (hash-table-gethash-impl table) key table default)
+ (let ((s #.(format nil "table messup~%")))
+ (sb-unix:unix-write 2 s 0 (length s))
+ (sb-ext:search-roots (sb-ext:make-weak-pointer table) :print :verbose)
+ (error "can't go on- table ~X borked"
+ (sb-kernel:get-lisp-obj-address table))))))
+(compile 'safe-gethash3)
+(setf (symbol-function 'gethash3) #'safe-gethash3)
+
+;(defvar *in-convert-table* nil)
+;(defun wrap-internal-make-hash-table (realfun &rest args)
+; (let ((result (apply realfun args)))
+; (when (and *in-convert-table* (neq (heap-allocated-p result) :dynamic))
+; (let ((s (format nil "table ~x not on heap: ~s~%"
+; (sb-kernel:get-lisp-obj-address result)
+; (sb-debug:list-backtrace :count 20))))
+; (sb-sys:with-pinned-objects (s)
+; (sb-unix:unix-write 2 s 0 (length s)))))
+; result))
+;(compile 'wrap-internal-make-hash-table)
+;(sb-int:encapsulate 'sb-impl::%make-hash-table 'trace #'wrap-internal-make-hash-table)
+
+(in-package sb-pcl)
+(defun wrap-convert-table (realfun a b c)
+ (let ((result (funcall realfun a b c)))
+ (when (and (hash-table-p result)
+ (plusp (hash-table-count result))
+ (neq (heap-allocated-p result) :dynamic))
+ (let ((s (with-output-to-string (s)
+ (format s "converted table ~x keys" (sb-kernel:get-lisp-obj-address result))
+ (maphash (lambda (k v) v (format s " ~s" k)) result)
+ (format s "~%~a~%" (sb-debug:list-backtrace :count 20)))))
+ (setq s (coerce s 'base-string))
+ (sb-sys:with-pinned-objects (s)
+ (sb-unix:unix-write 2 s 0 (length s)))))
+ result))
+(compile 'wrap-convert-table)
+(sb-int:encapsulate 'convert-table 'trace #'wrap-convert-table)
+
+(in-package "CL-USER")
+|#
+;; Fancy save steps
+(defun save-and-exit (name &key toplevel save-runtime-options verbose
+ precompile-generics executable)
+ "Saves the current Lisp image and dies.
+ Arguments:
+ NAME - the file name to save the image.
+ TOPLEVEL - the name of the toplevel function.
+ SAVE-RUNTIME-OPTIONS - indicates if the runtime options shall be saved to the C runtime.
+ This is usually permanent.
+ VERBOSE - if true, the output streams are not muted before dumping the image.
+ PRECOMPILE-GENERICS - will precompile the generic functions before saving.
+ EXECUTABLE - Whether to combine the launcher with the image to create an executable."
+ (unintern 'save-and-exit)
+; (disable-debugger)
+; (when precompile-generics
+; (bazel.sbcl::precompile-generic-functions :verbose bazel.log:*verbose*))
+; (unless verbose (bazel.sbcl:mute-output-streams))
+; (fold-identical-code :aggressive t)
+; (setf (extern-alien "gc_coalesce_string_literals" char) 2)
+ (save-lisp-and-die name :executable executable
+ :toplevel toplevel
+ :save-runtime-options save-runtime-options)
+ (sb-int:bug "Unreachable"))
diff --git a/main.lisp b/main.lisp
index 73c4b5c..6565017 100644
--- a/main.lisp
+++ b/main.lisp
@@ -551,14 +551,18 @@ If LISP_MAIN is NIL or T it will call top-level REPL as well."
(funcall-named "UIOP:CALL-IMAGE-DUMP-HOOK")
;; Set to a sane value.
(in-package "COMMON-LISP-USER")
- ;; Finally call the Lisp implementation function.
- (save-lisp-and-die
- name
- :toplevel #'restart-image
- :save-runtime-options save-runtime-options
- :precompile-generics precompile-generics
- :executable executable
- :verbose (plusp *verbose*)))
+ (dolist (candidate '("lisp/devtools/bazel/imagesave.lisp"
+ "third_party/lisp/bazel/imagesave.lisp"))
+ (when (probe-file candidate)
+ (load candidate)
+ (funcall (intern "SAVE-AND-EXIT")
+ name
+ :toplevel #'restart-image
+ :save-runtime-options save-runtime-options
+ :precompile-generics precompile-generics
+ :executable executable
+ :verbose (plusp *verbose*))))
+ (sb-ext:save-lisp-and-die name :toplevel #'restart-image :executable t))
(defun set-optimization-mode (optimization-mode)
"Proclaim the optimization settings based on the OPTIMIZATION-MODE."
diff --git a/rules.bzl b/rules.bzl
index 6f37ee7..80d9713 100644
--- a/rules.bzl
+++ b/rules.bzl
@@ -200,6 +200,10 @@ _LISP_BINARY_ATTRS.update({
doc = ("SBCL C++ dependencies. Consumers should generally omit this " +
"attr and use the default value."),
),
+ "helper_script": attr.label(
+ default = None,
+ allow_single_file = True,
+ ),
"_elfinate": attr.label(
default = Label(_ELFINATE),
executable = True,
@@ -674,7 +678,10 @@ def _lisp_binary_impl(ctx):
content = content,
)
- inputs = [specs]
+ if ctx.file.helper_script == None:
+ inputs = [specs]
+ else:
+ inputs = [specs, ctx.file.helper_script]
inputs.extend(fasls)
inputs.extend(hashes)
inputs.extend(warnings)
diff --git a/sbcl.lisp b/sbcl.lisp
index b7ee14e..7999b66 100644
--- a/sbcl.lisp
+++ b/sbcl.lisp
@@ -109,15 +109,6 @@
(setf (extern-alien "lisp_startup_options" int) 1)
nil)
-(defun terminate-other-threads ()
- "Terminates all non-system threads but the current one."
- (let ((threads (remove-if (lambda (x)
- (or (thread-ephemeral-p x)
- (eq x *current-thread*)))
- (list-all-threads))))
- (mapc #'terminate-thread threads)
- (mapc (lambda (thread) (join-thread thread :default nil)) threads)))
-
(defun name-closure (closure name)
"Return CLOSURE with the NAME changed, so it prints nicely."
;; This is not necessary, except for debugging and aesthetics.
@@ -278,32 +269,6 @@
;;; Generate an image.
;;;
-(defun save-lisp-and-die (name &key toplevel save-runtime-options verbose
- precompile-generics executable)
- "Saves the current Lisp image and dies.
- Arguments:
- NAME - the file name to save the image.
- TOPLEVEL - the name of the toplevel function.
- SAVE-RUNTIME-OPTIONS - indicates if the runtime options shall be saved to the C runtime.
- This is usually permanent.
- VERBOSE - if true, the output streams are not muted before dumping the image.
- PRECOMPILE-GENERICS - will precompile the generic functions before saving.
- EXECUTABLE - Whether to combine the launcher with the image to create an executable."
- (sb-ext:disable-debugger)
- (terminate-other-threads)
- (when precompile-generics
- (precompile-generic-functions :verbose bazel.log:*verbose*))
- (unless verbose (mute-output-streams))
- (sb-ext:fold-identical-code :aggressive t)
- (setf (extern-alien "gc_coalesce_string_literals" char) 2)
- (sb-ext:save-lisp-and-die
- name
- :executable executable
- :toplevel toplevel
- :save-runtime-options save-runtime-options)
-
- (assert (not "Expected the image to survive after save-lisp-and-die."))) ; NOLINT
-
(defun set-interpret-mode (compile-mode)
"Set the mode of eval to :interpret if COMPILE-MODE is :LOAD. Otherwise, set it to :COMPILE."
(declare (optimize (speed 1) (safety 3) (compilation-speed 1) (debug 1)))