【问题标题】:How do I compile and run a Common Lisp program from the directory of the .asd file?如何从 .asd 文件的目录编译和运行 Common Lisp 程序?
【发布时间】: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 版)

【问题讨论】:

    标签: common-lisp asdf


    【解决方案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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-03
      • 2010-11-27
      • 2010-10-24
      • 2012-07-31
      相关资源
      最近更新 更多