Haskellのパッケージ管理について調べてみた
Haskellやって1年ちょっと経つわけですが、Haskellで使うpackageの管理についてよく知らなかったので色々調べてみました。
対象としては最近Haskellを始めた方やpackage管理についてよく知らないという方向けです、 packageを利用する側からの説明なので、作るにはどうしたらいいかは書いてません(`・ω・´)ゞ
そして、やたら長いのです。
ちなみにghc-7.0.3で、OSはLinux Mintです。
packageって?
packageはHaskellのライブラリを構成する一つのまとまりで、packageはいくつかのmoduleをまとめています。
moduleはだいたい.hs拡張子のHaskellプログラム1ファイルと対応していて、module Foo.Bar where
とファイルの上の方に書いてあるFoo.Barがモジュール名です。
Haskell Platformに含まれる標準的なpackageはココを見てみましょう。 え、Haskell Platformを入れてない? 入れましょう(゚Д゚)
右側にある haskell98-2.0.0.1
とかってヤツですね(・・)
packageはそのパッケージから利用できるmoduleがあって、あるパッケージのモジュールを使いたくなったらプログラムからimportするわけです。
どんなモジュールが使えるかは、次のようにghc-pkg
コマンドを使うことで調べられます。
textパッケージから利用できるモジュールを調べてみると、
% ghc-pkg field text exposed-modules
exposed-modules: Data.Text Data.Text.Array Data.Text.Encoding
Data.Text.Encoding.Error Data.Text.Foreign Data.Text.IO
Data.Text.Internal Data.Text.Lazy Data.Text.Lazy.Builder
Data.Text.Lazy.Builder.Int Data.Text.Lazy.Builder.RealFloat
Data.Text.Lazy.Encoding Data.Text.Lazy.IO Data.Text.Lazy.Internal
Data.Text.Lazy.Read Data.Text.Read
となりますね。同じパッケージでも複数のバージョンがあるとそれも出てきます。
ghc-pkg
コマンドは他にもいろいろ機能がありますが、それも追って紹介します。
cabalって?
Haskellのパッケージのメタ情報や管理などをしている一つのエコシステムです。
主な役者は、
- Cabalパッケージ
- cabal-installパッケージ
- .cabalファイル
- cabalコマンド
などです。
各パッケージのメタ情報は<package名>.cabal
なるファイルに収められていて、ココに依存関係やらバージョンやらビルド方法など様々な情報が収められています。
Hackageにあるパッケージ群にはこのファイルが付属していて、こいつを使って管理されているようです。
Haskellのパッケージ管理は主にcabal
コマンドを通して行います。
cabal
コマンドはcabal-installパッケージが提供するCabalパッケージへのインターフェースです。
え?よくわからん?私もですъ(゚Д゚)
ちなみにcabal-installパッケージの依存先を見てみると、
HTTPとかunixとかnetworkとかのパッケージに依存しててイカにも外部と通信してパッケージを取ってインストールしますよ〜ってな感じですね! もちろんCabalパッケージにも依存しているようです。
Cabalパッケージの依存関係はシンプルです。
まぁHaskell Platformを入れていればcabal
コマンドは使えるはずなので、特に問題ないはずです。
cabalを使えばHackageからパッケージをインストールしたり、自分の作ったパッケージをビルドしたりHackageへアップロードするのが簡単になります。助かります。
使い方
Hackageから特定のパッケージをインストールしてくるには、cabal install
コマンドを使います。
が、そのまえに、Hackageにある最新のパッケージの情報とローカルの情報を同期させるため、
% cabal update
をしましょう。
それから、例えばconduitパッケージをインストールしたいなら、
% cabal install conduit
と打てば、conduitの最新版がインストールされます。 インストールするバージョンの指定もできます。
% cabal install conduit-0.5.2.4
各パッケージの情報は、
% cabal info conduit
とすればパッケージの説明や、現在インストールされているバージョンなどが見られます。 利用可能なモジュール名もここで表示されますね:-)
自分でHaskellのプロジェクトを作るには、
% cabal init
を使えば、対話的に自分のパッケージ管理のための.cabalファイルが生成されます。
自分のパッケージの依存関係を解決してインストールするには、.cabalファイルのあるディレクトリで
% cabal install
とすればOKです。Hackageから自動的に依存しているパッケージを集めてビルドしてくれます。
依存している必要なパッケージが一度入ってしまえば、次からはcabal build
をするだけです。
しかしこのcabal
コマンドはなぜだかインストールしたパッケージを削除する機能が無いので、その辺使い勝手が良いcab
を使うと良いかもしれませんねъ(゚Д゚)
cab: A maintenance command of Haskell cabal packages
他にも使うパッケージをプロジェクトで共有したくないよ〜って時はcabal-devが便利です。 パッケージの依存関係が他と干渉してメタメタになることはたまによくある(?)ことなので、こういうのも便利ですね!
他にもcabal-installの代替や補佐役のパッケージは色々ありますよ。
packageはどこに?
インストールして利用できるようになったはずのパッケージはどこにあるんでしょう?
これは、% ghc-pkg list
で一覧で出てきます。
% ghc-pkg list
/var/lib/ghc-7.0.3/package.conf.d
Cabal-1.10.1.0
GLUT-2.1.2.1
HTTP-4000.1.1
HUnit-1.2.2.3
OpenGL-2.2.3.0
QuickCheck-2.4.1.1
....
/home/kenta/.ghc/x86_64-linux-7.0.3/package.conf.d
Cabal-1.14.0
HUnit-1.2.4.2
ListLike-3.1.6
MissingH-1.1.1.0
MonadCatchIO-transformers-0.3.0.0
PSQueue-1.1
QuickCheck-2.4.2
SHA-1.5.0.1
SafeSemaphore-0.7.0
attoparsec-conduit-0.4.0.1
....
適当に% cabal install
しまくってるとズラズラ〜っとたくさん出てきます。こんな感じでインストールされたパッケージの名前とバージョンが表示されますね。
上に表示された/var/lib/ghc-7.0.3/package.conf.d
は、システムにインストールされたパッケージの情報を持っているディレクトリです。Haskell Platformをインストール先を指定せずインストールするとこんな感じのところに入ると思います。
その下の/home/kenta/.ghc/x86_64-linux-7.0.3/package.conf.d
は、ユーザーの領域にインストールされたパッケージですね。% cabal install
でインストールしたパッケージは普通こっちの管理下に入ります。なので普通に% cabal install
するにはsudoは要らないわけです:-)
それでは、package.conf.d/ディレクトリの中を見てみましょう。
% ls /var/lib/ghc-7.0.3/package.conf.d
Cabal-1.10.1.0-e951c182da4a22a7b82c0f2e4be13b7b.conf
GLUT-2.1.2.1.conf
HTTP-4000.1.1.conf
HUnit-1.2.2.3.conf
OpenGL-2.2.3.0.conf
QuickCheck-2.4.1.1.conf
X11-1.5.0.0.conf
X11-xft-0.3.conf
array-0.3.0.2-143060371bda4ff52c270d1067551fe8.conf
base-4.3.1.0-91c3839608ff4d3ec95f734c5ae4f31c.conf
bin-package-db-0.0.0.0-03f52950478226c0334a7d7e83d56e17.conf
....
おぉ、パッケージの設定ファイルみたいなのが、たくさんありますね!
package.conf.d
ってのはpackage configuration directoryの略でしょうかね。
一つ設定ファイルの中身を見てみましょう。中身は人間が読めるようになっています。 (長いので、一部省略)
% cat zlib-0.5.3.1.conf
name: zlib
version: 0.5.3.1
id: zlib-0.5.3.1-7e19941cbd00147a79723e25160ffc8b
license: BSD3
copyright: (c) 2006-2008 Duncan Coutts
(....)
exposed-modules: Codec.Compression.GZip Codec.Compression.Zlib
Codec.Compression.Zlib.Raw Codec.Compression.Zlib.Internal
hidden-modules: Codec.Compression.Zlib.Stream
import-dirs: /usr/lib/haskell-packages/ghc/lib/zlib-0.5.3.1/ghc-7.0.3
library-dirs: /usr/lib/haskell-packages/ghc/lib/zlib-0.5.3.1/ghc-7.0.3
hs-libraries: HSzlib-0.5.3.1
extra-libraries: z
extra-ghci-libraries:
include-dirs:
includes: zlib.h
depends: base-4.3.1.0-91c3839608ff4d3ec95f734c5ae4f31c
bytestring-0.9.1.10-6aa1efbfa95d1689fc03d61e7c4b27c4
(....)
なるほど、「フィールド名: 内容」の形式で、バージョンやどんなモジュールが利用可能か、依存関係なども書いてありますね。
先ほど紹介した% ghc-pkg field <package名> exposed-modules
というヤツは、実はこのファイルのフィールドを指定して、それを表示させているだけでした。
つまり、% ghc-pkg field <package名> <フィールド名>
となってます。
ですので、% ghc-pkg field zlib depends
とやれば、
% ghc-pkg field zlib depends
depends: base-4.3.1.0-91c3839608ff4d3ec95f734c5ae4f31c
bytestring-0.9.1.10-6aa1efbfa95d1689fc03d61e7c4b27c4
と、どのフィールドでもフィールド名を指定してやれば見られるわけです。
実際には、.confファイルをキャッシュしているpackage.cacheファイルを見に行っています。straceがあれば、
% strace ghc-pkg field zlib depends 2>&1 1>/dev/null | grep open
などでわかりますね。
@shelarcyさんに実際のコードの位置を教えて頂きました。ありがとうございます。( ;∀;)
readPackageConfig :: DynFlags -> FilePath -> IO [PackageConfig] readPackageConfig dflags conf_file = do isdir <- doesDirectoryExist conf_file proto_pkg_configs <- if isdir then do let filename = conf_file </> "package.cache" debugTraceMsg dflags 2 (text "Using binary package database:" <+> text filename) conf <- readBinPackageDB filename return (map installedPackageInfoToPackageConfig conf) # (省略)
確かにpackage.cacheを読みに行ってますね!
そして、パッケージの実体がどこにあるのかというと、import-dirs
やlibrary-dirs
フィールドに書いてあるようです。
そこをfind
で覗いてみると。。。
% find /usr/lib/haskell-packages/ghc/lib/zlib-0.5.3.1/ghc-7.0.3/ -type f -printf '%P\n'
HSzlib-0.5.3.1.o
Codec/Compression/Zlib/Raw.hi
Codec/Compression/Zlib/Stream.hi
Codec/Compression/Zlib/Internal.hi
Codec/Compression/Zlib.hi
Codec/Compression/GZip.hi
libHSzlib-0.5.3.1.a
おぉ、オブジェクトファイル(.o)やらインターフェースファイル(.hi)もありますね!
実際ghciで試してみると、
% ghci
GHCi, version 7.0.3: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> :m Codec.Compression.GZip Data.ByteString.Lazy.Char8
Prelude Codec.Compression.GZip Data.ByteString.Lazy.Char8> unpack . decompress . compress . pack $ "Compress me!"
Loading package bytestring-0.9.1.10 ... linking ... done.
Loading package zlib-0.5.3.1 ... linking ... done.
"Compress me!"
最後から2番目の行にある通り、ちゃんとzlibパッケージをロードして、リンクしてるらしいことがわかりますね。 *1
ココまではシステム(global)に入ったパッケージについて述べて来ましたが、ユーザー領域でも大体一緒のようです。
インストールされたパッケージは、先程調べたように/home/kenta/.ghc/x86_64-linux-7.0.3/package.conf.d
にありました。
内容も一緒ですが、ここにはcabal
で入れたものの情報が入ります。
コンパイル済みの実体は、~/.cabal/lib
以下に同じように収まってますね。
~/.cabal/bin
には、cabal install
などで入れたプログラムの実行ファイルがあります。cabal install
でインストールしたプログラムを使うなら、ココへのパスを通しておきましょう:-)
先ほど紹介したcabal-dev
やcab
などの実行ファイルも~/.cabal/bin
にありました。
それで、今までhackage.haskell.orgのレポジトリしか使ってませんでしたが、実はミラーがあります。 本家hackage.haskell.orgはちょくちょく落ちるので、ミラーを使ってみたりするのも良いかもしれません。
- hdiff.luite.com
- hackage.haskell.biz
はすぐ見つけられたのですが、他はよくわかりません(`・ω・´)ゞ
cabal
コマンドでこれらのミラーを使うには、~/.cabal/config
ファイルのremote-remo
を編集すれば、リポジトリを切り替えられます。
最初は本家の
remote-repo: hackage.haskell.org:http://hackage.haskell.org/packages/archive
になっているので、この行をコメントアウト(--)して、
-- remote-repo: hackage.haskell.org:http://hackage.haskell.org/packages/archive
remote-repo: hackage.haskell.biz:http://hackage.haskell.biz
とすればOKです。
試しに% cabal update
をしてみると、
% cabal update
Downloading the latest package list from hackage.haskell.biz
ちゃんとミラーに取りに行ってますね!
ちなみに、取ってきたパッケージの情報は、~/.cabal/packages/<リポジトリのホスト名>/00-index.tar
に収められています。
本家レポジトリなら、~/.cabal/packages/hackage.haskell.ord/00-index.tar
ですね。
このtarファイルの中身は、% tar -tf 00-index.tar
をしてみると、
% tar -tf 00-index.tar | head
4Blocks/0.1/4Blocks.cabal
4Blocks/0.2/4Blocks.cabal
AC-Angle/1.0/AC-Angle.cabal
AC-Boolean/1.0.0/AC-Boolean.cabal
AC-Boolean/1.1.0/AC-Boolean.cabal
AC-BuildPlatform/1.0.0/AC-BuildPlatform.cabal
AC-BuildPlatform/1.1.0/AC-BuildPlatform.cabal
AC-Colour/1.1.1/AC-Colour.cabal
AC-Colour/1.1.2/AC-Colour.cabal
AC-Colour/1.1.3/AC-Colour.cabal
.cabalファイルがただひたすら入ってます。凄く...たくさんです...///
長くなってきたので一旦ここまで
なんだかやたらと長くなってきたので一旦ここまでにします。
そのうちある程度まとまったらまた書きます(^m^;)