5

我正在研究 SBCL 中的项目欧拉问题,并为每个解决方案保留一个短文件。每个问题都有一些基于凌晨 5 点的测试,这些测试来自“主要”测试套件。这些测试在“tests.lisp”运行时运行。由于我厌倦了手动维护文件列表,我编写了一些代码来为我做这件事:

(defpackage #:euler/asdf
  (:use :cl :asdf))
(in-package #:euler/asdf)

;; directory containing the problem files
(defparameter +dir+ "/home/stefan/quicklisp/local-projects/euler")

;; build file list for package components
(defun files-for-problems (dir)
  (mapcar #'(lambda (p) (list :file (pathname-name p) :depends-on '("package")))
      (directory (concatenate 'string dir "/e????.lisp"))))

;; build dependency list for all tests component
(defun depends-on-problems (dir)
  (mapcar #'pathname-name
      (directory (concatenate 'string dir "/e????.lisp"))))

;; define euler system
(defsystem euler
    :name "euler"
    :author "Stefan Schmiedl"
    :description "Solutions to problems at http://projecteuler.net"
    :depends-on ("iterate" "fiveam" "cl-csv")
    :components #.`((:file "package")
            ,@(files-for-problems +dir+)
         #.`(:file "tests" :depends-on ,(depends-on-problems +dir+))))

简而言之,defsystem euler使用所有 e????.lisp 文件作为组件,而 tests.lisp 依赖于所有这些文件。

这是一个好主意吗?是否有“官方”方式来defsystem使用目录中的所有文件或与给定文件名模式匹配的所有文件?

我觉得我在这里遗漏了一些基本的东西,尤其是在阅读了github 上一些关于“更具声明性的 defsystem”的 ELS 幻灯片之后,我上面所做的事情可能会被不赞成。


在摆弄了 Fare 的建议之后,我现在拥有的是:

;; define private package for defsystem
(defpackage #:euler-system
  (:use :cl :uiop :asdf))
(in-package #:euler-system)


;; define euler system
(defsystem "euler"
  :author "Stefan Schmiedl"
  :description "Solutions to problems at http://projecteuler.net"
  :depends-on ("iterate" "fiveam" "cl-csv")
  :components ((:module "package"
                        :pathname ""
                        :components ((:file "package")))
               (:module "problems"
                        :pathname ""
                        :depends-on ("package")
                        :components #.(mapcar #'(lambda (p) (list :file (pathname-name p)))
                                              (directory-files (pathname-directory-pathname
                                                                (uiop/lisp-build:current-lisp-file-pathname))
                                                               "e*.lisp")))
               (:module "tests"
                        :pathname ""
                        :depends-on ("package" "problems")
                        :components ((:file "tests")))))

感谢您的反馈。

4

2 回答 2

6

对于目录部分,我建议使用相对路径名。你可以通过几种方式做到这一点。

1-您不得使用绝对路径名。使用这样的相对路径名,可能通过变量:(subpathname (current-file-pathname) #p"e????.lisp")

2-我不确定?作为通配符的便携性如何——如果你能接受它,*它就更便携了。

3- uiop:directory-files 在这个和许多上下文中比 cl:directory 更安全。

4-对于没有#的处理通配符模式的“官方”方式。或 (eval `...),从 asdf/contrib/wild-modules.lisp 获得灵感——也就是说,一次性使用 #。是完全可以接受的,特别是因为我们离纯粹的声明性 .asd 文件还很远。

5-对于分组依赖项,您可以使用

(defsystem "euler"
  :depends-on ("iterate" "fiveam" "cl-csv")
  :serial t
  :components
   ((:module "package" :pathname ""
       :components ((:file "package")))
    (:module "problems" :pathname "" :depends-on ("package")
       :components #.(mapcar ...))
    (:module "tests" :pathname ""
       :components ((:file "tests")))))

6-您可以使用辅助系统而不是模块,此时system-relative-pathname可以使用:

(defsystem "euler" :depends-on ("euler/tests"))
(defsystem "euler/tests"
  :depends-on ("euler/package")
  :components ((:file "package")))
(defsystem "euler/problems"
  :depends-on ("euler/package")
  :components
    #.(mapcar ... (directory-files (system-relative-pathname "euler" #p"e*.lisp")))))
(defsystem "euler/tests"
  :depends-on ("euler/problems")
  :components ((:file "tests")))

7-在上面我假设asdf3,并且您使用uiop不带前缀:

(defpackage :euler-system (:use :cl :uiop :asdf))
(in-package :euler-system)

如果你没有定义任何函数或变量或类,你可以直接(in-package :asdf)

很高兴您喜欢我在 ELS 2013 上的演讲。我在 ELS 2014 上发表了另一篇演讲,在同一个存储库中。

于 2014-06-12T03:27:45.147 回答
3

ASDF 提供了三种内置组件类型,您:file仅在系统定义中使用简单组件类型。通常,为了将一些文件组合在一起,会引入单独的模块(它们几乎可以直接转换到不同的目录),但是模块仍然需要您指定(子)组件,然后您又回到了开始的地方。我简要地查看了是否有支持您构建的功能的 ASDF 扩展,但没有找到任何东西。因此,虽然您的代码可能存在一些小问题(例如通配符语法可能无法跨实现移植),但您的一般方法对我来说看起来不错。

为了解决您关于这是否是一个好主意的第二个问题:查看 make 的隐含规则,我认为拥有这样的东西可能会有用。但是,通常情况下,您在各种文件之间存在依赖关系,一旦您需要指定这些文件,您基本上就不得不列出组件及其依赖关系。defsystem(s) 的整个想法是能够指定依赖关系和所需的序列化。因此,您的用例可能不太常见,这可能解释了为什么您找不到一个容易提供的解决方案。

于 2014-06-11T20:05:55.590 回答