Common Lisp编译生成的fasl能否留存编译时信息并在运行时调用?
如何让Common Lisp的FASL文件留存编译时信息
你的问题核心是:编译阶段通过eval-when (:compile-toplevel)赋值的变量值,并没有被写入FASL文件,导致新会话加载时变量回到初始的nil状态。要解决这个问题,需要把编译时计算的值嵌入到生成的FASL文件中,让加载时能直接读取固化后的信息。
原代码问题分析
原代码中eval-when (:compile-toplevel)的赋值操作仅在编译当前会话中生效,这个赋值过程不会被记录到FASL文件里。FASL只保存了defvar *compilation-time* nil的变量定义,所以新会话加载时,变量初始值仍是nil。
解决方案:固化编译时信息到FASL
方法1:使用defconstant直接固化值
defconstant的初始表达式会在编译阶段求值,求值结果会被直接写入FASL文件,加载时无需重新计算:
(in-package :cl-user) (defconstant *compilation-time* (multiple-value-bind (s m h) (decode-universal-time (get-universal-time)) (format nil "~D:~D:~D" h m s)) :documentation "编译该文件的时间") (defconstant *compilation-path* *compile-file-pathname* :documentation "编译时的源文件路径") (format t "Built at: ~A~%" *compilation-time*) (format t "Built from: ~A~%" *compilation-path*)
方法2:动态生成加载时赋值代码
如果不需要用常量(允许后续修改变量值),可以在编译阶段计算值,然后生成加载时执行的赋值语句,嵌入到FASL中:
(in-package :cl-user) (defvar *compilation-time* nil) (defvar *compilation-path* nil) (eval-when (:compile-toplevel) ;; 在编译阶段计算时间和路径 (let ((current-time (multiple-value-bind (s m h) (decode-universal-time (get-universal-time)) (format nil "~D:~D:~D" h m s))) (source-path *compile-file-pathname*)) ;; 生成加载时执行的赋值代码,把编译时的值固化进去 (eval `(eval-when (:load-toplevel :execute) (setf *compilation-time* ,current-time) (setf *compilation-path* ,source-path))))) (format t "Built at: ~A~%" *compilation-time*) (format t "Built from: ~A~%" *compilation-path*)
测试效果
编译后加载FASL文件,就能看到正确的编译时间和路径:
# sbcl --noinform --no-userinit * (load "foo.fasl") Built at: 17:33:20 Built from: #P"/tmp/common-lisp/foo.lisp" T * *compilation-time* "17:33:20"
内容的提问来源于stack exchange,提问作者xiepan
相关产品推荐
相关产品推荐

