如何从 .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 平台上,makeinstall 擅长整个复制文件位。

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:quitasdf: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 程序?的主要内容,如果未能解决你的问题,请参考以下文章

如何删除子目录下所有指定后缀文件?

如何使用命令行编译和运行java文件

如何从 docker 容器运行 webpack 构建?

如何从 POST 请求上传节点中的图像

如何在 SSH 下远程使用 IDE 编译和调试

如何从 git 中排除打字稿编译的文件