我试图使用lisp代码制作可执行文件。但我无法编译 lisp文件,因为在加载hellowolrd
系统之前没有helloworld
包
;; test.lisp
(asdf:load-system :helloworld)
(defun main()
(helloworld:start))
当然,我制作了helloworld
系统并将其放在~/quicklisp/local-projects/
中。 helloworld
系统已成功加载且没有错误。
;; ~/quicklisp/local-projects/helloworld/helloworld.asd
(asdf:defsystem helloworld
:version "1.0"
:components ((:file "package")))
;; ~/quicklisp/local-projects/helloworld/package.lisp
(defpackage :helloworld
(:use :common-lisp :asdf)
(:export :start))
(in-package :helloworld)
(defun start()
(format t "Welcome, ASDF"))
我想在没有显式加载的情况下编译test.lisp
。我还尝试了use-package
和defpackage
,但失败了。
;; test.lisp
(asdf:load-system :helloworld)
(use-package :helloworld)
(defun main()
(helloworld:start))
;; test.lisp
(asdf:load-system :helloworld)
(defpackage :test
(:use :cl :asdf)
(:export :main))
(in-package :test)
(defun main()
(helloworld:start))
如何使用helloworld
系统中定义的helloworld
包而不加载它?
我是否应该使用helloworld
系统制作新系统?
答案 0 :(得分:5)
在这段代码中,有一些有趣的事情发生了:
;; test.lisp (asdf:load-system :helloworld) (defun main() (helloworld:start))
您无法将其整体编译,因为您已经注意到尝试阅读符号hellowworld:start
是一个问题,因为还没有helloworld包。要读取符号,至少需要定义包。但是,为什么我们不能在(asdf:load-system :helloworld)
遇到同样的问题?简单地说,ASDF软件包已经定义了(实现包括它,或者你已经加载了它,或者别的东西。那么你可以做的一件事是确保在编译时你已经加载了你的helloworld系统:
;; test.lisp
(eval-when (:compile-toplevel)
(asdf:load-system :helloworld))
(defun main()
(helloworld:start))
那应该让你编译文件;因为您在编译时会评估加载表单,然后将在您定义main
时定义包。
当然,现在你已经有了一个已编译的文件,但是如果你把它加载到一个新的helporld系统中,helloworld系统还没有被加载,会发生什么?你有问题。所以你真的想在加载文件时加载那个系统,也许你只是从文件中执行表单(例如,如果你一次只读一个表单并评估它们)。因此,您可能希望在另外两个上下文中评估该负载系统:
;; test.lisp
(eval-when (:compile-toplevel :load-toplevel :execute)
(asdf:load-system :helloworld))
(defun main()
(helloworld:start))
所有这一切,请务必考虑在这种情况下这是否是最适合加载系统的方法。如果出于某种原因,您尝试将所有代码保存在一个文件中或制作交付脚本,则可能会有意义。另一方面,如果您正在制作另一个ASDF可加载系统,那么您可能只是将helloworld作为依赖项包含在内,并让ASDF处理加载依赖项。
答案 1 :(得分:2)
1-要拥有可以编译和加载的文件,请使用eval-when。请参阅我的文章http://fare.livejournal.com/146698.html
2-我建议使用cl-launch:
cl-launch -sp helloworld -r start -o helloworld -d!
3-如果您不想在空间浪费60MB,而是可以站在半秒到几秒的启动延迟,请使用#!/ usr / bin / cl脚本。
答案 2 :(得分:1)
如果要编译二进制文件,可以使用简单的Lisp脚本,例如: G。 (假设是SBCL)
;;;; build.lisp
(require "asdf")
(asdf:operate 'asdf:load-op "helloworld")
(sb-ext:save-lisp-and-die "helloworld"
:toplevel helloworld:start
:executable t)
生成文件:
all:
sbcl --load build.lisp
当加载文件时,Lisp因此执行其表单,所以当它到达save-lisp-and-die
时它已经知道helloworld
包。
还有用于制作可执行文件,buildapp和cl-launch的专用工具。后者可以生成可执行脚本并使用已保存的核心。
UPD
是的,以这种方式构建的可执行文件将包含整个Lisp,因此1)它将是巨大的; 2)有两个或更多这样的可执行文件意味着你已经获得了整个Lisp的(可能是多余的)副本。
另一种实现可以产生更小的二进制文件,例如: G。 CLISP;他们说商业产品甚至更好。
对于无Lisp的目标计算机,这是唯一可行的解决方案。相反,如果目标计算机具有Lisp编译器,则还有另外两个选项:脚本和已保存的核心。
您可以使shell脚本调用sbcl --load <file>
或使用cl-launch来获取复杂的shell包装器,它可以像您希望的那样可移植。 SBCL和CLISP也支持shebang。
但是,如果启动时间很重要,加载库将成为瓶颈,即使它们只编译一次。在这种情况下,您可以使用自定义核心或图像来解决此问题。核心可以被认为是Lisp世界的保存状态,保存核心是sb-ext:save-lisp-and-die
及其对应物的主要任务。加载核心很快,您立即编译了所有库。但是再次大小变得相当大,尽管没有独立可执行文件那么大。当然,一个核心可用于多种应用。 Cl-launch也可以与核心一起使用;特别是,它允许方便地定义输入功能。