*.cabalファイルの内容を埋める
前節のcabal initコマンドで生成されたsample.cabalファイルの内容は以下のようになります。Cabalのフィールド名や項目名では、大文字と小文字は区別されません。「What does the package build:」で選んだ選択肢によって内容は変わります。
「1) Library」を選んだ場合
-- Initial sample.cabal generated by cabal init. For further -- documentation, see http://haskell.org/cabal/users-guide/ -- The name of the package. name: sample -- The package version. See the Haskell package versioning policy (PVP) -- for standards guiding when and how versions should be incremented. -- http://www.haskell.org/haskellwiki/Package_versioning_policy -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change version: 0.1.0.0 -- A short (one-line) description of the package. synopsis: Cabal package example -- A longer description of the package. -- description: -- URL for the project homepage or repository. homepage: http://example.org -- The license under which the package is released. license: BSD3 -- The file containing the license text. license-file: LICENSE -- The package author(s). author: shelarcy -- An email address to which users can send suggestions, bug reports, and -- patches. maintainer: shelarcy <shelarcy@example.org> -- A copyright notice. -- copyright: category: Development build-type: Simple -- Constraint on the version of Cabal needed to build this package. cabal-version: >=1.8 library -- Modules exported by the library. -- exposed-modules: -- Modules included in this library but not exported. -- other-modules: -- Other library packages from which modules are imported. build-depends: base ==4.5.*
「2) Executable」を選んだ場合
~ 略(「library」の直前までは共通) ~ executable sample -- .hs or .lhs file containing the Main module. -- main-is: -- Modules included in this executable, other than Main. -- other-modules: -- Other library packages from which modules are imported. build-depends: base ==4.5.*
*.cabalファイルでは、「description: 」や「build-depends:」などのフィールドがコメントアウトされています。cabal initコマンドで生成された*.cabalファイルは、パッケージを作成するための雛形にすぎません。パッケージとして利用するには、コメントアウトされている部分に必要な情報を補う必要があります。
name:フィールドには、パッケージの名前が書かれています。
version:フィールドに記述されるバージョンは、Haskellコミュニティで広く使われるバージョン番号付け規則に従う必要があります。ピリオドで区切られた最大4個の数字でバージョンを表現します。「0.1.0.0.1」のようなバージョンは使えません。下位の数字がある場合には、下位の数字がないバージョンよりも新しいバージョンとして扱われます。例えば「0.1」よりも「0.1.1」の方が新しいバージョンとして扱われます。無用の混乱を避けるため、下位の数字が必要ない場合でも、cabal initコマンドのデフォルト値である「0.1.0.0」のように4個の数字で表現するとよいでしょう。
パッケージで互換性がない変更を行った場合には、最初の数字か2番目の数字のどちらかを必ず上げる必要があります。例えば元のバージョンが「0.1.0.0」であれば、「1.0.0.0」か「0.2.0.0」にする必要があります。互換性を保ちつつ新しい機能(API)を追加するときには、3番目の数字を上げます。最後の数字を上げるのは、バグ修正だけを行ってAPIに変更がないときです(参考リンク1,参考リンク2)。
パッケージがどの程度安定しているかを示したい場合には、stability:フィールドに安定性に関する情報を追加します。
stability: experimental
maintainer:フィールドには、パッケージにバグなどの問題があったり、パッケージに対する改良の提案があったりしたときに連絡するメール・アドレスを記述します。
-- An email address to which users can send suggestions, bug reports, and -- patches. maintainer: shelarcy <shelarcy@example.org>
description:フィールドには、パッケージの詳しい説明を記述します。synopsis:フィールドに書き切れない細かい説明をここに記述することになります。
description:フィールドに記述したパッケージの説明は、Haddockというドキュメンテーション・ツールを使ってソースコードからドキュメントを生成する際や、HackageDBにアップロードしたパッケージのWebページが生成される際に使われます。Haddockで生成されるドキュメントやHackageDBでの表示を意識して、以下のような書式で書く必要があります。
description: This is a sample of Cabal package. This package show how to make Cabal package, and how to describe *.cabal file. . See more information at <http://example.org/>.
重要なのは、「.」だけの行と、「<」と「>」でURLを囲んでいる部分です。HackageDBのページでは、単なる空白行は段落の区切りになりません。段落の区切りを示すには「.」だけの行を使います。またHackageDBのページでURLをリンクとして展開するには、URLを「<」と「>」で囲む必要があります。この書式は、Haddockのドキュメントにも共通しています。ほかにもHackageDBとHaddockに共通した書式があります(参考リンク1,参考リンク2,参考リンク3)。
homepage:フィールドには、cabal initコマンドで入力したパッケージの開発プロジェクトのWebサイト、またはパッケージの開発に利用しているバージョン管理システムのレポジトリのURLが記述されています。
-- URL for the project homepage or repository. homepage: http://example.org
これとは別にバグ報告先を書いておきたい場合には、bug-reports:フィールドに追加情報を記述します。
bug-reports: mailto:shelarcy@example.org
bug-reports: http://example.org/trac/sample
bug-reports:フィールドには、バグ報告に使うメール・アドレスやメーリングリストのURLに加え、バグ・トラッカーとして利用するTracやgithubのIssuesなどのURLを書くことができます。
copyright:フィールドは、パッケージの著作権所有者を示すものです。この内容は、LICENSEファイルでの著作権所有者の表示に矛盾しないよう注意する必要があります。
build-type:フィールドは、このパッケージのビルド方法について言及する部分です。cabalコマンドなどの動作を決めるのに利用されます。特別な処理が必要でなければ、デフォルトのSimpleのままで構いません。
もしパッケージがHaskell外部のライブラリなどに依存しており、パッケージのビルドに./configureコマンドを利用する必要がある場合には、build-type:フィールドをConfigureに変更する必要があります。その場合には、Setup.hsも修正する必要があります。
最初に自動生成されるSetup.hsの内容は以下の通りです。
import Distribution.Simple main = defaultMain
自動生成されたSetup.hsは、基本的にはそのまま利用できます。ただし、./configureコマンドを利用するには、以下のように書き換える必要があります(参考リンク)。
import Distribution.Simple main = defaultMainWithHooks autoconfUserHooks
なお、build-type:フィールドがConfigureに設定されている場合にcabalコマンドから利用できるのは、パッケージのビルド時に利用することがあらかじめ定義されている./configureコマンドなどのコマンドだけです。autoconfコマンドやautoreconfコマンドといったconfigureファイルの生成コマンドは対象外です。そうしたコマンドを利用する場合には、cabalコマンドやSetup.hsでパッケージをビルドする前にconfigureファイルを生成しておく必要があります。
Cabalパッケージのモジュールや他のパッケージなどを利用して、Setup.hsの内容をさらにカスタマイズすることもできます。例えば、コンパイラに渡すオプションを生成するwx-configコマンドを追加で利用するといったことが可能です。その場合、build-type:フィールドがSimpleやConfigureではパッケージをビルドできません。build-type:フィールドをCustomに変更したうえで、Setup.hsを書き換えてください。
cabal-version:フィールドでは、Cabalパッケージに対する*.cabalファイルの互換性を示します。Cabalはバージョンによって、記述可能な内容やフィールド名が異なります。また、以前のバージョンには*.cabalファイルの解釈にバグがありました。このため、作成するパッケージがどのバージョンのCabalと互換性があるかをcabal-version:フィールドに記述します。cabal-version:フィールドに記されたバージョンと、実際の*.cabalの内容に不整合があると、警告メッセージが表示されたりエラーになったりします。その場合には、警告メッセージやエラー・メッセージの内容に応じて*.cabalの内容を修正してください。
問題が起こるのを避けるため、cabal-version:フィールドでは少し厳しめの制約を付けて、古いバージョンのCabalではビルドできないことを明示する方がよいでしょう。cabal initコマンドで生成された*.cabalファイルのcabal-version:フィールドでは、1.8以上が指定されます。この個所は、GHC 7.0.xのCabalのバージョンである1.10か、Haskell Platform 2012.x.0.0で提供されているGHC 7.4.xのCabalのバージョンである1.14に書き換えたほうがよいと思います。
cabal-version: >=1.14
data-files:フィールドには、アイコンやテスト・データといったソースコード以外のファイルを記述します。パッケージでそうしたファイルを利用しない場合には記述する必要はありません。
data-dir:フィールドには、data-files:フィールドに列挙したファイルを格納しているディレクトリを記述します。このフィールドを省略した場合、*.cabalファイルの置いてあるディレクトリと同じディレクトリ(つまり「.」)だとみなされます。ただ、データの格納用には専用のディレクトリを用意したほうがよいでしょう。
data-files:フィールドに記述されたファイルには、ビルド時に自動生成される「Paths_<パッケージ名>モジュール」のgetDataDir関数やgetDataFileName関数を通してアクセスできます(参考リンク)。
例えば、data-files:フィールドに記述された最初のファイルの中身を出力する処理は、以下のようになります。
import Control.Monad import System.Directory import Paths_sample main = do dir <- getDataDir files' <- getDirectoryContents dir files <- filterM doesFileExist files' file <- getDataFileName $ head files txt <- readFile file putStr txt