almost 5 years ago

Допустим, у вас есть классная маленькая программка на Common Lisp под названием
filter. Она занимает всего один файл исходного кода под названием filter.lisp
примерно в 200 строк длиной. В этом файле вот такой заголовок:

(defpackage :localhost.me.filter
  (:use :common-lisp)
  (:export :run))
(in-package :localhost.me.filter) 

И дальше собственно сама программа. Допустим также, что вы пользуетесь своей
программой часто. Загвоздка в том, что для того, чтобы её запустить, вам
необходимо сделать слишком много действий:

$ cd $PROGRAMDIR 
$ sbcl 
CL-USER> (load "filter.lisp") CL-USER> (:localhost.me.filter:run) 

Рассмотрим два решения, одно простое, но не всегда поможет, другое посложнее
и поможет уже в большем числе случаев.

Решение первое

SBCL умеет запускать программы на CL в режиме скриптов командной строки, для
этого используется специальный флаг --script. Более того, благодаря этому
можно написать лисп-программу, добавить к ней типичный юниксовый шебанг, вызывающий
SBCL с этим флагом, и она будет работать как любой другой шелл-скрипт (за исключением
того, что рантайм SBCL весит 50MB бгг). Так что напишем этот скрипт:

#!/usr/bin/sbcl --script
(load "filter.lisp")
(:localhost.me.filter:run)

Теперь положим этот скрипт в ту же директорию, что и filter.lisp, в файл
с именем run и можно будет запускать нашу программу так:

$ ./run

Что и требовалось.

Решение второе

Однако, у флага --script есть одна особенность: SBCL не загружает никакие
пользовательские скрипты инициализации при старте, так что скрипт будет выполняться
в 100% дефолтном рантайме. Это плохо, если, допустим, вы используете Quicklisp
и у вас в filter.lisp есть такой вызов:

... (ql:quickload :CL-FAD) (use-package :CL-FAD) ...

Ну или вызов любой другой библиотеки, неважно. Если запустить такой скрипт
первым способом, то SBCL будет грязно ругаться по поводу того, что он не знает,
что такое ql:quickload.

Для решения этой проблемы мы смухлюем, подменив бинарник SBCL таким бинарником,
у которого все нужные библиотеки уже загружены. В SBCL есть функция save-lisp-and-die,
которая выгружает текущее состояние рантайма в файл с указанным именем. Дальше
этот файл можно использовать как обычный бинарник SBCL, в том числе, и для
вызовов с использованием --script. Поэтому если запустить SBCL и сразу выгрузить
его в файл, то полученный бинарник будет содержать всё, что SBCL подключил
при загрузке, основываясь на пользовательских конфигах.

(save-lisp-and-die "/абсолютный/путь/до/файла")

Теперь в скрипте run, который написан по первому варианту, заменяем путь
до системного SBCL путём до выгруженного бинарника, и скрипт начнёт работать
как должен.

 
comments powered by Disqus