У меня есть проект на Haskell, который направлен на создание некоторых привязок C ++. Я написал оболочки C и собрал их в автономную статически связанную библиотеку.
Я хотел бы написать привязки Haskell для статической связи с оболочками C, чтобы мне не приходилось раздельно распространять оболочки C, но я не могу заставить ее работать, и был бы признателен за некоторую помощь.
Я указываю библиотеку C как дополнительную библиотеку, но мой cabal build
Шаг не добавляет его в команду компиляции.
Я создал небольшой проект, чтобы проиллюстрировать это (http://github.com/deech/CPlusPlusBindings).
Содержит небольшой класс C ++ (https://github.com/deech/CPlusPlusBindings/tree/master/cpp-src), обертка C (https://github.com/deech/CPlusPlusBindings/tree/master/c-src), рабочая программа тестирования С (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 непосредственно перед этапом сборки. Я проверил, что на этапе сборки 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. Других предупреждений или ошибок нет.
Для решения этой проблемы мне пришлось:
ghc-options
тег в моем интрига файл, чтобы убедиться, что они связаны в правильном порядке.Все изменения в тестовом проекте (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, а затем использовать объектные файлы 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
Других решений пока нет …