Как заставить defsystem использовать все?

Я работаю над задачами Эйлера проекта в 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 в качестве компонентов, а тесты.lisp зависят от всех этих файлов.

Это хорошая идея? Есть ли «официальный» способ заставить defsystem использовать все файлы в каталоге или все файлы, соответствующие заданному шаблону имени файла?

Я чувствую, что упускаю здесь что-то элементарное, особенно после прочтения некоторых слайды ELS на github о "более декларативной системе защиты", где то, что я сделал выше, вероятно, будет неодобрительно.


После некоторого возни с предложением Фаре вот что у меня теперь есть:

;; 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")))))

Спасибо за ответ.


person Stefan Schmiedl    schedule 11.06.2014    source источник
comment
FWIW, я бы не рекомендовал помещать ваши тесты в вашу систему в целом: кто-то, использующий вашу систему Эйлера, вполне может захотеть запустить ее без установки и загрузки вашей тестовой библиотеки. (Я понимаю, что для упражнения Эйлера проекта эта проблема может не применяться, поэтому я сказал в целом.) В этом случае я бы добавил систему Эйлера/теста, чтобы заменить ваш модуль, и добавил бы порядок-к чтобы запустить тестовую операцию на euler, вам нужно запустить тестовую операцию на euler/test. У нас есть дополнение FiveAM-asdf, упрощающее указание таких TEST-OP.   -  person Robert P. Goldman    schedule 13.06.2014
comment
То есть, на самом деле, в моем расписании, как только я достиг проблемы 100 :-)   -  person Stefan Schmiedl    schedule 15.06.2014


Ответы (2)


Для части каталога я рекомендую использовать относительные пути. Вы можете сделать это несколькими способами.

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 в том же репозитории.

person Faré    schedule 12.06.2014
comment
Выступление 2014 года — это место, где я получил URL-адрес репозитория, я посмотрел видео вашей презентации, и расширенная версия действительно близка к началу моего списка чтения. Спасибо вам за вашу работу. - person Stefan Schmiedl; 12.06.2014
comment
Я собираюсь переключиться на модули (ваш № 5), так как это устраняет дублирование в моем коде. Кроме того, вторая дефсистема в вашем № 6, кажется, страдает копипастеритом ;-) - person Stefan Schmiedl; 12.06.2014
comment
Если модуль имеет только один файл, на самом деле это не обязательно должен быть модуль — просто используйте :file. Виноват. Кроме того, вам не нужно уточнять что-либо с помощью uiop/foo:, если вы уже используете :use'ing :uiop. Наконец, поскольку вы не определяете какую-либо функцию или переменную, вы можете просто (in-package :asdf) вместо создания собственного пакета. - person Faré; 14.06.2014

ASDF предоставляет три встроенных типа компонентов, вы используете простой тип компонента :file только в определении вашей системы. Как правило, для группировки некоторых файлов вместе вводятся отдельные модули (которые в значительной степени напрямую переводятся в разные каталоги), но модули по-прежнему требуют, чтобы вы указали (под)компоненты, и тогда вы вернетесь к тому, с чего начали. Я кратко посмотрел, будет ли расширение ASDF, поддерживающее созданную вами функциональность, но ничего не нашел. Таким образом, хотя в вашем коде могут быть некоторые незначительные проблемы (например, синтаксис подстановочных знаков, вероятно, не переносимый между реализациями), ваш общий подход выглядит хорошо для меня.

Чтобы ответить на ваш второй вопрос о том, является ли это хорошей идеей: Глядя на неявные правила make, я думаю, что иметь что-то подобное может быть полезно. Однако чаще всего у вас есть зависимости между различными файлами, и как только вам нужно указать их, вы в основном возвращаетесь к необходимости перечислять компоненты и их зависимости. Вся идея defsystem(s) состоит в том, чтобы иметь возможность указывать зависимости и требуемые сериализации. Таким образом, ваш вариант использования может быть не слишком распространенным, что может объяснить, почему вы не можете найти легко предоставляемое решение для этого.

person schaueho    schedule 11.06.2014
comment
План состоит в том, чтобы создать библиотечный модуль, и файлы решения будут зависеть от него. Я не думаю, что этот практический проект оправдывает усилия по точной настройке зависимостей. - person Stefan Schmiedl; 12.06.2014