Как правильно собираются проекты на Haskell

13 ноября 2013

Что-то в последнее время мне подозрительно часто стали задавать этот вопрос. Как по мне, тут все предельно просто, и на пост тема едва ли тянет. Но раз люди интересуются, видимо, у них возникают какие-то сложности с самостоятельным поиском ответа, так что, наверное, кому-нибудь этот пост пригодится.

Примем за рабочую теорию, что установку Haskell Platform вы осилили. И, конечно же, вы уже знаете, что пакеты в мире Haskell принято хранить и искать на Hackage, а устанавливать при помощи программы cabal:

cabal update
cabal install имя-пакета

Когда вы работаете над собственным проектом, все примерно то же самое. Вы также создаете пакет, содержащий модули и исполняемые файлы, и собираете его с помощью cabal. Возможно, вам не захочется заливать свой код на Hackage, но это и не требуется. Давайте рассмотрим сборку моего пакета с примерами использования OpenGL в Haskell.

Склонируем git-репозиторий и перейдем в каталог с пакетом:

git clone git@bitbucket.org:afiskon/hs-opengl-examples.git
cd hs-opengl-examples

Вот как выглядит содержимое пакета:

.
??? data
?   ??? box.jpg
?   ??? bricks.png
??? LICENSE
??? opengl-examples.cabal
??? Setup.hs
??? src
    ??? BlackWindow.hs
    ??? JumpingTriangle.hs
    ??? MovingTriangle.hs
    ??? RedTriangle.hs
    ??? RedWindow.hs
    ??? RotatingPyramid.hs
    ??? TexturedBox.hs
    ??? TexturedWalls.hs
    ??? Utils
        ??? Drawing.hs

Здесь мы видим следующее. LICENSE — это файл с текстом лицензии, его, в принципе, можно оставлять пустым. В каталоге src содержатся все исходники. Файл Setup.hs содержит следующий код:

import Distribution.Simple
main = defaultMain

Он генерируется автоматически при создании скелета пакета, о чем чуть ниже.

Наконец, самый главный файл, opengl-examples.cabal. Здесь задается описание пакета, перечисляются его зависимости и так далее. Можно провести аналогию с rebar.config из мира Erlang.

Посмотрим на содержимое cabal-файла:

name:               opengl-examples
version:            0.2.0
stability:          Stable
category:           Graphics
synopsis:           OpenGL Examples
description:        OpenGL Examples
license:            BSD3
license-file:       LICENSE
author:             Alexander Alexeev
homepage:           http://eax.me/haskell-opengl/
maintainer:         mail@eax.me
copyright:          2013
build-type:         Simple
cabal-version:      >=1.8
data-dir:           data
data-files:         *.jpg, *.png

executable          black-window
  ghc-options:      -O2 -Wall -fno-warn-missing-signatures
  hs-source-dirs:   src
  main-is:          BlackWindow.hs
  build-depends:    base >= 4.5,
                    OpenGL >= 2.8,
                    GLUT >= 2.4

На мой взгляд, формат более чем самодокументируемый и в дополнительных пояснениях не нуждается. Отмечу только, что я опустил описание всех исполняемых файлов, кроме первого, с именем black-window. Остальные исполняемые файлы описываются аналогичным образом.

Чтобы установить такой пакет, нужно просто перейти в его каталог и сказать:

cabal install

Cabal сам установит все зависимости и соберет пакет. Компиляция займет какое-то время. Кроме того, поскольку в пакете используется OpenGL, cabal может попросить вас установить некоторые зависимости с помощью системного менеджера пакетов. Когда сборка завершится, собранные программы можно будет запустить таким образом:

~/.cabal/bin/rotating-pyramid

При использовании описанного выше подхода все пакеты и их зависимости ставятся в ~/.cabal, в одну кучу. С одной стороны, это хорошо, потому что, если несколько пакетов зависят от одного и того же пакета, его придется собрать только один раз. Однако при этом могут возникать конфликтные ситуации, когда несколько пакетов имеют в зависимостях один и тот же пакет, но разных версий. Для борьбы с этой проблемой в cabal 1.18 была добавлена поддержка так называемых песочниц.

Узнайте версию вашего cabal, сказав:

cabal --version

Если версия оказалась меньше, чем 1.18, скажите:

cabal update
cabal install cabal-install

… а также убедитесь, что каталог $HOME/.cabal/bin/ прописан в переменной окружения $PATH.

Теперь попробуем собрать пакет в песочнице:

cabal sandbox init
cabal install
./.cabal-sandbox/bin/rotating-pyramid

Для открытия REPL скажите:

cabal repl jumping-triangle

… где jumping-triangle — это имя исполняемого файла. Если вы работаете над библиотекой, этот параметр можно опустить.

До выхода cabal 1.18 вместо песочниц было принято пользоваться программу cabal-dev. Если вам по каким-то причинам хочется использовать именно ее, скажите:

cabal install cabal-dev

Пользоваться cabal-dev очень просто:

cabal-dev install
./cabal-dev/bin/rotating-pyramid
cabal-dev ghci

В первом приближении дела обстоят как-то так. Более подробную информацию вы найдете, например, в Cabal User Guide.

Для закрепления материала советую вам попробовать создать простенький пакет, содержащий программу, выводящую в консоль «Привет, мир!», а также собрать ее описанными выше способами. Для создания скелета пакета используйте команду cabal init. Думаю, все это займет у вас от силы минут десять. Затем добавьте в проект какие-нибудь зависимости. Например, попробуйте написать программу, запрашивающую у пользователя строку, а затем выводящую эту строку в base64.

Любые вопросы и дополнения, как всегда, горячо приветствуются.

Дополнение: Также вас могут заинтересовать заметки Устанавливаем самую свежую версию компилятора Haskell и Сборка проектов на Haskell при помощи Stack.

Метки: , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.