Haskellでiconv
苦戦しちゃったので作業メモを残しておきます。
ビルド環境はGHC 6.10.4です。
iconv.dllをインポートする
dllをダウンロード
http://www.kaoriya.net/
ここから、ビルド1.10-20060516のdllを取得してくる。
reimpのインストール
http://sourceforge.net/projects/mingw/files/
ここから、mingw utilitiesを取得してくる。
但し、Version 0.4.1だと、何故か上記dllのreimpに失敗してしまうため、Version 0.3を使うこと。
reimpを実行する
reimp -c -d iconv.lib
これで、.defが手に入る。
dlltoolを実行する
dlltool -v -d iconv.def -D iconv.dll -l libiconv.dll.a
これで、.dll.aが手に入る。
Haskell版iconvパッケージをインポートする
Hackageからダウンロード
http://hackage.haskell.org/package/iconv
ここから、iconv-0.4.0.2.tar.gzをダウンロード。
ここの説明に6.10でビルドできると書いてあるのに…罠じゃないのか、これは。
configure
runghc setup configure --extra-include-dirs=C:\sdk\iconv-1.10-20060516-dll --extra-lib-dirs=C:\sdk\iconv-1.10-20060516-dll
ディレクトリは適宜修正する。
build
runghc setup build
エラーが出ます。
[2 of 2] Compiling Codec.Text.IConv ( Codec\Text\IConv.hs, dist\build\Codec\Text\IConv.o ) Codec\Text\IConv.hs:152:29: Not in scope: data constructor `Exception.IOException'
http://d.hatena.ne.jp/sirocco/20090507/1241668523
ここのコメントによると、Exceptionの仕様変更らしい。
--- IConv.hs.old 2009-05-08 05:06:30.000000000 +0900 +++ IConv.hs 2009-05-08 05:07:06.000000000 +0900 @@ -53,8 +53,8 @@ import Prelude hiding (length, span) -import Control.Exception (assert) -import qualified Control.Exception as Exception +import Control.OldException (assert) +import qualified Control.OldException as Exception import Foreign.C.Error as C.Error (Errno, errnoToIOError) import qualified Data.ByteString.Lazy as L (ByteString, toChunks, fromChunks)
これで、buildできるようになる。
install
runghc setup install
一応、ここまでは成功する。
iconvのテストプログラムを書いてビルド
適当に、ソースを書いてみます。
やっぱりCで書くのに比べると断然楽だよなあ。
import Codec.Text.IConv (convert) import qualified Data.ByteString.Lazy as L main :: IO () main = L.interact $ convert "ISO-2022-JP" "UTF-8"
で、ビルド。
E:\iconvtest>ghc --make iconvtest.hs [1 of 1] Compiling Main ( iconvtest.hs, iconvtest.o ) Linking iconvtest.exe ... C:\Program Files (x86)\Haskell\iconv-0.4.0.2\ghc-6.10.4/libHSiconv-0.4.0.2.a(Int ernal.o):fake:(.text+0x2196): undefined reference to `iconv_open' C:\Program Files (x86)\Haskell\iconv-0.4.0.2\ghc-6.10.4/libHSiconv-0.4.0.2.a(Int ernal.o):fake:(.text+0x2348): undefined reference to `iconv_close' C:\Program Files (x86)\Haskell\iconv-0.4.0.2\ghc-6.10.4/libHSiconv-0.4.0.2.a(Int ernal.o):fake:(.text+0x33f3): undefined reference to `iconv' collect2: ld returned 1 exit status
むぅ…。
iconv.dll.aへのリンクが通ってないと思われるので、-liconvなどのオプションを色々試すもダメ。
根本的に何かが違うらしい。
ソースの見直し
Internal.hsの中身を見てみると、以下のような部分を発見した。
---------------------- -- The foreign imports newtype ConversionDescriptor = ConversionDescriptor (ForeignPtr ConversionDescriptor) -- iconv_t foreign import ccall unsafe "iconv.h iconv_open" c_iconv_open :: CString -- to code -> CString -- from code -> IO (Ptr ConversionDescriptor) foreign import ccall unsafe "iconv.h iconv" c_iconv :: Ptr ConversionDescriptor -> Ptr (Ptr CChar) -- in buf -> Ptr CSize -- in buf bytes left -> Ptr (Ptr CChar) -- out buf -> Ptr CSize -- out buf bytes left -> IO CSize foreign import ccall unsafe "iconv.h &iconv_close" c_iconv_close :: FinalizerPtr ConversionDescriptor
関数プロトタイプでも違うんだろうか、と思ってiconv.hの中身を開いてみる。
#ifndef LIBICONV_PLUG #define iconv_open libiconv_open #endif extern LIBICONV_DLL_EXPORTED iconv_t iconv_open (const char* tocode, const char* fromcode);
正直、こういう定義はあまりやって欲しくないなあ…
まあそれはともかく、恐らくGHC側でLIBICONV_PLUGを定義しているとは考えにくいし、
.defや.dllの中身を見ると、明らかに関数名はlibiconv_openなどの定義になっているので、
Internal.hsの中身をちょいちょい修正して、libiconv_openなどの名前に変えてやります。
これでconfigure/build/install…するも、やっぱりダメ。
これ自体は合ってるらしいんだけど、まだ足りない。
cabalのソース見直し
(意味を調べるのが面倒だったので)cabalの中身は見てなかったのだけれど、問題になりそうなところ発見。
if os(darwin) || os(freebsd) -- on many systems the iconv api is part of the standard C library -- but on some others we have to link to an external libiconv: extra-libraries: iconv
どうやら、ここでiconv.dll.aをリンクするかどうか決定しているらしいです。
(どう見てもソース的にアレですが)どうせwindowsでしか使わないので以下のようにコードを追加してみる。
if os(darwin) || os(freebsd) -- on many systems the iconv api is part of the standard C library -- but on some others we have to link to an external libiconv: extra-libraries: iconv else extra-libraries: iconv.dll
これでconfigure/build/install。ビルドしたところ、正常に動作しました。