如何从 .asd 文件的目录编译和运行 Common Lisp 程序?
Posted
技术标签:
【中文标题】如何从 .asd 文件的目录编译和运行 Common Lisp 程序?【英文标题】:How do I compile and run a Common Lisp program from the directory of the .asd file? 【发布时间】:2021-03-25 19:16:54 【问题描述】:我的目录结构如下:
my-project/
├── my-project.asd
├── package.lisp # defpackage.
├── utils.lisp # Functions used by main.lisp.
└── main.lisp # Main program.
my-project.asd
:
(defsystem "my-project"
:components ((:file "package")
(:file "utils")
(:file "main")))
package.lisp
:
(defpackage :com.example
(:use :cl))
utils.lisp
:
(in-package :com.example)
(defun double (x)
(* x 2))
main.lisp
:
(in-package :com.example)
(format t "~a" (double 3))
问题是:如何使用 ASDF 编译和运行main.lisp
?
我能够通过以下方式编译和运行程序:
$ mv my-project ~/common-lisp/.
$ sbcl
* (require :asdf)
* (asdf:load-system :my-project)
但是,这非常愚蠢。我不想将我的项目移动到~/common-lisp/
只是为了运行它。我想直接从项目目录本身编译和运行程序。 my-project/
目录可以在任何地方,我希望它可以放在任何地方。也就是说,我想从当前目录加载系统。
想想make
,我可以直接从Makefile
本身的目录编译文件。如何从*.asd
文件本身的目录中类似地编译和运行Common Lisp 程序?
(我使用的是 SBCL 1.4.5 版和 ASDF 3.3.1 版)
【问题讨论】:
【参考方案1】:这就是我为解决这个问题所做的。它可能不是您所需要的,但这里可能有一些可以提供帮助的想法。
用于开发
确保您尝试构建的系统只有一个副本。因此,特别要确保没有 ASDF 可能找到的已安装副本:见下文。
那么这将起作用。首先确保您的 ASDF 系统定义是可冷加载的,因此特别要确保它的顶部有正确的(in-package :asdf-user)
。
那么构建系统的方法是:
$ cd .../my-project
$ sbcl
[...]
* (require :asdf) ;already loaded in my case by init files
nil
* (load "my-project.asd")
t
* (asdf:load-system "my-project")
; compiling file "/media/psf/share/tmp/my-project/package.lisp" (written 15 DEC 2020 09:06:54 AM):
; processing (defpackage :com.example ...)
[...]
*
现在你很好了。所以我做的三个技巧是:
根本避免考虑整个源注册表头发,因为如果你想太多,触手会撕掉你的脸(我知道,它发生在我身上,我现在没有脸); 确保系统只有一份副本,这样 ASDF 就不能使用源注册表头发我避免考虑找到错误的; 显式加载系统定义文件 - 无论 ASDF 在哪里查找,它至少会在与该目录相同的目录中查找。用于生产
答案是Quicklisp。做任何需要让 Quicklisp 安装在你的 Lisp 中。然后你需要知道它的安装目录在哪里:有一些默认值,但我从不使用它,因为我对文件系统应该是什么样子有自己的概念。
那么诀窍是 Quicklisp 将在其local-projects
目录下查找、构建和加载系统(据我所知,Quicklisp 完全由能力和魔法组成)。因此,如果您在那里放置一个系统,那么 Quicklisp 将简单而优雅地处理将其放入运行映像中。
要进行此安装...我有生成文件。我知道,我应该使用 Lisp 工具,但我生活在 *nix 平台上,make
和 install
擅长整个复制文件位。
Makefile
的相关部分(实际上这就是全部)是:
# Where Quicklisp lives, and the name of this project using slashes
QUICKLISP = /local/tfb/packages/quicklisp
THIS = org/tfeb/sample
FASLS = *fasl *fsl
.PHONY: install uninstall clean
# To install the project make its directory, copy all the sources and
# the sysdcl into it, and then nuke Quicklisp's cache file so it searches
# next time
#
install:
@mkdir -p "$(QUICKLISP)/local-projects/$(THIS)"
@cd "$(QUICKLISP)/local-projects/$(THIS)" && rm -f $(FASLS)
@install -C -v -m 444 *.lisp *.asd "$(QUICKLISP)/local-projects/$(THIS)"
@rm -f "$(QUICKLISP)/local-projects/system-index.txt"
# To uninstall the project just nuke the directory and the cache
#
uninstall:
@rm -rf "$(QUICKLISP)/local-projects/$(THIS)"
@rm -f "$(QUICKLISP)/local-projects/system-index.txt"
clean:
@rm -f $(FASLS) *~
这里有四件有趣的事情:
我只是将make
用作文件复制机——它不会编译任何东西或类似的东西,而且完全可以使用脚本;
您需要清除 Quicklisp 的缓存文件,以便它在启动时再次搜索;
我禁用了 ASDF 的输出翻译,这就是我花时间清理已编译文件的原因 - 安装后,项目总是应该从冷态重建;
uninstall
目标是您在开发之前需要运行的目标 - 它会破坏已安装的版本,因此 ASDF 找不到它。
在项目目录中运行合适的make install
后,(ql:quickload :org.tfeb.sample)
将为您编译和加载它。
请注意,另一种方法(由 Ehvince 在评论中建议)是将 Quicklisp 的 local-projects
目录下的符号链接保留回代码的规范版本。我不这样做,但它会工作得很好,并且在某些方面可能会更好。
【讨论】:
Quicklisp 的本地项目中的符号链接也有效。 @Ehvince:是的,这是一个很好的观点——我已经在答案中添加了一个注释,以防评论消失【参考方案2】:我发现可以做到以下几点:
$ sbcl
* (require "asdf")
* (asdf:load-asd "/absolute/path/to/my-project/my-project.asd")
* (asdf:load-system :my-project)
注意:
(require "asdf")
是加载 ASDF 的推荐方式,根据 ASDF manual 的“加载 ASDF”部分。
注意:除 GNU CLISP 之外的所有实现也接受
(require "ASDF")
、(require 'asdf)
和(require :asdf)
。为了便于携带,您应该使用(require "asdf")
。
asdf:load-asd
在给定的路径不正确 (!) 时不会因任何错误而失败,因此请确保给定的绝对路径是正确的。
使用cl:load
代替asdf:load-asd
似乎也可行,但ASDF 手册明确警告不要这种做法:
确实,ASDF 不会简单地使用
cl:load
加载.asd
文件,您也不应该。当您在系统上操作时,您应该让 ASDF 找到并加载它们。如果您必须以某种方式加载.asd
文件,请使用与ASDF 相同的函数asdf:load-asd
。除此之外,它已经将*package*
绑定到asdf-user
。当您使用slime-asdf
贡献时,最新版本的 SLIME(2013-02 及更高版本)知道当您C-c C-k
时会这样做。
【讨论】:
+1。所以它给出了一个命令:sbcl --load my-project.asd --eval '(asdf:load-system :my-project)'
。这会给你一个提示。您可以添加对uiop:quit
或asdf:make
等的调用。【参考方案3】:
您需要告诉 asdf 在哪里可以找到您的项目。
以下是相关参考资料:
https://common-lisp.net/project/asdf/asdf/Configuring-ASDF-to-find-your-systems.html
引用上述资源:
首先创建目录 ~/.config/common-lisp/source-registry.conf.d/;那里创建一个文件 使用您选择的任何名称,但类型为 conf,例如 50-luser-lisp.conf;在这个文件中,添加以下行告诉 ASDF 递归扫描 /home/luser/lisp/ 下的所有子目录 .asd 文件:(:tree "/home/luser/lisp/")
这就够了。您可以将 /home/luser/lisp/ 替换为您想要的任何位置 安装您的源代码。您实际上不需要指定 如果你使用默认的 ~/common-lisp/ 和你的 实现提供 ASDF 3.1.2 或更高版本。如果你的实现 提供了 ASDF 3 的早期变体,您可能需要指定 (:tree (:home "common-lisp/")) 用于引导目的,然后安装一个 ~/common-lisp/asdf/ 下 ASDF 的最新源代码树。
链接中还有更多内容。
【讨论】:
如果my-project/
可以在任何地方,我是否应该告诉 ASDF 从我的主目录 (~
) 的根目录递归扫描?
@Flux 不,不要那样做!
我认为没有必要弄乱源注册表,尤其是当我们在 .asd 的同一目录中时(因为我们可以--load
它)。【参考方案4】:
我做了类似你发现的事情。它的要点是--load
.asd。
先决条件
my-project.asd
以(require "asdf")
开头。
(require "asdf")
(asdf:defsystem "my-project"
:version "0.0"
…)
当我在 Slime 上时,我可以C-c C-k
(编译和加载)这个文件。
我不太确定“--load”是否/为什么需要它。
单线
我可以通过一次 Lisp 编译器的调用来构建项目:
sbcl --load my-project.asd --eval '(asdf:load-system "my-project")'
这会加载它并给我一个 REPL。我可以添加--eval (uiop:quit)
退出。
注意:我听说人们说最好使用asdf:load-asd
。
使用 Quicklisp - 当你有依赖项时是必需的
我实际上使用的是 Quicklisp 而不是 asdf:load-system
,因为它会加载我项目的依赖项。
sbcl --load my-project.asd --eval '(ql:quickload "my-project")'
(请注意,我没有将我的项目复制到 Quicklisp 的本地项目中。如果我这样做了,我就不需要在此处加载 .asd)
使用 Makefile
这一行可以变成一个简单的 Makefile。
LISP ?= sbcl
build:
$(LISP) --load my-project.asd \
--eval '(ql:quickload :my-project)' \
# more rules here
--eval '(quit)'
使用 lisp 文件进行简化以全部运行
我们需要:
1- 加载 .asd
2- 快速加载依赖项
3- 运行我们的脚本
我们也可以从 lisp 文件中执行此操作。
run.lisp
:
(load "my-project.asd") ;; relative path: we must be in the same directory
(ql:quickload "my-project") ;; I installed Quicklisp and my ~/.sbclrc has the Quicklisp-initialization snippet
(my-project::main) ;; whatever function acts as the entrypoint
(uiop:quit) ;; portable way to quit.
构建二进制文件
我使用asdf:make
,如下所述:https://lispcookbook.github.io/cl-cookbook/scripting.html#with-asdf
【讨论】:
以上是关于如何从 .asd 文件的目录编译和运行 Common Lisp 程序?的主要内容,如果未能解决你的问题,请参考以下文章