Как скомпилировать и запустить программу на Common Lisp из каталога файла .asd?

У меня есть следующая структура каталогов:

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)

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
7
0
2 324
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вам нужно сообщить 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/.

По ссылке еще много всего.

Должен ли я сказать ASDF рекурсивно сканировать из корня моего домашнего каталога (~), если my-project/ может быть где угодно?

Flux 15.12.2020 11:59

@Flux Нет, не делай этого!

Ehvince 15.12.2020 12:32

Я не думаю, что возиться с исходным реестром необходимо, особенно когда мы находимся в том же каталоге, что и .asd (поскольку мы можем это --load).

Ehvince 15.12.2020 12:32

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

Для развития

Убедитесь, что существует только одна копия системы, которую вы пытаетесь построить. Поэтому, в частности, убедитесь, что нет установленной копии, которую может найти 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 ...)
[...]
*

А теперь ты в порядке. Итак, три трюка, которые я делаю:

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

Для производства

Ответ на это 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 как машину для копирования файлов — она ничего не компилирует или что-то в этом роде, и было бы вполне возможно использовать скрипт;
  • вам нужно удалить кэш-файл Quicklisp, чтобы он снова выполнял поиск при запуске;
  • Я отключил выходные переводы ASDF, поэтому трачу время на сдувание скомпилированных файлов — после установки проект всегда нужно пересобирать с нуля;
  • цель uninstall — это то, что вам нужно запустить перед разработкой — она уничтожит установленную версию, поэтому ASDF не найдет ее.

Как только вы запустите подходящий make install в каталоге вашего проекта, (ql:quickload :org.tfeb.sample) просто скомпилирует и загрузит его для вас.

Обратите внимание, что альтернативный подход (предложенный Ehvince в комментарии) заключается в том, чтобы оставить символическую ссылку в каталоге Quicklisp local-projects на каноническую версию кода. Я так не делаю, но это будет работать нормально, а может быть, и лучше в некоторых отношениях.

Символическая ссылка в локальных проектах Quicklisp также работает.

Ehvince 15.12.2020 18:45

@Ehvince: да, это хороший момент - я добавил примечание к ответу на случай, если комментарий исчезнет

user5920214 16.12.2020 11:17
Ответ принят как подходящий

Я обнаружил, что можно сделать следующее:

$ 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 и т. д.

Ehvince 15.12.2020 12:10

Я делаю что-то подобное, как вы узнали. Суть в том, чтобы --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.

С Quicklisp - необходимо, когда у вас есть зависимости

Вместо asdf:load-system я использую Quicklisp, потому что он загружает зависимости моего проекта.

sbcl --load my-project.asd --eval '(ql:quickload "my-project")'

(Обратите внимание, что я не копировал свой проект в локальные проекты Quicklisp. Если бы я это сделал, мне не нужно было бы загружать здесь .asd)

С Makefile

Этот однострочный код можно превратить в простой Makefile.

LISP ?= sbcl
build:
    $(LISP) --load my-project.asd \
         --eval '(ql:quickload :my-project)' \
         # more rules here
         --eval '(quit)'

Упрощение с помощью файла lisp, чтобы запустить их все

Мы должны:

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

Другие вопросы по теме