我正在研究SBCL中的项目euler问题,并为每个解决方案保留一个简短的文件。每个问题都有一些基于5 am的测试,这些测试是从“主要”测试套件中引用的。运行“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
使用目录中的所有文件或匹配给定文件名模式的所有文件?
我觉得我在这里缺少一些基本的东西,特别是在阅读了一些关于“更具声明性的定义系统”的ELS slides on github后,我上面所做的事情可能会不受欢迎。
经过一些摆弄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")))))
感谢您的反馈。
答案 0 :(得分:6)
对于目录部分,我建议使用相对路径名。你可以采取多种方式。
1-你不能使用绝对路径名。像这样使用相对路径名,可能通过变量:(subpathname (current-file-pathname) #p"e????.lisp")
2-我不确定便携式?
是如何作为通配符 - 如果你可以使用它,*
便携得多。
3- uiop:在这个和许多上下文中,目录文件比cl:目录更安全。
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上又在同一个存储库中给了另一个。
答案 1 :(得分:3)
ASDF提供了三种内置组件类型,您只在系统定义中使用简单的:file
组件类型。通常,为了将一些文件分组在一起,可以引入单独的模块(几乎直接转换为不同的目录),但模块仍然需要您指定(子)组件,然后您又回到了开始的位置。我简要地看了一下ASDF扩展是否支持您构建的功能,但没有找到任何东西。因此,虽然您的代码可能存在一些小问题(例如通配符语法可能无法跨实现移植),但您的一般方法对我来说很好。
解决你的第二个问题:这是一个好主意:看看make的隐含规则,我认为有这样的东西可能会有用。但是,通常情况下,您在各种文件之间存在依赖关系,并且只要需要指定这些依赖关系,您基本上就必须列出组件及其依赖关系。 defsystem的整个想法是能够指定依赖关系和所需的序列化。因此,您的用例可能不会太常见,这可能解释了为什么您找不到一个容易提供的解决方案。