Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a statically-linked Haskell library on Mac

I am using: Mac OS X 10.10, GHC 7.10.2 (Haskell Platform), Cabal 1.22

I am trying to create a statically-linked library in Haskell. The intention is to provide a library with a C-compatible ABI, for use cross-platform and cross-language. None of the consumers of this library are expected to be implemented in Haskell so the C-compatible ABI is critical, as is ease of distribution of the resulting library.

My problem is, none of the FFI tutorials / examples use 3rd party modules, and hardly any even mention cabal. Both of these are prerequisites for me. When I follow a simple FFI tutorial it works, but as soon as I put in a 3rd party module, it fails for me.

I created a very simple example detailing my problem at: https://github.com/tomkludy/ffihell

If you build the library using cabal configure;cabal build, you will find in dist\build a library named something like libHSffihell-0.1.0.0-70CjWiqse6C2Al3vL5a4k7 (followed by .a, _p.a, and -ghc7.10.2.dylib).

Problem 1: The library is named something different every time. How can I make cabal name the library the same every time I build it?

If you then build the C program that consumes it (tryit.c) by modifying build_c.sh to point to the right library name, it works:

> ghc tryit.c -Idist/build -Ldist/build -lHSffihell-0.1.0.0-70CjWiqse6C2Al3vL5a4k7 -no-hs-main -o tryit
> ./tryit
foo 4: 5

However, if you uncomment these lines in Foo.hs, causing it to pull in the Data.Text library:

{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings #-}
module Foo where

import qualified Data.Text as T

foreign export ccall foo :: Int -> IO Int

foo :: Int -> IO Int
foo n = return $ n + 1

-- UNCOMMENTED BELOW HERE...
foreign export ccall bar :: Int -> IO ()
bar :: Int -> IO ()
bar n = putStrLn $ T.unpack $ T.concat $ ["." :: T.Text | _ <- [1..n]]

Then try to build...

> cabal build
(... no errors ...)
> ghc tryit.c -Idist/build -Ldist/build -lHSffihell-0.1.0.0-70CjWiqse6C2Al3vL5a4k7 -no-hs-main -o tryit
Undefined symbols for architecture x86_64:
  "_textzu1l1AN4I48k37RaQ6fm6CEh_DataziText_concat_closure", referenced from:
      _S45B_srt in libHSffihell-0.1.0.0-70CjWiqse6C2Al3vL5a4k7.a(Foo.o)
  "_textzu1l1AN4I48k37RaQ6fm6CEh_DataziText_concat_info", referenced from:
      _ffihezu70CjWiqse6C2Al3vL5a4k7_Foo_zdfstableZZC0ZZCffihezzu70CjWiqse6C2Al3vL5a4k7ZZCFooZZCbar2_info in libHSffihell-0.1.0.0-70CjWiqse6C2Al3vL5a4k7.a(Foo.o)
      _c4bD_info in libHSffihell-0.1.0.0-70CjWiqse6C2Al3vL5a4k7.a(Foo.o)
  "_textzu1l1AN4I48k37RaQ6fm6CEh_DataziTextziShow_unpackCStringzh_closure", referenced from:
      _S45B_srt in libHSffihell-0.1.0.0-70CjWiqse6C2Al3vL5a4k7.a(Foo.o)
  "_textzu1l1AN4I48k37RaQ6fm6CEh_DataziTextziShow_unpackCStringzh_info", referenced from:
      _ffihezu70CjWiqse6C2Al3vL5a4k7_Foo_zdfstableZZC0ZZCffihezzu70CjWiqse6C2Al3vL5a4k7ZZCFooZZCbar4_info in libHSffihell-0.1.0.0-70CjWiqse6C2Al3vL5a4k7.a(Foo.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Problem 2: Can't find symbols related to 3rd party module

The errors seem to be related to symbols imported by Data.Text.

This example is as trivial as I could make it, but my real library has about 50 3rd party modules, and the list of modules will change over time as I expand the functionality provided. I need to get to a single statically linked library that includes my own functions + all of the dependencies so that the people using my library won't have to keep changing their build scripts/makefiles/etc.

Problem 3: Unable to build with clang/gcc

The only way that I could get the "tryit.c" program to link with my program at all, even before consuming 3rd parties, was to compile it with GHC. Since my consumers are not programming Haskell they will not have GHC. Is there any documentation on compiling programs against Haskell libraries, NOT using GHC for the compilation? I could not find anything... Just docs stating that it "should be possible" but without any details.

Thanks for any help!

Update I was able to get much further by using ld -r as suggested below, in combination with -force_load(my library) and -reexport_library(every module library, plus many of the GHC framework libraries). You can see the new code at the same git repo. The build_lib.sh builds the Haskell library, and build_c.sh then uses that library to build a C-source executable that consumes it.

I am still unable to solve Problem #1 or Problem #3, and I am also struggling to find a way to automatically pick up all of the right module library dependencies. I will update if I get a complete solution.

like image 680
Tom Kludy Avatar asked Nov 10 '22 06:11

Tom Kludy


1 Answers

In short, use ghc -v to build an application that uses your library and see how it invokes gcc in the link step, then use ld -r instead. You might also want to adjust the link command to link against some C dependencies like GMP statically, depending on your needs.

like image 67
Reid Barton Avatar answered Nov 15 '22 06:11

Reid Barton