OSX、ghci、dylib,正确的方式是什么?

23

我需要编译一些C代码,然后通过FFI引用它们。我想在OSX上使用ghci内的绑定。我的限制之一是不能在.cabal文件中直接把C源代码交给ghc。这是由于ghc / cabal的一个限制,可能会在ghc的下一个版本中修复(但我希望我的代码现在和旧版本中都能正常工作)。有关详细信息,请参见此错误

该错误的主要问题在于,C代码需要与一些Objective-C模块一起编译,而ghc将它们误解为链接器脚本。 我尝试了很多东西,自己用makefile构建文件是唯一有效的方法。 但实际上,这不应该成为一个问题,因为它应该与我决定使用未经自己构建的外部C库相同。 为了解决此问题,让我们假装它是一个可以轻松重建不同选项的单独的C库。

如果我将C库构建为.a,则ghci会抱怨无法打开.dlyb。我的第一个问题是:为什么ghci需要.dlyb并且它真的使用它吗?

当我构建dylib时,加载代码到ghci时会出现段错误

请记住,此绑定已在其他平台(Linux和Windows)上正常工作,而且当我编译而不是使用ghci时,在OSX上绑定也可以正常工作。这个问题只出现在osx / ghci组合中。

在上面的跟踪中,我正在使用gdb,但无论是否使用gdb都会崩溃。我追踪到了导致崩溃的代码行:

void _glfwClearWindowHints( void )
{
    memset( &_glfwLibrary.hints, 0, sizeof( _glfwLibrary.hints ) );
}

问题出在memset那一行,实际上问题是当在ghci内运行时写入_glfwLibrary的hints结构体会导致内存访问冲突。hints结构体只是一些整数。它非常简单,因此我认为问题可能是链接方式或ghci加载代码的方式有问题。

以下是我使用的makefile部分内容,用于构建dylib和.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)

大部分标志都直接从GLFW Makefile中获取,因此我认为它们应该适用于该库。

第一行看起来有点奇怪,但这是我为解决这个问题所使用的解决方案。

平台细节:

编辑:这是我的问题:

  • 这是否适用于ghci?
  • 如果是这样,我做错了什么或如何修复崩溃?
  • 我能否只使用库的静态.a版本与ghci配合使用?

这主要是一个Cabal问题,因此我删除了“gui”标签,改为添加“cabal”。 - Raeez
1个回答

9

初步问题

这个应该可以在ghci中运行吗? 如果是的话,我做错了什么或者如何修复崩溃?

在OSX 10.6.7(使用Haskell平台/ GHC 7.0.2),我可以按以下方式将您构建的共享库加载到ghci中:

➜  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  

注意:我使用您提供的Makefile构建了glfw库,并另外使用了您的.cabal文件来处理src/Graphics/UI/GLFW.hsc并构建dist/build/Graphics/UI/GLFW.hs(即我之前运行过cabal configure/build)。

我是否可以只使用静态.a版本的库和ghci一起使用?

是的,GHC 7.0.2(GHC手册)包含了加载静态库的支持。compiler/ghci/Linker.lhs非常有用,它将让您对ghci如何决定命令行参数的意义有一个高层次的认识。此外,当处理各种平台支持问题时,我发现这份文档非常有用。

使用ghci链接静态存档。

截至撰写本文,compiler/ghci/Linker.hs 的第 1113 行表明 ghci 目前要求静态归档文件由包系统构建(即命名为 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           

进一步调查命令行参数解析表明,指定的库在reallyInitDynLinker函数的第402行被收集:
; classified_ld_inputs <- mapM classifyLdInput cmdline_ld_inputs

其中classifyLdInput是定义在上面的

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

这意味着,在软件包规范之外,目前没有直接的方法在ghci中链接存档文件(或者换句话说,目前没有命令行参数来执行此操作)。
修复您的cabal软件包:
在您的.cabal软件包规范中,您正在尝试构建两个冲突的库:
- A:链接到预构建库(根据Setup.hs和Makefile中的规范构建,并按照extra-libraries和extra-lib-dirs指令进行链接) - B:内联构建新库(c-sources和frameworks指令)
解决上述错误的简单方法是在为Mac OSX构建时删除启用B的所有指令。
   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

并且

     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

我没有测试过除了验证以下内容现在是否正常工作之外的任何内容:

➜  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>      

非常感谢!我刚刚在我的OSX机器上验证了这个修复程序,在以前它总是失败。 - Jason Dagit
看起来在完全运行之前,我需要微调一下我的 Setup.hs。当我尝试构建一个依赖它的程序时,我收到了这个警告:ld: 警告:目录“build/dynamic”在-L后未找到。 - Jason Dagit
当我运行生成的程序时,它会显示: dyld:库未加载:build/dynamic/libglfw.dylib 引用自:/Users/dagit/tmp/test-glfw/./cabal-dev/bin/lesson01 原因:找不到图像 Trace/BPT trap - Jason Dagit
我通过在我的 Setup.hs 中添加一个 post install 步骤来解决了这个问题,该步骤调用 install_name_tool 来设置 dylib 的绝对路径。您可以在此 diff 中查看更改:https://github.com/dagit/GLFW-b/commit/8c46810c057cdb24451ddfa57e2ec33a4cf19f55#Setup.hs - Jason Dagit
@Jason——不幸的是,cabal目前还没有将C库的一般部署视为一流功能(使打包构建库有点困难);我建议您将libglfw.dylib(依赖于操作系统)安装到系统范围内,或计算cabal目标安装目录,并在cabal configure阶段将其添加到您的.cabal文件的extra-lib-dirs指令中(更具可移植性)。两者都可以工作,但归根结底,直接让cabal构建库会是理想的选择。 - Raeez
我上面概述的解决方案使用install_name_tool确实计算了目标安装目录。我认为我不需要将其放在extra-lib-dirs中,因为我在使用库时没有遇到任何问题,但如果出现任何问题,这将是我尝试的第一件事。再次感谢。 - Jason Dagit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接