Cabal (작성 중)

Posted on March 29, 2025

Nix로 빌드하는 방법을 보다보니, 정작 Cabal은 튜토리얼 한 번 제대로 읽어 본 적이 없네요. 공식 매뉴얼에서 필요한 부분만 요약했습니다. 처음 공부할 때는, 상업적으로 쓰려면 Stack이 낫겠다 싶어, Cabal은 안쓰고, Stack으로 프로젝트를 빌드하며 공부했습니다. 하지만, Stack도 빌드에는 Cabal을 쓰기 때문에, 세세한 빌드 제어를 위해서는 결국 Cabal을 봐두어야 합니다.

cabal 매뉴얼

.cabal 파일은 여러 섹션stanza1으로 이루어져 있습니다.

메타 정보들

common
  ...
executable
  ...
library
  ...

executable 섹션과 library 섹션의 필드들은 비슷한데, 다음 차이만 있습니다.

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:

How to package haskell code

build-type

주로 하스켈 패키지만 빌드하는 게 아니라, 다른 외부 패키지들을 빌드하거나, 시스템 환경 설정들에 무슨 일을 해야한다거나 하면, 적당히 아래 5종류 중 하나를 골라 Cabal의 동작을 그 때 그 때 달리 할 수 있습니다.

Cabal 매뉴얼 - build-type

other-modules

프로젝트가 생성할 패키지를 사용하는 곳에서 import할 수 있는 모듈들은 exposed-modules에 나열하고, 외부로 공개하지 않는 모듈들 (구현 세부 사항, 도우미 함수들…)은 other-modules에 나열합니다. 직접하려고 하면, 생각만해도 귀찮아집니다. 복잡해지면, 손으로 직접 지정하지 않고, hpack 도구를 이용하는 편이 낫습니다. hpackpackage.yaml을 기반으로 .cabal 파일을 자동으로 생성합니다.

@TODO 당장 전체 필드를 모두 정리하기엔 무리가 있고, 자주 쓰이는 것들 위주로 정리할 예정입니다.

pkgconfig-depends

pkg-config를 이용해 정보를 받아 올 의존 패키지 목록. 패키지들의 버전이나 버전 범위를 지정하지 않으면, 시스템에 있는 아무 버전이나 쓸 수 있는 것으로 간주합니다. 시스템에서 가용한 pkg-config 패키지를 pkg-config --list-all로 확인할 수 있고, pkg-config.pc 파일을 찾을 위치는 PKG_CONFIG_PATH 환경 변수로 지정할 수 있습니다.

extra-libraries

빌드할 때 링크할 .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

hpack: A modern format for haskell packages
stack을 설치하면 자동으로 설치되기도 하고, cabal install hpack으로 설치할 수도 있습니다. hpack은 하스켈 패키지를 위한 포맷(package.yaml 작성 문법 쯤)을 의미하기도 하고, 이 포맷을 읽어들여 .cabal을 만들어내는 명령줄 도구 이름 hpack을 의미하기도 합니다.

hpack 명령줄 도구는 hpack 형식으로 작성된 package.yaml을 읽어 들여 .cabal 파일을 생성합니다.”

Setup.hs 명령어

Cabal 버전 2.0부터 기본으로 생성되진 않습니다. 하지만, 아직도 시스템 특정 라이브러리 연결이라든지, 빌드 중간에 필요한 작업이 있을 때 사용할 수 있습니다. (빌드할 때 복잡한 요구사항이 생기면, 여긴 아예 하스켈 소스 파일을 하나 사용하는 절차를 둔 것이니, 결국 이리 오지 않을까 싶은데요. 지금은 경험해보지 못해 일단 자세한 정리는 다음으로 미뤄야겠습니다.)

수작업으로 다음 내용을 가진 Setup.hs 파일을 프로젝트 루트에 만들고,

import Distribution.Simple

main :: IO ()
main = defaultMain

아래와 같이 실행해서 기본 사용법을 살펴 볼 수 있습니다.

> runhaskell Setup.hs --help 

@TODO

Cabal v1 v2 v3

@TODO
v2 에서 샌드박스 도입
v3 에서 cabal.project 도입

cabal.project

빌드 플래그들을 스크립트화 하기 위해 도입된 걸로 보입니다.

cabal configure --enable-profiling

이렇게 플래그를 주어 지정하던 걸, cabal.project

profiling: True

처럼 써 줄 수 있습니다.

아래 파일들을 이용할 수 있고, 나중 것이 정의되면 이전 것을 덮어 씌웁니다.

  1. 사용자 프로젝트들에 모두 쓰일 글로벌 설정 (기본값은 ~/.config/cabal/config)
  2. cabal.project 현재 프로젝트 루트에 위치
  3. cabal.project.freeze (cabal freeze의 출력)
  4. cabal.project.local (cabal configure의 출력)

cabal build를 실행했을 때, 현재 폴더에 없으면 상위 폴더에서 cabal.project.* 파일을 찾습니다.

packages: */*.cabal
with-compiler: /opt/ghc/8.0.1/bin/ghc

package cryptohash
  optimization: False

  1. stanza: .cabal의 library 섹션, executable 섹션, … 등을 libaray stanza, executable stanza라 부릅니다. stanza는 정보 그룹을 의미합니다.↩︎

Github 계정이 없는 분은 메일로 보내주세요. lionhairdino at gmail.com