Сборка бинарников Lisp
Небольшое руководство, как собрать исполняемый файл с помощью ECL.
В основе этого документа лежит частично переработанная документация с решением ряда найденных проблем.
Положим, у нас есть некоторая система, которую нужно собрать в исполняемый модуль.
tst-ecl.asd
(asdf:defsystem #:tst-ecl :serial t :depends-on (#:alexandria) :components ((:module "src" :components ((:file "package") (:file "example")))))
src/example.lisp
(in-package #:tst) (defun test-function (n) (format t "Factorial of ~a is: ~a~%" n (alexandria:factorial n)))
src/package.lisp
(in-package :cl-user) (defpackage #:tst (:use #:cl) (:export #:test-function))
Можно собрать исполняемый модуль с помощью, скажем, SBCL, используя sb-ext:save-lisp-and-die
, но такой подход выгружает в файл ядро (core), что делает довольно объемные бинарные файлы (40Мб без компрессии или 30Мб, если собрать ядро с компрессией).
А можно использовать ECL. В этой реализации Lisp есть расширение системы ASDF
, предоставляющее абстракцию для построения библиотек и исполнимых файлов прямо из определения системы.
Установка Quicklisp
Дальнейшие рассуждения предполагают, что ECL у вас установлен (из пакетов операционной системы, скачан с сайта или собран из исходников), а Quicklisp еще не был установлен.
В моем случае версия ECL 21.2.1:
$ ecl --version ECL 21.2.1
Начнем с установки менеджера библиотек для Common Lisp, как он сам себя называет.
$ curl -O https://beta.quicklisp.org/quicklisp.lisp $ ecl --load quicklisp.lisp ;;; Loading "/home/user/quicklisp.lisp" ;;; Loading #P"/usr/local/lib/ecl-21.2.1/sockets.fas" ==== quicklisp quickstart 2015-01-28 loaded ==== To continue with installation, evaluate: (quicklisp-quickstart:install) For installation options, evaluate: (quicklisp-quickstart:help) ECL (Embeddable Common-Lisp) 21.2.1 (git:UNKNOWN) Copyright (C) 1984 Taiichi Yuasa and Masami Hagiya Copyright (C) 1993 Giuseppe Attardi Copyright (C) 2013 Juan J. Garcia-Ripoll Copyright (C) 2018 Daniel Kochmanski Copyright (C) 2021 Daniel Kochmanski and Marius Gerbershagen 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 in: #<process TOP-LEVEL 0x55d0b7830f80>. >
В приглашении ECL набираем (quicklisp-quickstart:install)
, как нам и написано в подсказке.
> (quicklisp-quickstart:install) ; Fetching #<url "http://beta.quicklisp.org/client/quicklisp.sexp"> ; 0.82KB ================================================== ... ==== quicklisp installed ==== To load a system, use: (ql:quickload "system-name") To find systems, use: (ql:system-apropos "term") To load Quicklisp every time you start Lisp, use: (ql:add-to-init-file) For more information, see http://www.quicklisp.org/beta/ NIL >
После этого необходимо добавить инициализацию Quicklisp в файл инициализации .eclrc
:
> (ql:add-to-init-file) I will append the following lines to #P"/home/user/.eclrc": ;;; The following lines added by ql:add-to-init-file: #-quicklisp (let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))) (when (probe-file quicklisp-init) (load quicklisp-init))) Press Enter to continue. #P"/home/user/.eclrc" >
Подготовка системы tst
к сборке
Если вы работаете с SBCL, то свой проект вы можете разместить где угодно в операционной системе, а ссылку на него разместить в папке ~/quicklisp/local-projects
, после чего система просто загружается через (ql:quickload "tst-ecl")
.
Оказалось, что ECL, в отличие от других реализаций, не ходит по символическим ссылкам в файловой системе, поэтому придется действовать в обратном направлении. Проект необходимо разместить в папке ~/quicklisp/local-projects/tst
, а после этого в нужном месте сделать символическую ссылку на проект.
После этого проект начинает загружаться в ECL:
> (ql:quickload "tst-ecl") To load "tst-ecl": Load 1 ASDF system: tst-ecl ; Loading "tst-ecl" To load "alexandria": Load 1 ASDF system: asdf Install 1 Quicklisp release: alexandria ; Fetching #<url "http://beta.quicklisp.org/archive/alexandria/2023-06-18/alexandria-20230618-git.tgz"> ; 55.61KB ================================================== 56,945 bytes in 0.05 seconds (1158.55KB/sec) ; Loading "alexandria" [package alexandria].............................. [package alexandria-2] To load "tst-ecl": Load 1 ASDF system: tst-ecl ; Loading "tst-ecl" [package tst] ("tst-ecl") >
Возможно, что понадобится обновить список проектов, размещенных локально:
> (quicklisp-client:register-local-projects)
Сборка системы
Последний этап — собственно сборка исполняемого файла.
Здесь просто повторяем команду из документации ECL.
> (asdf:make-build :tst-ecl :type :program :move-here #P"./" :epilogue-code '(progn (tst:test-function 5) (si:exit))) (#P"/home/user/devel/tst-ecl") >
Под Windows дополнительно требуется выполнить команду
> (require 'cmp)
которая настраивает компилятора (по умолчанию она в бинарнике Ecl не включена).
Кроме того, под Windows необходимо запускать сборку внутри оболочки MS Visual Studio, то есть с доступным компилятором cl
.
Результат сборки
REPL можно закрыть. После выполнения asdf:make-build
, если все прошло успешно, должен сформироваться файл tst-ecl
. На моей системе получился файл размером 1,265,520 байт. При этом, у файла не установлен бит запуска, это необходимо сделать вручную.
$ ./tst-ecl Factorial of 5 is: 120
Объем кода
При сборке исполняемого файла ECL не выгружает ядро, как это делают другие реализации. Сборка производится от вызываемой функции, поэтому на размер исполняемого файла не влияет количество фактически загруженных систем, в том числе сам Quicklisp. Поэтому, для сборки можно не запускать ECL в режиме norc (ecl --norc
), оставив инициализацию ECL с помощью .eclrc
.