Pull to refresh

Установка common lisp на правах пользователя

Reading time5 min
Views3K
made with lisp
Есть проблема: На рабочих серверах (у нас используется политика тонких клиентов, толстых серверов) не установлено никакой lisp машины, и я, разумеется, не администрирую их.
На ум сразу приходят 2 решения:
  • Уговорить администратора.
  • Справиться самостоятельно.

Первый вариант подходит для настоящих внедренцев. Я к сожалению не владею достаточными аргументами, почему вдруг все терминальные сервера должны обзавестись ещё и лисп машиной.
Поэтому здесь пойдёт речь о втором варианте. (А именно о ECL на linux в custom каталоге).


Идя по этому пути, я в первую очередь вспомнил 2 реализации, с которыми работаю дома:
  • ANSI Common Lisp (clisp)
  • Steel Bank Common Lisp (sbcl)

Ни одну из них мне не удалось заставить работать из нестандартной директории, если кто может подсказать, милости прошу. Тут я вспомнил про ECL, уже однажды выручившим меня, когда мне понадобилось встроить lisp движок в сишный проект.

Рабочее окружение


В нашей конторе есть множество идентчиных терминальных серверов, а также несколько сетевых шар. Домашние директории ограничены объёмом 100 МиБ поэтому устанавливать сюда что-либо мне очень не хочется. Но есть человеческое сетевое файловое хранилище, на которое в моей домашней директории указывает символьная ссылка «ns»:
 $ ls ~ -l
. . .
lrwxrwxrwx   1 necto users  14 Jul 22 12:22 ns -> /network/file/system/users/necto
. . . 


Установка


Как забрать дистрибутив написано на странице проекта. Самый простой путь (imho):
$ cd tmp && git clone git://ecls.git.sourceforge.net/gitroot/ecls/ecl && cd ecl 

Из ./configure --help можно почерпнуть, что для того, чтобы инсталлировать ecl в нестандартную директорию, достаточно указать пару префиксов, от которых он будет отталкиваться. Не долго думая устанавливаю нужные префиксы:
$ ./configure --prefix "~/ns/ecl/" --exec-prefix "~/ns/ecl/"

Но не тут-то было:
Switching to directory `build' to continue configuration.
configure: error: expected an absolute directory name for --exec_prefix: ~/ns/ecl/

Что ж, хорошо ещё, что симлинк не заставил разворачивать.
./configure --prefix "/home/necto/ns/ecl/" --exec-prefix "/home/necto/ns/ecl/"

Теперь канонично:
$ mkdir ~/ns/ecl && make &&  make install

У меня всё прошло без проблем, и в результате:
 $ ~/ns/ecl/bin/ecl
ECL (Embeddable Common-Lisp) 11.1.1
Copyright (C) 1984 Taiichi Yuasa and Masami Hagiya
Copyright (C) 1993 Giuseppe Attardi
Copyright (C) 2000 Juan J. Garcia-Ripoll
ECL is free software, and you are welcome to redistribute it
under certain conditions; see file 'Copyright' for details.
Type :h for Help.  
Top level.
> (quit)

Если после бесконфликтного make всё же:
 /home/necto/ns/ecl/bin/ecl: error while loading shared libraries: libecl.so.11.1: cannot open shared object file: No such file or directory 
Можно будет добавить /home/necto/ns/ecl/lib в LD_LIBRARY_PATH ($ export LD_LIBRARY_PATH=LD_LIBRARY_PATH:/home/necto/ns/ecl/lib ).

«Встройка»


В первую очередь я устанавливал ecl для того, чтобы писать скрипты (например для сбора статистика по дампам). А значит вот такой запуск интерпретатора отнюдь не удобен: $~/ns/ecl/bin/ecl -load tst.lisp
Поэтому необходимо пропатчить ./.bashrc. В той же директории ~/ns/ecl добавляю файлик to-bashrc:
#/bin/bash
# should be included into your .bashrc
# should be in .../ecl folder
# defines two commands:
# ecl - to run lisp interpreter in interactive mode [usage: ecl --help]
# cl $file - to execute lisp source with +cmd-args+ bind to
# list of commandline arguments

path_to_ecl=$(dirname ${BASH_SOURCE[0]})
# Use the next line, if ecl can't find  libecl.so
export LD_LIBRARY_PATH="$path_to_ecl"/lib:$LD_LIBRARY_PATH

alias ecl="$path_to_ecl"'/bin/ecl'

function ecl-run 
{
    line="(progn (defconstant +cmd-args+ '(${@})) (defconstant +/cl+ \"$path_to_ecl\/\") (load \"$path_to_ecl/my/common.lisp\" :verbose nil) (load \"$1\") (quit))"
    ecl -eval "$line"
}

alias cl='ecl-run'

Здесь, помимо тривиально алиаса добавлен вариант интерпретации файлов, с необходимыми функциями определёнными в ~/ns/ecl/my/common.lisp в котором помимо прочих удобных плюшек есть такие функции:
;;; common.lisp - aggregator of different tools
;;; being used in scripts

;; add standart lybrary path e. g. /home/necto/ns/ecl
:: use it on such way (load (lib/ "my/common.lisp"))
(defun lib/ (str)
  (if (find-symbol "+/CL+")
    (concatenate 'string +/cl+ str)
    str))

;; Another function useful in everyday scripting:
;; determine is a symb given as argument
(if (find-symbol "+CMD-ARGS+")
    (defun symbol-mentioned-in-params (symb)
      (find symb +cmd-args+)))

;; Use "verbose" or "talkative" to see debug info from
;; your script
(defun verbose-enabled-p () 
  (and (find-symbol "+CMD-ARGS+")
       (or (find 'talkative +cmd-args+)
           (find 'verbose +cmd-args+))))

;; A output stream which you can enable by taking option "talkative"
;; to a script using it
(defconstant extra-out (if (verbose-enabled-p)
                         t
                         (make-string-output-stream)))

;; Also if user doesn't want to view all it's loads, disable it:
(setf *load-verbose* (verbose-enabled-p))

;;...

Полный код
Кстати очень рекомендую pregexp — весьма лёгкая и библиотечка для работы с regexp'ами. Очень маленькая библиотечка всего 1 файл в ~800 строк. Она используется в ecl/my/common.lisp ну очень широко.

Также можно видеть, что будет определена константа +cmd-args+ содержащая список всех аргументов, переданных скрипту.
Теперь для интеграции всего этого хозяйства достаточно дописать в .bashrc:
 source ~/ns/ecl/to-bashrc 


Использование


Теперь достаточно создать, например, all-funs.lisp:

(defun print-funcs ()
    (process-every-file file (make-pathname :directory '(:relative) :name "*" :type "lisp") :input
        (for-every-appropriate-line file "defun ([^ ]+) \(([^)]*)\)" (funame args)
            (print-hello funame)
            (print args extra-out))))

(defun print-hello (name)
    (format t "~%Hello, ~a." name))

(print-funcs)

И запустить его:
$ cl all-funs.lisp
Hello, print-funcs.
Hello, print-hello. 

А если указать флаг talkative:
$ cl all-funs.lisp talkative
Hello, print-funcs.

Hello, print-hello.
name 


Но самое интересное, что если теперь коллеге захочется попользоваться вашими наработками, ему достаточно будет включить /net/file/system/users/necto/ecl/to-bashrc в свой .bashrc и всё!

Заключение



Собственно этим я хотел сказать, что встраивание common lisp в качестве удобного скриптового интерпретатора, на ряду с тем же python или perl довольно просто, и отнюдь не требует административных привилегий.

Один минус мне пока не удалось устранить: Как известно лисп славится своим repl в рантайме, а ECL имеет чрезвычайно неудобную консоль, и если вы случайно ошиблись, она вываливается в качестве обработчика исключения (конкретно выводит из себя невозможность стереть ошибочный символ). Я здесь разбираюсь с трудом, поэтому интересует мнение lisp общественности: как можно заменить её на что-нибудь более редактируемое?

Необходимые компоненты:

Embeddable Common Lisp
bash
Pregexp(кстати несколько медленновата, и порой ей не хватает стека (на длинных строках) кто что посоветует такого же переносимого?)
P. s.: Эх, не нашёл логотипа ecl, а сам рисовать не умею, поэтому поставил обобщённый лейбл.
P. s.2: Интересно, ещё как кто форматирует сорцы на lisp? Ибо тег source lang=«lisp» только добавляет серый фон для длинных исходников, не более (или это ограничение предпросмотра?).
Tags:
Hubs:
+6
Comments1

Articles