Подтвердить что ты не робот

Статическая связь библиотеки C с библиотекой Haskell

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

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

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

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

Он содержит небольшой класс С++ (https://github.com/deech/CPlusPlusBindings/tree/master/cpp-src), обертка C (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. Других предупреждений и ошибок нет.

4b9b3361

Ответ 1

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

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

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

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

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

Перезагрузка Библиотека Haskell

  • Задайте тип сборки custom, добавив:

    build-type: custom
    

    в файл камеры.

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

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

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

  • Определите крюк сборки. Этот крюк сборки будет запускать make в командной строке для сборки частей С++ и 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}
    
  • Теперь мы запускаем сборку по умолчанию с обновленными записями:

    buildHook simpleUserHooks new_pkg_descr lib_lbi user_hooks bld_flags
    
  • После создания архива была создана, но мы должны заново создать его для включения объектов C, сгенерированных командой 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 ()
    
  • По умолчанию 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
    
  • Теперь мы сохраняем путь к этой копии в структуру 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]}
    
  • Теперь мы снова запускаем по умолчанию buildHook с обновленными записями (которые теперь знают о новом архиве) только для исполняемых компонентов:

    buildHook simpleUserHooks exe_pkg_descr exe_lbi user_hooks bld_flags