如何从 .asd 文件的目录编译和 运行 Common Lisp 程序?
How do I compile and run a Common Lisp program from the directory of the .asd file?
我的目录结构如下:
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 版)
您需要告诉 asdf 在哪里可以找到您的项目。
相关参考如下:
https://common-lisp.net/project/asdf/asdf/Configuring-ASDF-to-find-your-systems.html
引用上述资源:
First create the directory
~/.config/common-lisp/source-registry.conf.d/; there create a file
with any name of your choice but with the type conf, for instance
50-luser-lisp.conf; in this file, add the following line to tell ASDF
to recursively scan all the subdirectories under /home/luser/lisp/ for
.asd files: (:tree "/home/luser/lisp/")
That’s enough. You may replace /home/luser/lisp/ by wherever you want
to install your source code. You don’t actually need to specify
anything if you use the default ~/common-lisp/ as above and your
implementation provides ASDF 3.1.2 or later. If your implementation
provides an earlier variant of ASDF 3, you might want to specify
(:tree (:home "common-lisp/")) for bootstrap purposes, then install a
recent source tree of ASDF under ~/common-lisp/asdf/.
link 中还有更多内容。
这就是我解决这个问题的方法。它可能不是您所需要的,但这里的想法可能会有所帮助。
为了发展
确保您尝试构建的系统只有一个副本。因此,特别要确保没有 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 将简单而优雅地处理将它放入 运行ning 图像。
要执行此安装...我有生成文件。我知道,我应该使用 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
目录下保留一个符号 link 返回到代码的规范版本。我不这样做,但它会工作得很好,并且在某些方面可能会更好。
我发现可以执行以下操作:
$ sbcl
* (require "asdf")
* (asdf:load-asd (merge-pathnames "my-project.asd" (uiop:getcwd)))
* (asdf:load-system :my-project)
注:
(require "asdf")
是加载 ASDF 的推荐方式,根据 ASDF manual.
的“加载 ASDF”部分
NB: all implementations except GNU CLISP also accept (require "ASDF")
, (require 'asdf)
and (require :asdf)
. For portability’s sake, you should use (require "asdf")
.
asdf:load-asd
必须是绝对路径,并且当给定的路径不正确(!)时可能不会失败并出现任何错误,因此请确保给定的绝对路径是正确的。
使用 cl:load
而不是 asdf:load-asd
似乎也可行,但 ASDF 手册明确警告不要这样做:
Indeed, ASDF does not load .asd
files simply with cl:load
, and neither should you. You should let ASDF find and load them when you operate on systems. If you somehow must load a .asd
file, use the same function asdf:load-asd
that ASDF uses. Among other things, it already binds the *package*
to asdf-user
. Recent versions of SLIME (2013-02 and later) know to do that when you C-c C-k
when you use the slime-asdf
contrib.
我做了与您发现的类似的事情。它的要点是 --load
.asd.
先决条件
my-project.asd
以 (require "asdf")
开头。
(require "asdf")
(asdf:defsystem "my-project"
:version "0.0"
…)
当我在 Slime 上时,我可以C-c C-k
(编译并加载)这个文件。
我不太确定if/why“--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
我的目录结构如下:
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 版)
您需要告诉 asdf 在哪里可以找到您的项目。
相关参考如下:
https://common-lisp.net/project/asdf/asdf/Configuring-ASDF-to-find-your-systems.html
引用上述资源:
First create the directory ~/.config/common-lisp/source-registry.conf.d/; there create a file with any name of your choice but with the type conf, for instance 50-luser-lisp.conf; in this file, add the following line to tell ASDF to recursively scan all the subdirectories under /home/luser/lisp/ for .asd files: (:tree "/home/luser/lisp/")
That’s enough. You may replace /home/luser/lisp/ by wherever you want to install your source code. You don’t actually need to specify anything if you use the default ~/common-lisp/ as above and your implementation provides ASDF 3.1.2 or later. If your implementation provides an earlier variant of ASDF 3, you might want to specify (:tree (:home "common-lisp/")) for bootstrap purposes, then install a recent source tree of ASDF under ~/common-lisp/asdf/.
link 中还有更多内容。
这就是我解决这个问题的方法。它可能不是您所需要的,但这里的想法可能会有所帮助。
为了发展
确保您尝试构建的系统只有一个副本。因此,特别要确保没有 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 将简单而优雅地处理将它放入 运行ning 图像。
要执行此安装...我有生成文件。我知道,我应该使用 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
目录下保留一个符号 link 返回到代码的规范版本。我不这样做,但它会工作得很好,并且在某些方面可能会更好。
我发现可以执行以下操作:
$ sbcl
* (require "asdf")
* (asdf:load-asd (merge-pathnames "my-project.asd" (uiop:getcwd)))
* (asdf:load-system :my-project)
注:
的“加载 ASDF”部分(require "asdf")
是加载 ASDF 的推荐方式,根据 ASDF manual.NB: all implementations except GNU CLISP also accept
(require "ASDF")
,(require 'asdf)
and(require :asdf)
. For portability’s sake, you should use(require "asdf")
.asdf:load-asd
必须是绝对路径,并且当给定的路径不正确(!)时可能不会失败并出现任何错误,因此请确保给定的绝对路径是正确的。使用
cl:load
而不是asdf:load-asd
似乎也可行,但 ASDF 手册明确警告不要这样做:Indeed, ASDF does not load
.asd
files simply withcl:load
, and neither should you. You should let ASDF find and load them when you operate on systems. If you somehow must load a.asd
file, use the same functionasdf:load-asd
that ASDF uses. Among other things, it already binds the*package*
toasdf-user
. Recent versions of SLIME (2013-02 and later) know to do that when youC-c C-k
when you use theslime-asdf
contrib.
我做了与您发现的类似的事情。它的要点是 --load
.asd.
先决条件
my-project.asd
以 (require "asdf")
开头。
(require "asdf")
(asdf:defsystem "my-project"
:version "0.0"
…)
当我在 Slime 上时,我可以C-c C-k
(编译并加载)这个文件。
我不太确定if/why“--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