昨日はGHCi環境のカスタマイズについて説明した。
今日は、GHCを使ったビルドについて。
GHCでのビルド
以下のHaskellのソースコードをビルドして、実行ファイルを作ることを考える。
-- Main.hs import System.Environment import Control.Monad import Fibo main = do args <- getArgs when (length args > 0) $ do let count = read $ head args print $ map fibo [1..count]
-- Fibo.hs module Fibo (fibo) where fibo :: Integer -> Integer fibo n = fibo' n 1 1 fibo' :: Integer -> Integer -> Integer -> Integer fibo' 1 a b = a fibo' n a b = fibo' (n - 1) b (a + b)
これをビルドするには、次のように、ghc
コマンドの引数にMain.hsを指定すればいい。
$ ghc Main.hs
上のコマンドを実行すると、以下のようになる。
$ ls Fibo.hs Main.hs $ ghc Main.hs [1 of 2] Compiling Fibo ( Fibo.hs, Fibo.o ) [2 of 2] Compiling Main ( Main.hs, Main.o ) Linking Main ... $ ls Fibo.hi Fibo.hs Fibo.o Main* Main.hi Main.hs Main.o $ ./Main 10 [1,1,2,3,5,8,13,21,34,55] $
コンパイルされてオブジェクトファイル(拡張子は.o
)とインタフェースファイル(拡張子は.hi
)が作られ、そして、リンクが行われてMainという実行ファイルが作られていることが分かると思う。
依存するモジュールのコンパイル
ここで一つポイントになるのが、Fibo.hsは引数に指定していないということ。
GHCはimport宣言から依存関係を調べて、必要となるモジュールのコンパイルも行ってくれるようになっている。
これはさらに、Makeのようにファイルのタイムスタンプを確認して、必要ならコンパイルを行うということもやってくれる。
$ ls Fibo.hi Fibo.hs Fibo.o Main* Main.hi Main.hs Main.o $ ghc Main.hs -- ファイルは最新なので、何も起こらない $ touch Fibo.hs -- Fibo.hsのタイムスタンプを更新 $ ghc Main.hs [1 of 2] Compiling Fibo ( Fibo.hs, Fibo.o ) -- Fibo.hsがコンパイルされる Linking Main ... $
見てのとおり、Fibo.hsのタイムスタンプを更新すると、Fibo.hsだけコンパイルを行って、そしてリンクしてくれているのが分かる。
これはかなり便利。
ソースファイルの置き場所
import宣言からモジュールの定義されているソースファイルを見つけられるようにするために、ソースファイルは規則に従った場所に置く必要がある。
といっても、細かい規則があるわけではなくて、ドット.
で区切られて入れ子になっているモジュールのソースファイルは、検索パスに含まれるディレクトリを起点として、ドットをディレクトリの区切りに置き換えた場所に置きましょう、というだけ。
例えば、A.B.Cというモジュールなら、(検索パスに含まれるディレクトリ)/A/B/C.hsとする必要がある。
もしモジュールが入れ子になっていないのであれば、検索パスに含まれるディレクトリにソースファイルが置いてあれば問題ない。
検索パスの追加
検索パスを追加したい場合には、-i
オプションを使う。
-i
に続いて(半角スペースを入れずに)検索パスに追加したいディレクトリを指定すればいい。
(複数指定したい場合、コロン:
で区切る)
例えば、先程のMain.hsとFibo.hsをsrc/というディレクトリに置いてビルドするには、次のようにする:
$ mkdir src $ mv Main.hs Fibo.hs src/ $ ls src/ $ ls src/ Fibo.hs Main.hs $ ghc -isrc src/Main.hs [1 of 2] Compiling Fibo ( src/Fibo.hs, src/Fibo.o ) [2 of 2] Compiling Main ( src/Main.hs, src/Main.o ) Linking src/Main ... $ ls src/ $ ls src/ Fibo.hi Fibo.hs Fibo.o Main* Main.hi Main.hs Main.o $
ここで、ちょっと気をつけたいことが2つ。
まず、ghc
の引数には、Main.hsではなくsrc/Main.hsを指定しないといけないということ。
検索パスにsrc/が入っているので、Main.hsを指定すれば見つけてくれそうなものだけど、見つけようとするのはモジュール名に対するソースファイルなので、引数で指定されたソースファイル自体を見つけてくれるわけではない。
もう一つは、出力がsrc/の中にされているということ。
GHCは、デフォルトでは出力をソースファイルと同じ場所に行うようになっている。
なので、オブジェクトファイルやインタフェースファイル、実行ファイルが、ソースファイルの置かれているsrc/に出力されている。
出力先のコントロール
オブジェクトファイル、インタフェースファイルや、実行ファイルが生成される場所が、ソースファイルを置いてあるディレクトリと同じだと、ごちゃごちゃして分かりにくい。
なので、生成されるファイルの出力先をコントロールするためのオプションが用意されている。
最終的な出力ファイル名の指定
最終的な出力ファイル名を指定するには、-o
オプションを使う。
$ ghc -isrc -o fibonacci src/Main.hs [1 of 2] Compiling Fibo ( src/Fibo.hs, src/Fibo.o ) [2 of 2] Compiling Main ( src/Main.hs, src/Main.o ) Linking fibonacci ... $ ls fibonacci* src/ $ ls src/ Fibo.hi Fibo.hs Fibo.o Main.hi Main.hs Main.o $
最終的に作られた実行ファイルが指定された名前になっていることが分かると思う。
オブジェクトファイルなどの出力先の指定
オブジェクトファイルやインタフェースファイルを出力するディレクトリを指定するには、-outputdir
オプションを使う。
$ ghc -isrc -outputdir bin -o fibonacci src/Main.hs [1 of 2] Compiling Fibo ( src/Fibo.hs, bin/Fibo.o ) [2 of 2] Compiling Main ( src/Main.hs, bin/Main.o ) Linking fibonacci ... $ ls bin/ fibonacci* src/ $ ls bin/ Fibo.hi Fibo.o Main.hi Main.o $ ls src/ Fibo.hs Main.hs $
オブジェクトファイルやインタフェースファイルが指定されたディレクトリに出力されているのが分かると思う。
これでソースファイルの入ったディレクトリはキレイなまま。
なお、出力先に指定されたディレクトリが存在しなければ、勝手に作ってくれる。
便利!
今日はここまで!