无法使用系统中定义的lisp包

时间:2014-09-10 07:23:13

标签: lisp common-lisp sbcl quicklisp asdf

我试图使用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-packagedefpackage,但失败了。

;; 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系统制作新系统?

3 个答案:

答案 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也可以与核心一起使用;特别是,它允许方便地定义输入功能。