У меня есть следующая структура каталогов:
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))
Проблема в том, как мне скомпилировать и запустить main.lisp
с помощью ASDF?
Мне удалось скомпилировать и запустить программу:
$ mv my-project ~/common-lisp/.
$ sbcl
* (require :asdf)
* (asdf:load-system :my-project)
Однако это невероятно глупо. Я не хочу перемещать свой проект в ~/common-lisp/
только для того, чтобы запустить его. Я хочу скомпилировать и запустить программу прямо из каталога проекта. Каталог my-project/
может быть где угодно, и я хочу, чтобы его можно было разместить где угодно. Другими словами, я хотел бы загрузить систему из текущего каталога.
Подумайте о make
, где я могу компилировать файлы прямо из каталога самого Makefile
. Как мне аналогичным образом скомпилировать и запустить программу на Common Lisp из каталога самого файла *.asd
?
(Я использую SBCL версии 1.4.5 и ASDF версии 3.3.1)
Вам нужно сообщить 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: (:дерево "/home/luser/lisp/")
Достаточно. Вы можете заменить /home/luser/lisp/ на любое другое место. для установки вашего исходного кода. На самом деле вам не нужно указывать ничего, если вы используете ~/common-lisp/ по умолчанию, как указано выше, и ваш реализация обеспечивает ASDF 3.1.2 или более позднюю версию. Если ваша реализация предоставляет более ранний вариант ASDF 3, вы можете указать (:tree (:home "common-lisp/")) для начальной загрузки, затем установите недавнее исходное дерево ASDF в ~/common-lisp/asdf/.
По ссылке еще много всего.
@Flux Нет, не делай этого!
Я не думаю, что возиться с исходным реестром необходимо, особенно когда мы находимся в том же каталоге, что и .asd (поскольку мы можем это --load
).
Это то, что я делаю, чтобы решить эту проблему. Возможно, это не совсем то, что вам нужно, но здесь могут быть идеи, которые могут помочь.
Убедитесь, что существует только одна копия системы, которую вы пытаетесь построить. Поэтому, в частности, убедитесь, что нет установленной копии, которую может найти 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 ...)
[...]
*
А теперь ты в порядке. Итак, три трюка, которые я делаю:
Ответ на это Quicklisp. Сделайте все необходимое, чтобы Quicklisp был установлен в вашем Lisp. Затем вам нужно знать, где находится его каталог установки: есть какой-то вариант по умолчанию, но я никогда не использую его, так как у меня есть свои представления о том, как должна выглядеть файловая система.
Тогда хитрость в том, что Quicklisp найдет, создаст и загрузит системы в своем каталоге local-projects
(насколько я могу судить, Quicklisp полностью сделан из компетентности и магии). Так что если поставить туда систему, то Quicklisp просто и элегантно справится с ее попаданием в работающий образ.
Чтобы сделать эту установку... У меня есть make-файлы. Я знаю, что мне следует использовать инструменты Lisp, но я живу на платформах *nix, а make
и install
хорошо умеют копировать файлы.
Соответствующая часть 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
как машину для копирования файлов — она ничего не компилирует или что-то в этом роде, и было бы вполне возможно использовать скрипт;uninstall
— это то, что вам нужно запустить перед разработкой — она уничтожит установленную версию, поэтому ASDF не найдет ее.Как только вы запустите подходящий make install
в каталоге вашего проекта, (ql:quickload :org.tfeb.sample)
просто скомпилирует и загрузит его для вас.
Обратите внимание, что альтернативный подход (предложенный Ehvince в комментарии) заключается в том, чтобы оставить символическую ссылку в каталоге Quicklisp local-projects
на каноническую версию кода. Я так не делаю, но это будет работать нормально, а может быть, и лучше в некоторых отношениях.
Символическая ссылка в локальных проектах Quicklisp также работает.
@Ehvince: да, это хороший момент - я добавил примечание к ответу на случай, если комментарий исчезнет
Я обнаружил, что можно сделать следующее:
$ sbcl
* (require "asdf")
* (asdf:load-asd (merge-pathnames "my-project.asd" (uiop:getcwd)))
* (asdf:load-system :my-project)
Примечание:
(require "asdf")
— рекомендуемый способ загрузки ASDF в соответствии с разделом «Загрузка ASDF» руководства ASDF.
NB: все реализации, кроме GNU CLISP, также принимают
(require "ASDF")
,(require 'asdf)
и(require :asdf)
. Для переносимости вы должны использовать(require "asdf")
.
asdf:load-asd
должен быть абсолютным путем и не может привести к ошибке, если указанный путь неверен (!), поэтому убедитесь, что данный абсолютный путь верен.
Использование cl:load
вместо asdf:load-asd
также может работать, но руководство ASDF прямо предостерегает от такой практики:
Действительно, ASDF не загружает файлы
.asd
просто с помощьюcl:load
, и вы не должны этого делать. Вы должны разрешить ASDF находить и загружать их при работе с системами. Если вам каким-то образом необходимо загрузить файл.asd
, используйте ту же функциюasdf:load-asd
, которую использует ASDF. Среди прочего, он уже связывает*package*
сasdf-user
. Последние версии SLIME (2013-02 и более поздние версии) умеют делать это, когда выC-c C-k
когда используетеslime-asdf
contrib.
+1. Так по одной команде выдает: sbcl --load my-project.asd --eval '(asdf:load-system :my-project)'
. Это дает вам подсказку. Вы можете добавить вызов uiop:quit
или asdf:make
и т. д.
Я делаю что-то подобное, как вы узнали. Суть в том, чтобы --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
.
Вместо asdf:load-system
я использую Quicklisp, потому что он загружает зависимости моего проекта.
sbcl --load my-project.asd --eval '(ql:quickload "my-project")'
(Обратите внимание, что я не копировал свой проект в локальные проекты Quicklisp. Если бы я это сделал, мне не нужно было бы загружать здесь .asd)
Этот однострочный код можно превратить в простой Makefile.
LISP ?= sbcl
build:
$(LISP) --load my-project.asd \
--eval '(ql:quickload :my-project)' \
# more rules here
--eval '(quit)'
Мы должны:
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
Должен ли я сказать ASDF рекурсивно сканировать из корня моего домашнего каталога (
~
), еслиmy-project/
может быть где угодно?