ジンジャー研究室

長めのつぶやき。難しいことは書きません。

cabal install/build 時に実行時に参照するファイルを含める方法

やりたいこと

いまいち上手く日本語に出来なかったので図解する。

f:id:jinjor:20150817141116p:plain

コマンドラインツール等で、実行時に手元のファイルをテンプレートとして利用したり、静的ファイルをディレクトリごとコピったりしたいことが良くある。でもそのままExecutableにするとファイルが付いてこなくてどうしよう、と言う話。上の図で言うと、赤い矢印で示したファイル参照を実現したい。

.cabalファイルの記述

実行時に必要なファイルを.cabalファイルに記述する。Data-dir:に必要なディレクトリ、Data-files:にそのディレクトリ下のファイルを羅列する。この指定が曲者で拡張子ワイルドカードに出来ない。なので、/**/*とかにしたいのを我慢しつつ、拡張子をひとつずつ記述する。

foobar.cabal

Data-dir:
  data

Data-files:
  templates/*.html.tmpl
  templates/*.js.tmpl
  assets/*.svg
  assets/*.ico
  assets/*.png

Haskellから呼び出す

foobarというパッケージに付随するファイルは、Paths_foobar.getDataFileName :: FilePath -> IO FilePathで呼び出せるようにcabalがコンパイルしてくれる。魔法か。

Main.hs

import Paths_foobar

main :: IO ()
main =
  do
    path <- Paths_foobar.getDataFileName "templates/app.js.tmpl"
    putStrLn path -- 絶対パス

どうなっているのか

手元のWindowsで、インストール先はこんな感じになってた。

C:\Users\UserName\AppData\Roaming\cabal
 ├ bin
 │  └ foobar.exe
 └ x86_64-windows-ghc-7.10.2
     └ foobar
       ├ templates
       └ assets

応用例:インストール時にコンパイルしたファイルを使う

データディレクトリに入れるファイルをインストール時に動的に作りたい場合がある。例えば、配布しているソースは.coffeeで、実際に使うのはコンパイルした.jsという場合。

.cabalファイルで、Build-type: Customとすると、Setup.hsファイルにビルド時に呼ばれるフックを記述できる。内容は略。

foobar.cabal

Build-type: Custom

Setup.hs

(略)

作ったもの

Elmでパッケージを公開する前にドキュメントをプレビューできるツール

github.com

参考用にどうぞ。