静态链接C库与库哈斯克尔 [英] Statically linking a C library with a Haskell library

查看:162
本文介绍了静态链接C库与库哈斯克尔的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Haskell项目,旨在创造出一些C ++绑定。我已经编写的C包装和其编译成一个独立的静态链接库。

I have a Haskell project that aims to create some C++ bindings. I've written the C wrappers and compiled them into a stand-alone statically linked library.

我想写的哈斯克尔绑定静态链接到C包装,这样我就不必单独分发的C包装,但我似乎无法得到它的工作,并会AP preciate一些帮助。

I'd like to write the Haskell bindings to link statically to the C wrappers so that I don't have to distribute the C wrappers separately but I can't seem to get it working and would appreciate some help.

我指定的C库作为一个额外的库,但我的阴谋构建一步似乎并没有将它添加到编译命令。

I specify the C library as an extra library but my cabal build step doesn't seem to add it to compile command.

我创建了一个小项目来说明这个( http://github.com/deech/CPlusPlusBindings )。

I've created a small project to illustrate this (http://github.com/deech/CPlusPlusBindings).

它包含了一个小的C ++类(<一个href=\"https://github.com/deech/CPlusPlusBindings/tree/master/cpp-src\">https://github.com/deech/CPlusPlusBindings/tree/master/cpp-src),在C包装(<一个href=\"https://github.com/deech/CPlusPlusBindings/tree/master/c-src\">https://github.com/deech/CPlusPlusBindings/tree/master/c-src),一个运行的C测试程序(<一个href=\"https://github.com/deech/CPlusPlusBindings/tree/master/c-test\">https://github.com/deech/CPlusPlusBindings/tree/master/c-test)和Haskell的文件(<一个href=\"https://github.com/deech/CPlusPlusBindings/blob/master/src/BindingTest.chs\">https://github.com/deech/CPlusPlusBindings/blob/master/src/BindingTest.chs).

It contains a small C++ class (https://github.com/deech/CPlusPlusBindings/tree/master/cpp-src), the C wrapper (https://github.com/deech/CPlusPlusBindings/tree/master/c-src), a working C test routine (https://github.com/deech/CPlusPlusBindings/tree/master/c-test) and the Haskell file (https://github.com/deech/CPlusPlusBindings/blob/master/src/BindingTest.chs).

C库中添加Setup.hs中没有惊天动地的文件,因为这就是我刚才在构建步骤f之前,我真正的项目中使用它建立在C库制造通过惊天动地。我已经验证,在构建步骤的 extraLibs 部分 BuildInfo 包含库名称, extraLibDirs 包含正确的目录。

The C library is added in Setup.hs not in the Cabal file because that's how I have it my real project which builds the C library using "make" through Cabal just before the build stepf. I have verified that at the build step the extraLibs part of BuildInfo contains the library name and extraLibDirs contains the right directory.

我的阴谋构建的输出是:

creating dist/setup
./dist/setup/setup build --verbose=2
creating dist/build
creating dist/build/autogen
Building CPlusPlusBinding-0.1.0.0...
Preprocessing library CPlusPlusBinding-0.1.0.0...
Building library...
creating dist/build
/usr/local/bin/ghc --make -fbuilding-cabal-package -O -odir dist/build -hidir dist/build -stubdir dist/build -i -idist/build -isrc -idist/build/autogen -Idist/build/autogen -Idist/build -I/home/deech/Old/Haskell/CPlusPlusBinding/c-src -I/home/deech/Old/Haskell/CPlusPlusBinding/cpp-includes -optP-include -optPdist/build/autogen/cabal_macros.h -package-name CPlusPlusBinding-0.1.0.0 -hide-all-packages -package-db dist/package.conf.inplace -package-id base-4.6.0.1-8aa5d403c45ea59dcd2c39f123e27d57 -XHaskell98 -XForeignFunctionInterface BindingTest
Linking...
/usr/bin/ar -r dist/build/libHSCPlusPlusBinding-0.1.0.0.a dist/build/BindingTest.o
/usr/bin/ar: creating dist/build/libHSCPlusPlusBinding-0.1.0.0.a
/usr/bin/ld -x --hash-size=31 --reduce-memory-overheads -r -o dist/build/HSCPlusPlusBinding-0.1.0.0.o dist/build/BindingTest.o
In-place registering CPlusPlusBinding-0.1.0.0...
/usr/local/bin/ghc-pkg update - --global --user --package-db=dist/package.conf.inplace

不幸的是,无论是编辑还是连接步骤使用C库。有没有其他的警告或错误。

Unfortunately neither the compilation nor the linking step uses the C library. There are no other warnings or errors.

推荐答案

要解决这个问题,我必须:

To solve this problem I had to:


  1. 与C绑定目标文件重新链接库的Haskell和

  2. 使用 GHC选项标记在我的惊天动地文件,以确保他们在正确的顺序链接。

  1. re-link the Haskell library with the C bindings' object files and
  2. use the ghc-options tag in my Cabal file to make sure they linked in the right order.

所有的变化都在测试项目中( http://github.com/deech/CPlusPlusBindings )。

All the changes are in the test project (http://github.com/deech/CPlusPlusBindings).

下面创建一个包括C和Haskell的对象进行了详细解释,它不是简单的一个新的压缩文件的过程。出现此复杂性,因为没有办法(如惊天动地1.16.0.2)的挂钩到构建过程的一部分,链接

Below the process of creating a new archive that includes both the C and Haskell objects is explained in detail and it is not simple. The complexity occurs because there is no way (as of Cabal 1.16.0.2) to hook into the linker part of the build process.

在惊天动地文件中设置标志是微不足道的,因此在此不再赘述。

Setting the flags in the Cabal file is trivial so it is not described here.

重新链接哈斯克尔图书馆


  1. 设置构建类型自定义中加入:

build-type: custom

到小集团文件。

通过更换方法 Setup.hs 与插入定制构建逻辑:

Insert customized build logic by replacing the main method in Setup.hs with:

main = defaultMainWithHooks simpleUserHooks {
              buildHook = myBuildHook, 
              ...
       }

这告诉构建过程中,而不是在 simpleUserHooks 定义默认的构建过程中,它应该使用会在 myBuildHook 这将在下面定义的函数。同样,清理过程将被覆盖自定义功能 myCleanHook

This tells the build process that instead of going with the default build process defined in simpleUserHooks it should use the myBuildHook function which is defined below. Similarly the clean up process is overridden with the custom function myCleanHook.

定义构建挂钩。这个构建钩将运行制作命令行来构建C ++和C部分,然后用C的对象文件创建链接的哈斯克尔绑定时。

Define the build hook. This build hook will run make on the command line to build the C++, and C portions and then use the C object files when creating linking the Haskell bindings.

我们开始 myBuildHook

myBuildHook pkg_descr local_bld_info user_hooks bld_flags = do

先运行制作不带参数:

rawSystemExit normal "make" []

然后添加头文件的位置和库目录和库本身的<一个href=\"http://hackage.haskell.org/packages/archive/Cabal/latest/doc/html/Distribution-PackageDescription.html#g:1\">PackageDescription记录并更新<一个href=\"http://hackage.haskell.org/packages/archive/Cabal/latest/doc/html/Distribution-Simple-LocalBuildInfo.html#g:1\">LocalBuildInfo新包装说明:

Then add the locations of the header files and library directories and the library itself to the PackageDescription record and update the LocalBuildInfo with the new package description:

let new_pkg_descr = (addLib . addLibDirs . addIncludeDirs $ pkg_descr)
    new_local_bld_info = local_bld_info {localPkgDescr = new_pkg_descr}

buildHook 打响了 configureHook 存储编译在 compBuildOrder <秩序/ code>(组件构建顺序) LocalBuildInfo 记录的关键。我们需要让我们分开图书馆建设和构建过程的可执行建筑构件图书馆建设隔离。

Before the buildHook fired the configureHook stored the order of compilation in the compBuildOrder (component build order) key of the LocalBuildInfo record. We need to isolate the building of the library so we separate the library building and executable building parts of the build process.

构建顺序只是一个列表,我们知道构建组件是一个库,如果它只是一个普通的 CLibName 类型构造,所以我们隔离从列表和更新的元素在 LocalBuildInfo 纪录只是其中:

The build order is just a list and we know the build component is a library if it's just a plain CLibName type constructor so we isolate those elements from the list and update the LocalBuildInfo record with only them:

let (libs, nonlibs) = partition
                       (\c -> case c of
                                CLibName -> True
                                _ -> False)
                       (compBuildOrder new_local_bld_info)
    lib_lbi = new_local_bld_info {compBuildOrder = libs}


  • 现在我们有更新的记录运行默认的构建挂钩:

  • Now we run the default build hook with the updated records:

    buildHook simpleUserHooks new_pkg_descr lib_lbi user_hooks bld_flags
    


  • 一旦完成建设已创建的归档文件,但我们必须重新创造它包括在步骤1中的制作命令生成的C对象。因此,我们抓住一些设置和C的对象文件路径的列表:

  • Once it's done building an archive has been created but we have to re-create it to include the C objects generated by the make command in step 1. So we grab some settings and a list of the C object file paths:

    let verbosity = fromFlag (buildVerbosity bld_flags)
    info verbosity "Relinking archive ..."
    let pref = buildDir local_bld_info
        verbosity = fromFlag (buildVerbosity bld_flags)
    cobjs <- getLibDirContents >>= return . map (\f -> combine clibdir f) 
                                          . filter (\f -> takeExtension f == ".o")
    

    然后将它交给 withComponentsLBI ,其作用构建的各个组件。在这种情况下,因为我们只处理库的一部分,只有一个组成部分。 惊天动地 getHaskellObjects 获取Haskell的目标文件列表和 createArLibArchive 创建一个档案,所以我们可以重新运行链接:

    And then hand it off to withComponentsLBI which acts on each component of the build. In this case since we're only dealing with the library part there is only one component. Cabal provides getHaskellObjects for getting a list of the Haskell object files and createArLibArchive for creating an archive so we can re-run the linker:

     withComponentsLBI pkg_descr local_bld_info $ \comp clbi ->
      case comp of
        (CLib lib) -> do
                  hobjs <- getHaskellObjects lib local_bld_info pref objExtension True
                  let staticObjectFiles = hobjs ++ cobjs
                  (arProg, _) <- requireProgram verbosity arProgram (withPrograms local_bld_info)
                  let pkgid = packageId pkg_descr
                      vanillaLibFilePath = pref </> mkLibName pkgid
                  Ar.createArLibArchive verbosity arProg vanillaLibFilePath staticObjectFiles
        _ -> return ()
    


  • 默认 buildHook 这是在步骤4执行命令创建了一个名为package.conf.inplace临时包数据库文件用于存放库的描述中建这样可执行文件可以反对它不链接库需要被安装到默认的系统包文件。不幸的是每个 buildHook 运行空白出来,所以我们需要留住的临时副本:

  • The default buildHook which was run in Step 4 created a temporary package database file named "package.conf.inplace" which holds the description of the library that was built so that executable can link against it without the library needing to be installed to the default system package file. Unfortunately every buildHook run blanks it out so we need to hold on to a temporary copy:

      let distPref  = fromFlag (buildDistPref bld_flags)
          dbFile = distPref </> "package.conf.inplace"
     (tempFilePath, tempFileHandle) <- openTempFile distPref "package.conf"
     hClose tempFileHandle
     copyFile dbFile tempFilePath
    


  • 现在我们存储到复制到 LocalBuildInfo 结构的路径与是在第3步<过滤掉构建过程的可执行部分以及/ p>

  • Now we store a path to that copy into the LocalBuildInfo structure along with the executable parts of the build process which were filtered out in Step 3.

      let exe_lbi = new_local_bld_info {
                      withPackageDB = withPackageDB 
                                        new_local_bld_info ++ 
                                       [SpecificPackageDB tempFilePath], 
                      compBuildOrder = nonlibs
                    }
    

    ,并在 PackageDescription ,因此它可以由默认拆下清洗的 extraTmpFiles 部分再存放路径达钩子。

    and store the path again in the extraTmpFiles part of the PackageDescription so it can be removed by the default clean up hook.

    exe_pkg_descr = new_pkg_descr {extraTmpFiles = extraTmpFiles new_pkg_descr ++ [tempFilePath]}
    


  • 现在我们终于运行默认 buildHook 与刚刚可执行组件更新的记录(现在知道新的存档)又说:

  • Now we finally run the default buildHook again with the updated records (which now know about the new archive) on just the executable components:

    buildHook simpleUserHooks exe_pkg_descr exe_lbi user_hooks bld_flags
    


  • 这篇关于静态链接C库与库哈斯克尔的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆