I need to build some C code and then reference that C code via the FFI. I would like to use my binding from inside ghci on osx. On of my constraints is that I cannot just hand the C sources to ghc in the .cabal file. This is due to a limitation with ghc/cabal that may be fixed in the next release of ghc (but I want my code to work now and on older releases). See this bug for details.
The gist of that bug is that the C code needs to be compiled with some Objective-C modules and ghc misinterprets those as linker scripts. I've tried many things and building the files myself with a makefile is the only thing that has worked. Really, this shouldn't be an issue though because it should be the same as if I decided to use a external C library that I didn't build myself. For the sake of this issue, let's pretend it's a separate C library that I can easily rebuild with different options.
If I build the C library as a .a, then ghci comlains that it cannot open the .dylib. My first question is: Why does ghci need a .dylib and does it really use it?
When I build a dylib I get a segfault when loading the code into ghci.
Keep in mind, this binding works already on other platforms, both linux and windows, and the binding works fine on osx when I'm compiling instead of using ghci. This problem specific to the osx/ghci combo.
In that trace above, I'm using gdb but it crashes regardless of whether I use gdb. I tracked it down to the lines that cause the crash:
void _glfwClearWindowHints( void ) { memset( &_glfwLibrary.hints, 0, sizeof( _glfwLibrary.hints ) ); }
The trouble maker is that memset line, well actually the problem is that when running inside ghci writing to the hints structure of _glfwLibrary
is a memory access violation. The hints struct is simply a bunch of ints. It's very flat and simple, and thus I think the problem is an issue either with how I'm linking things or with the way ghci is loading the code.
Here are the bits of my makefile that I use to build the dylib and the .a:
GCCFLAGS := $(shell ghc --info | ghc -e "fmap read getContents >>= \ putStrLn . unwords . read . Data.Maybe.fromJust . lookup \ \"Gcc Linker flags\"") FRAMEWORK := -framework Cocoa -framework OpenGL GLFW_FLAG := $(GCCFLAGS) -O2 -fno-common -Iglfw/include -Iglfw/lib \ -Iglfw/lib/cocoa $(CFLAGS) all: $(BUILD_DIR)/static/libglfw.a $(BUILD_DIR)/dynamic/libglfw.dylib $(BUILD_DIR)/dynamic/libglfw.dylib: $(OBJS) $(CC) -dynamiclib -Wl,-single_module -compatibility_version 1 \ -current_version 1 \ $(GLFW_FLAG) -o $@ $(OBJS) $(GLFW_SRC) $(FRAMEWORK) $(BUILD_DIR)/static/libglfw.a: $(OBJS) ar -rcs $@ $(OBJS)
Most of the flags are taken straight from the GLFW Makefile so I think they should be correct for that library.
The first line looks a bit weird but it's the solution I used for this problem.
Platform details:
Edit: Here are my questions:
Should this work with ghci? If so, what am I doing wrong or how can I fix the crash?
On OSX 10.6.7 (using the Haskell Platform /w GHC 7.0.2) I could load your built shared lib into ghci as follows:
➜ GLFW-b git:(master) ✗ ghci dist/build/Graphics/UI/GLFW.hs -Lbuild/dynam ic -lglfw GHCi, version 7.0.2: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer-gmp ... linking ... done. Loading package base ... linking ... done. Loading package ffi-1.0 ... linking ... done. Loading object (dynamic) glfw ... done final link ... done [1 of 1] Compiling Graphics.UI.GLFW ( dist/build/Graphics/UI/GLFW.hs, inte rpreted ) Ok, modules loaded: Graphics.UI.GLFW. *Graphics.UI.GLFW> initialize True
Note: I built the glfw
libs using your provided Makefile
, and additionally used your .cabal
file to process src/Graphics/UI/GLFW.hsc
and build dist/build/Graphics/UI/GLFW.hs
(i.e. I'd previously run cabal configure/build
).
Can I just get by with the static .a version of the library with ghci?
Yes, support for loading static libs was included in GHC 7.0.2 (GHC Manual). compiler/ghci/Linker.lhs
is a great read, and will give you a high-level sense of how ghci decides what to make of the command line arguments passed to it. Additionally, when navigating various platform support issues, I've found this documentation exceedingly useful.
As of writing, line 1113
of compiler/ghci/Linker.hs
demonstrates that ghci
currently requires that static archives be built by the package system (i.e. named HSlibname.a
)
locateOneObj :: [FilePath] -> String -> IO LibrarySpec locateOneObj dirs lib | not ("HS" `isPrefixOf` lib) -- For non-Haskell libraries (e.g. gmp, iconv) we assume dynamic library = assumeDll | not isDynamicGhcLib -- When the GHC package was not compiled as dynamic library -- (=DYNAMIC not set), we search for .o libraries or, if they -- don't exist, .a libraries. = findObject `orElse` findArchive `orElse` assumeDll
Further investigation of cmd line argument parsing indicates that libraries specified are collected at line 402
in the reallyInitDynLinker
function:
; classified_ld_inputs <- mapM classifyLdInput cmdline_ld_inputs
where classifyLdInput
is defined over
classifyLdInput :: FilePath -> IO (Maybe LibrarySpec) classifyLdInput f | isObjectFilename f = return (Just (Object f)) | isDynLibFilename f = return (Just (DLLPath f)) | otherwise = do hPutStrLn stderr ("Warning: ignoring unrecognised input `" ++ f ++ "'") return Nothing
This means that outside of a package specification, there currently is no direct way to link an archive file in ghci (or said differently, there currently is no cmd-line argument to do so).
In your .cabal
package specification, you are attempting to build two conflicting libraries:
Setup.hs
and Makefile
, and linked as per the extra-libraries
and extra-lib-dirs
directives)c-sources
and frameworks
directives).A simple fix for the error above is to simply remove all directives enabling B when building for Mac OSX, as follows:
include-dirs: glfw/include glfw/lib - c-sources: - glfw/lib/enable.c - glfw/lib/fullscreen.c - glfw/lib/glext.c - glfw/lib/image.c - glfw/lib/init.c - glfw/lib/input.c - glfw/lib/joystick.c - glfw/lib/stream.c - glfw/lib/tga.c - glfw/lib/thread.c - glfw/lib/time.c - glfw/lib/window.c + + if !os(darwin) + c-sources: + glfw/lib/enable.c + glfw/lib/fullscreen.c + glfw/lib/glext.c + glfw/lib/image.c + glfw/lib/init.c + glfw/lib/input.c + glfw/lib/joystick.c + glfw/lib/stream.c + glfw/lib/tga.c + glfw/lib/thread.c + glfw/lib/time.c + glfw/lib/window.c
and
if os(darwin) - include-dirs: - glfw/lib/cocoa - frameworks: - AGL - Cocoa - OpenGL extra-libraries: glfw - extra-lib-dirs: build/static build/dynamic + extra-lib-dirs: build/dynamic
I've not tested anything beyond verifying that the following now works properly:
➜ GLFW-b git:(master) ✗ ghci GHCi, version 7.0.2: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer-gmp ... linking ... done. Loading package base ... linking ... done. Loading package ffi-1.0 ... linking ... done. Prelude> :m + Graphics.UI.GLFW Prelude Graphics.UI.GLFW> initialize Loading package GLFW-b-0.0.2.6 ... linking ... done. True Prelude Graphics.UI.GLFW>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With