通用Lisp源代码组织

8
我是CL的新手,正在使用AllegroCL。我试图弄清楚如何组织我的源代码以满足以下要求:
  1. 我希望防止源代码包含测试套件。
  2. 我希望以便携的方式声明项目依赖项(包括src和test deps),以便我的团队成员不必修改其系统。
  3. 我想简化持续集成的检查,包括构建和测试。
我一直在尝试创造性地使用ASDF来满足这些要求,但我做不对。其他人是如何解决这个问题的呢?这两个要求是不是不符合"Lispy"的风格?
5个回答

5

使用ASDF或使用Allegro CL defsystem工具。

  1. 将它们作为两个不同的系统。测试套件系统依赖于软件系统。
  2. 使用相对路径名,基于系统定义文件的位置计算绝对路径名,或者使用逻辑路径名(这是CL中可以根据规则重新映射的路径名)的“pro”版本。
  3. 可能有一个Common Lisp的持续集成工具,但我还没有使用过任何一个。拥有一个defsystem描述是一个很好的开始。

很好。我一直在尝试使用ASDF。如何声明相对依赖项?asdf:defsystem中的:defsystem-depends-on不接受路径名,只接受系统名称。我尝试在测试asd中调用asdf:initialize-source-registry,但无法使其工作。 - jennykwan
1
我们使用Hudson来自动化并在git推送时运行CL单元测试。只要这些测试可以从shell启动,Hudson作为Common Lisp的CI工具就可以正常工作。可能会有更专门的Common Lisp CI工具,但我也没有使用过任何一个。 - Clayton Stanley
@amaevis:请单独发布这样更具体的问题。 - Rainer Joswig

5
我正在使用Quicklisp,它会在你的主目录下创建一个"quicklisp"文件夹,在其中可以找到一个"local-project"文件夹。该文件夹中包含一个文本文件,您可以在其中插入.asd文件的URI。
如何使用此实用程序:
- 在项目文件夹中创建 "project.asd" 和 "project-test.asd" - project.asd(管理纯项目代码的包含)
(asdf:defsystem :project-name
  :description "description here"
  :version "version here"
  :author "your name here"
  :depends-on (:a
               :list 
               :of
               :dependencie
               :libraries)
  :components ((:file "sourcefileone")
               (:file "sourcefiletwo")))

project-test.asd(管理测试代码的包含文件)

(asdf:defsystem :project-name-test
  :description "testing"
  ...
  :depends-on (:project-name)
  :components ((:file "sourcefileone-test")
               (:file "sourcefiletwo-test")))
  • 现在将这些文件的URI插入到上述命名为local-projects.txt的文件中

  • 将项目源代码并行化到.lisp文件中,将测试调用并行化到-test.lisp文件中(* -test.lisp文件必须包含一个test-execute调用)

  • 启动你的sbcl或者其他工具,然后使用(ql:quickload "project-name")或者(ql:quickload "project-name-test"),具体取决于你是要加载一个项目还是测试它。

唯一需要做的是,在复制项目的计算机上编写local-projects.txt。之后,你的同事可以在任何其他项目中使用asdf文件和quickload来依赖它。对于复制项目文件夹,你可以使用ctrl+c/v或者更先进的工具如git。

对于测试,我编写了自己的小型测试套件,但我敢打赌有更好的选择。更多关于quicklisp的信息可以在这里找到,关于asdf的信息可以在这里找到。如果你在配置quicklisp时遇到困难,也许这个问题可以帮助你。


感谢您提供详细的答案。我唯一不喜欢这种方法的地方是,依赖项由Quicklisp安装到系统中,而不是项目中。在像Clojure的Leiningen这样的工具中,依赖项和测试依赖项会被下载到项目中,并从那里加载。这可以防止项目中的版本与系统中的版本发生潜在的冲突。 - jennykwan

5
如果已经安装了Quicklisp,您可以使用内置功能Quickproject。
(ql:quickload "quickproject")
(quickproject:make-project "~/src/lisp/swatchblade/"
                         :depends-on '(vecto hunchentoot))

这将创建4个文件:
  • package.lisp
  • swatchblade.lisp
  • swatchblade.asd
  • README.txt

package.lisp定义了包命名空间:

(defpackage #:swatchblade  
(:use #:cl)
  (:shadowing-import-from #:vecto
                          #:with-canvas
                          #:rounded-rectangle
                          #:set-rgb-fill
                          #:save-png-stream))

swatchblade.asd 定义了系统/项目、源代码文件、依赖项等。

(asdf:defsystem #:swatchblade 
 :serial t
  :depends-on (#:vecto
               #:hunchentoot
               #:cl-colors)
  :components ((:file "package")
               (:file "swatchblade")))

swatchblade.lisp是源代码的位置。

您可以通过Quicklisp的quickload加载该项目:

* (ql:quickload "swatchblade")
loading output
* (swatchblade:start-web-server :port 8080)
Server started on port 8080.

如果您创建了另一个依赖于swatchblade系统的项目:
quickproject:make-project "~/src/lisp/whimsytron/" 
                       :depends-on '(swatchblade))

关于测试,您可以在package.lisp中为您的测试添加另一个命名空间:

    (defpackage #:swatchblade-tests  
      (:use #:cl #:swatchblade))

创建一个测试文件,编写代码,并将该文件添加到系统定义中:
(asdf:defsystem #:swatchblade 
 :serial t
  :depends-on (#:vecto
               #:hunchentoot
               #:cl-colors)
  :components ((:file "package")
               (:file "swatchblade")
               (:file "swatchglade-tests")))

加载swatchblade-tests命名空间以运行测试。

这里是包含测试的示例项目

如果您想避免Quicklisp将所有依赖项安装到系统中,据我所知,您将不得不手动安装依赖项并加载系统。

Quicklisp的作者Zach Beane在使用quickproject上发布了更详细的帖子。


1
我使用不同的测试包遇到的问题是,我必须导出我想要测试的所有函数。 - PuercoPop
你曾经想出解决方案了吗,@PuercoPop? - Michael Fox
@MichaelFox 我尝试了不同的方法。a) 只测试导出的API。b) 在测试包中使用import-from导入符号。 - PuercoPop

4
根据Rainer的建议,我建议您使用ASDF系统定义工具来定义两个系统:主要系统foo和附属系统foo-tests
foo系统的定义中,添加一个规范说明,在执行test-op测试操作时需要先对foo-tests进行测试。这样做可以确保如果您执行(asdf:test-system "foo"), 相应的测试系统及其依赖项将被加载,然后ASDF会执行test-op测试操作。
我认为FiveAM是一个适合构建测试的库。
以上步骤将使所有内容都被加载,但现在您需要确保在foo-tests上执行test-op测试操作时实际运行测试!为此,您需要在PERFORM方法和(eql (find-system "foo-tests"))上添加一个方法。该PERFORM方法应调用您定义的所有FiveAM测试,并成功通过或者如果测试失败则引发错误。
我已经为ASDF制作了一个FiveAM-tester插件。 我会尝试将其公开发布。

1

重要提示

上述建议很好,但是如果你尝试测试未导出的内容,你会感到沮丧。一个简单的解决方法是不要定义两个包。将你的测试放在与其他源代码相同的包中。有什么坏处呢?

如果你认为会有害处,那么你将不得不像这样做:

(defpackage #:sources
  (:use #:cl))

(defpackage #:tests
  (:use #:cl #:lisp-unit)
  (:import-from #:sources))

重要的部分是从源包中 :import-from 而不是使用 :use
然后,当您在测试包中使用源包中的符号时,您将不得不限定这些符号。例如,假设您有以下函数:
(defun return-true () t)

Your test might look like:

(define-test test-return-true
  "Make sure it works"
  (assert-equal t (sources::return-true)))

重要的部分是你说的是 (sources::return-true) 而不仅仅是 (return-true)。对于像 'sym 这样的符号,也是一样的;将其称为 'sources::sym

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接