2013-08-31 1 views
13

У меня есть проект Haskell, целью которого является создание некоторых привязок C++. Я написал обертки C и скомпилировал их в автономную статически связанную библиотеку.Статическая связь библиотеки C с библиотекой Haskell

Я хотел бы написать привязки Haskell, чтобы связать статически с обертками C, чтобы мне не пришлось распространять обертки C отдельно, но я не могу заставить его работать, и мне будет очень полезно помочь.

Я указываю библиотеку C как дополнительную библиотеку, но мой cabal build шаг, похоже, не добавляет ее в команду компиляции.

Я создал небольшой проект, чтобы проиллюстрировать это (http://github.com/deech/CPlusPlusBindings).

Он содержит небольшой C++ класс (https://github.com/deech/CPlusPlusBindings/tree/master/cpp-src), С обертка (https://github.com/deech/CPlusPlusBindings/tree/master/c-src), тест рабочего дня C (https://github.com/deech/CPlusPlusBindings/tree/master/c-test) и Haskell файл (https://github.com/deech/CPlusPlusBindings/blob/master/src/BindingTest.chs).

Библиотека C добавлена ​​в Setup.hs не в файле Cabal, так как у меня есть это мой реальный проект, который строит библиотеку C, используя «make» через Cabal непосредственно перед сборкой stepf. Я подтвердил, что на этапе сборки extraLibs часть BuildInfo содержит имя библиотеки и extraLibDirs содержит правильный каталог.

Выход из моего cabal build является:

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. Других предупреждений и ошибок нет.

ответ

10

Чтобы решить эту проблему, я должен был:

  1. повторной ссылке в библиотеку Haskell с объектными файлами привязок C и
  2. использовать ghc-options тег в моем Cabal файл, чтобы убедиться, что они связаны в правильный порядок.

Все изменения приведены в тестовом проекте (http://github.com/deech/CPlusPlusBindings).

Ниже подробно описывается процесс создания нового архива, который включает как объекты C, так и Haskell, и это не просто. Сложность возникает, потому что нет способа (как и для Cabal 1.16.0.2) подключиться к компоновщике процесса сборки.

Установка флагов в файле Cabal является тривиальной, поэтому она не описывается здесь.

пересобирание The Haskell библиотека

  1. Установите тип сборки в custom, добавив:

    build-type: custom 
    

    в Кабал файл.

  2. Вставить настроить логику сборки, заменив метод main в Setup.hs с:

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

    Это говорит процесс сборки, что вместо того, чтобы идти с процессом сборки по умолчанию, определенной в simpleUserHooks он должен использовать функцию myBuildHook, которая определенных ниже. Аналогичным образом процесс очистки отменяется с помощью специальной функции myCleanHook.

  3. Определите крюк сборки. Этот крюк сборки запустит make в командной строке для создания частей C++ и C, а затем будет использовать объектные файлы C при создании связывания привязок Haskell.

    Мы начинаем myBuildHook:

    myBuildHook pkg_descr local_bld_info user_hooks bld_flags = do 
    

    сначала работает make без аргументов:

    rawSystemExit normal "make" [] 
    

    Затем добавьте расположение заголовочных файлов и библиотечных каталогов и саму библиотеку в PackageDescription записи и обновите LocalBuildInfo с новым описанием пакета:

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

    Перед тем, как buildHook выпустил код configureHook, он сохранил порядок компиляции в ключе compBuildOrder (порядок сборки компонентов) в записи LocalBuildInfo. Нам нужно изолировать здание библиотеки, чтобы мы отделили здание библиотеки и исполняемые части здания процесса сборки.

    Порядок сборки просто список, и мы знаем, что компонент сборки библиотеки, если это просто обычный CLibName конструктор типа так выделим те элементы из списка и обновить LocalBuildInfo запись только с ними:

    let (libs, nonlibs) = partition 
             (\c -> case c of 
               CLibName -> True 
               _ -> False) 
             (compBuildOrder new_local_bld_info) 
        lib_lbi = new_local_bld_info {compBuildOrder = libs} 
    
  4. Теперь бежит сборку по умолчанию крюка в с обновленными записями:

    buildHook simpleUserHooks new_pkg_descr lib_lbi user_hooks bld_flags 
    
  5. После того, как это сделано зданием архива было создано, но мы должны воссоздать его ВКЛЮЧАЕТ е объекты С, генерируемых командой make в шаге 1. Таким образом, мы захватываем некоторые настройки и список путей к файлам объекта C:

    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, который действует на каждый компоненте сборки , В этом случае, поскольку мы имеем дело только с частью библиотеки, есть только один компонент. Cabal обеспечивает getHaskellObjects для получения списка объектных файлов Haskell и createArLibArchive для создания архива, чтобы мы могли повторно запускать линкер:

    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() 
    
  6. по умолчанию buildHook, который был запущен в шаге 4 создается файл базы данных временного пакета с именем «package.conf.inplace», в котором содержится описание библиотеки, которая была построена так, чтобы исполняемый файл мог связать ее без библиотеки, которая должна быть установлена ​​в файл системного пакета по умолчанию.К сожалению, каждый buildHook пробег заготовки это так, мы должны держать на временную копию:

    let distPref = fromFlag (buildDistPref bld_flags) 
         dbFile = distPref </> "package.conf.inplace" 
    (tempFilePath, tempFileHandle) <- openTempFile distPref "package.conf" 
    hClose tempFileHandle 
    copyFile dbFile tempFilePath 
    
  7. Теперь мы храним путь к этой копии в LocalBuildInfo структуру вместе с исполняемыми частями процесса сборки, которые были отфильтрованы в шаге 3.

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

    и сохранить путь снова в extraTmpFiles части PackageDescription поэтому она может быть удалена путем очистить крюком по умолчанию.

    exe_pkg_descr = new_pkg_descr {extraTmpFiles = extraTmpFiles new_pkg_descr ++ [tempFilePath]} 
    
  8. Теперь мы, наконец, запустить по умолчанию buildHook снова с обновленными записями (которые теперь знают о новом архиве) на только исполняемых компонентов:

    buildHook simpleUserHooks exe_pkg_descr exe_lbi user_hooks bld_flags 
    

 Смежные вопросы

  • Нет связанных вопросов^_^