Nix로 빌드하는 방법을 보다보니, 정작 Cabal은 튜토리얼 한 번 제대로 읽어 본 적이 없네요. 공식 매뉴얼에서 필요한 부분만 요약했습니다. 처음 공부할 때는, 상업적으로 쓰려면 Stack이 낫겠다 싶어, Cabal은 안쓰고, Stack으로 프로젝트를 빌드하며 공부했습니다. 하지만, Stack도 빌드에는 Cabal을 쓰기 때문에, 세세한 빌드 제어를 위해서는 결국 Cabal을 봐두어야 합니다.
.cabal 파일은 여러 섹션stanza1으로 이루어져 있습니다.
메타 정보들
common
...
executable
...
library ...
executable 섹션과 library 섹션의 필드들은 비슷한데, 다음 차이만 있습니다.
Main
모듈을 가진 파일을 갖고 있습니다.cabal init
에서 시작해 보겠습니다.
.
├── app
│ └── Main.hs
├── cabal-sample.cabal
├── CHANGELOG.md └── LICENSE
cabal-sample.cabal
cabal-version: 3.0
-- .cabal 스펙 버전으로 cabal-install 도구 버전과 Cabal 라이브러리 버전과 다를 수 있다.
-- Cabal 라이브러리 버전은 이 필드의 버전값 이상이어야 한다.
-- 2.2 스펙 버전 이후부터, 이 필드가 .cabal의 첫번째 줄이어야 한다.
name: cabal-sample
-- 패키지 버전
-- 하스켈 package versioning policy (PVP) 표준 가이드
-- https://pvp.haskell.org
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.1.0.0
license: MIT
license-file: LICENSE
author: lionhairdino
maintainer: lionhairdino@gmail.com
-- 소스 아래 설명 참고
build-type: Simple
extra-doc-files: CHANGELOG.md
-- 예시나 튜토리얼 모듈 등이 있다면 여기 써 준다.
-- extra-source-files:
-- 변수 바인딩과 비슷하다. 섹션들에서 공통common으로 쓰일 것들을 모아 둔다. executable과 library 섹션 등에서 import로 가져다 쓸 수 있다.
common warnings
ghc-options: -Wall -- GHC에게 넘길 추가 옵션
executable cabal-sample
-- common에서 정의한 항목들을 불러 온다.
import: warnings
-- Main 모듈을 갖고 있는 .hs 또는 .lhs 파일
main-is: Main.hs
-- 실행 파일을 만들 때 쓰인 Main 이외의 다른 모듈들
other-modules:
-- 패키지에 있는 모듈에서 쓰인 LANGUAGE 확장
other-extensions:
-- 프로젝트에 import된 모듈들이 어느 패키지에서 왔는가
build-depends: base ^>=4.19.2.0
-- 소스 파일들이 들어 있는 디렉토리
hs-source-dirs: app
-- 패키지 작성에 쓰인 기반 언어
default-language: Haskell2010
-- 설치할 데이터를 찾을 디렉토리
-- 기본값은 소스 디렉토리에서 찾는다.
data-dir:
data-file:
주로 하스켈 패키지만 빌드하는 게 아니라, 다른 외부 패키지들을 빌드하거나, 시스템 환경 설정들에 무슨 일을 해야한다거나 하면, 적당히 아래 5종류 중 하나를 골라 Cabal의 동작을 그 때 그 때 달리 할 수 있습니다.
Simple: Setup.hs
없이 Cabal이 직접 Distribution.Simple.defaultMain
를 호출하므로, Setup.hs
없이 빌드 작업을 할 수 있습니다. (@TODO Simple
인 경우에도 defaultMain
을 부르는 Simple.hs
를 작성해도 된다고 하는데, 그러면 Custom
을 고르면 되지 않나 싶은데, 아직 더 찾아 봐야겠습니다.)
Configure:
Distribution.Simple.defaultMainWithHooks defaultUserHooks
: C라이브러리와 연동하지만, autoconf
를 쓰지 않는 경우, configure
스크립트가 없고, 특정 빌드 작업이 필요한 경우로 쓴다고 합니다. (이럴 때는 그냥 Simple
타입이라 지정하면 되지 않을까 싶은데요. @TODO)Distribution.Simple.defaultMainWithHooks autoconfUserHooks
: 이 함수는 빌드 시작하기 전에 autoconf
스타일의 ./configure
를 호출하고, 그 다음 빌드 단계가 진행됩니다. 일반적으로 C언어 FFI를 쓰는 패키지에서 필요합니다. 빌드 전에 환경 설정을 확인해야 할 때 씁니다.Setup.hs
를 Distribution.Simple.DefaultMainWithHooks ~~
를 부르게 만들어야 합니다.
Make: Distribution.Make.defaultMain
을 호출합니다. Cabal이 직접 하스켈 빌드를 하지 않고, Makefile
을 읽어 make
로 빌드합니다. 보통 C나 C++ 프로젝트를 같이 빌드할 때 씁니다. Setup.hs
가 필요 없고, Cabal은 단순히 make
명령을 부르기만 합니다.
Custom: 사용자가 만든 Setup.hs
이나 Setup.lhs
를 사용합니다. 반드시 Setup.hs(lhs)
가 필요합니다.
Hooks: cabal-version: 3.14
이후에 추가됐다고 합니다. 나중에 custom
을 대체할 거라 하는데, 사용할 일이 생기면 그 때 정리해야겠습니다.
프로젝트가 생성할 패키지를 사용하는 곳에서 import
할 수 있는 모듈들은 exposed-modules
에 나열하고, 외부로 공개하지 않는 모듈들 (구현 세부 사항, 도우미 함수들…)은 other-modules
에 나열합니다. 직접하려고 하면, 생각만해도 귀찮아집니다. 복잡해지면, 손으로 직접 지정하지 않고, hpack
도구를 이용하는 편이 낫습니다. hpack
이 package.yaml
을 기반으로 .cabal
파일을 자동으로 생성합니다.
@TODO 당장 전체 필드를 모두 정리하기엔 무리가 있고, 자주 쓰이는 것들 위주로 정리할 예정입니다.
pkg-config
를 이용해 정보를 받아 올 의존 패키지 목록. 패키지들의 버전이나 버전 범위를 지정하지 않으면, 시스템에 있는 아무 버전이나 쓸 수 있는 것으로 간주합니다. 시스템에서 가용한 pkg-config
패키지를 pkg-config --list-all
로 확인할 수 있고, pkg-config
가 .pc
파일을 찾을 위치는 PKG_CONFIG_PATH
환경 변수로 지정할 수 있습니다.
빌드할 때 링크할 .so
나 .dll
같은 동적 라이브러리. 시스템에 있는 라이브러리지만 기본으로 추가 되는 것들이 아닌 것들을 써 줍니다.
library
builid-depends: base >= 4.7 && <5
extra-libraries: z, m, pthred
여기에 써 준 라이브러리들은 GHC에게 -optl-l<lib>
플래그로 넘어 갑니다.
※ pkgconfig-depends: zlib
로 추가했다면, pkg-config
를 이용해 설치 여부, 경로, 컴파일 플래그, 링크 플래그들을 가져 오기 때문에 extra-libraries: z
는 써 줄 필요 없습니다.
hpack: A modern format for haskell packages
stack을 설치하면 자동으로 설치되기도 하고, cabal install hpack
으로 설치할 수도 있습니다. hpack은 하스켈 패키지를 위한 포맷(package.yaml
작성 문법 쯤)을 의미하기도 하고, 이 포맷을 읽어들여 .cabal
을 만들어내는 명령줄 도구 이름 hpack
을 의미하기도 합니다.
“hpack
명령줄 도구는 hpack 형식으로 작성된 package.yaml
을 읽어 들여 .cabal
파일을 생성합니다.”
Cabal 버전 2.0부터 기본으로 생성되진 않습니다. 하지만, 아직도 시스템 특정 라이브러리 연결이라든지, 빌드 중간에 필요한 작업이 있을 때 사용할 수 있습니다. (빌드할 때 복잡한 요구사항이 생기면, 여긴 아예 하스켈 소스 파일을 하나 사용하는 절차를 둔 것이니, 결국 이리 오지 않을까 싶은데요. 지금은 경험해보지 못해 일단 자세한 정리는 다음으로 미뤄야겠습니다.)
수작업으로 다음 내용을 가진 Setup.hs
파일을 프로젝트 루트에 만들고,
import Distribution.Simple
main :: IO ()
= defaultMain main
아래와 같이 실행해서 기본 사용법을 살펴 볼 수 있습니다.
> runhaskell Setup.hs --help
@TODO
@TODO
v2 에서 샌드박스 도입
v3 에서 cabal.project
도입
빌드 플래그들을 스크립트화 하기 위해 도입된 걸로 보입니다.
cabal configure --enable-profiling
이렇게 플래그를 주어 지정하던 걸, cabal.project
에
profiling: True
처럼 써 줄 수 있습니다.
아래 파일들을 이용할 수 있고, 나중 것이 정의되면 이전 것을 덮어 씌웁니다.
~/.config/cabal/config
)cabal.project
현재 프로젝트 루트에 위치cabal.project.freeze
(cabal freeze
의 출력)cabal.project.local
(cabal configure
의 출력)cabal build
를 실행했을 때, 현재 폴더에 없으면 상위 폴더에서 cabal.project.*
파일을 찾습니다.
packages: */*.cabal
with-compiler: /opt/ghc/8.0.1/bin/ghc
package cryptohash
optimization: False