2

我试图使用 lisp 代码制作可执行文件。但是我根本无法编译hellowolrdlisp文件,因为在加载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系统?

4

3 回答 3

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

当然,现在你会有一个编译好的文件,但是如果你把它加载到一个没有加载 helloworld 系统的新的 Lisp 实例中会发生什么?你会有问题。因此,您真的想在加载文件时也加载该系统,并且可能您也只是从文件中执行表单(例如,如果您一次读取一个表单并评估它们)。因此,您可能想在另外两种情况下评估该负载系统:

;; test.lisp
(eval-when (:compile-toplevel :load-toplevel :execute)
  (asdf:load-system :helloworld))

(defun main()
  (helloworld:start))

综上所述,请务必考虑在这种情况下这是否是加载系统的最合适方式。如果出于某种原因,您试图将所有代码保存在一个文件中,或者制作交付脚本,这可能是有意义的。另一方面,如果您正在制作另一个 ASDF 可加载系统,那么您可能只是将 helloworld 作为依赖项包含在内,并让 ASDF 处理加载依赖项。

于 2014-09-10T11:45:21.333 回答
2

1- 要拥有一个可以编译和加载的文件,请使用 eval-when。见我的文章http://fare.livejournal.com/146698.html

2-我建议使用 cl-launch:

cl-launch -sp helloworld -r 开始 -o helloworld -d !

3- 如果您不想浪费 60MB 的空间,而是可以忍受半秒到几秒的启动延迟,请使用 #!/usr/bin/cl 脚本。

于 2014-09-10T16:56:57.237 回答
1

如果你想编译一个二进制文件,你可以使用一个简单的 Lisp 脚本,例如(假设 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 的副本。

另一种实现可以生成更小的二进制文件,例如 CLISP;他们说商业的更好。

对于没有 Lisp 的目标计算机,这是唯一可能的解决方案。相反,如果目标计算机具有 Lisp 编译器,则还有两个其他选项:脚本和保存的内核。

您可以创建一个 shell 脚本调用sbcl --load <file>或使用 cl-launch 来获得一个复杂的 shell 包装器,它可以随心所欲地移植。SBCL 和 CLISP 也支持 shebang。

但是,如果启动时间很重要,加载库将成为瓶颈,即使它们只编译一次。在这种情况下,您可以使用自定义核心图像来解决问题。一个核心可以被认为是 Lisp 世界的一个保存状态,保存一个核心是它的首要任务sb-ext:save-lisp-and-die和它的对应物。加载一个核心很快,您可以立即在其中编译所有库。但话又说回来,大小变得相当大,尽管不像独立可执行文件那样大。自然地,一个内核可以用于多种应用。Cl-launch 也可以与内核一起工作;特别是,它允许方便地定义入口函数。

于 2014-09-10T15:50:33.417 回答