我在Haskell中编写了一些函数,我想将它们构建成一个DLL,这样我就可以从我的C ++应用程序中调用它们了。我有一段可怕的时间。 (似乎从Haskell调用C DLL更常见,更好地记录。)
我的Haskell设置是MinGHC 7.10.1(32位)。 C ++项目使用Visual Studio 2010和Qt 5.4;我使用qmake从.pro文件生成VS项目文件。这就是我正在做的事情:
ghc -no-hs-main -v SpectrumMath.hs > ghc_output 2>&1
将GHC的详细构建输出转储到文件中。ghc_output
文件并找到链接器命令(它是“*** Linker:”之后的行)。提取库目录(由-L
标志指示)和库名称(由-l
标志指示)。将这些库添加到Qt .pro文件中,例如
LIBS += \
-Lsome_long_path_here \
-Lsome_other_long_path_here \
-lsome_library_name_here \
-lsome_other_library_name_here
运行ghc --print-libdir
。将/include
附加到此路径的末尾并将其添加到.pro文件中:
INCLUDEPATH += "C:/Program Files (x86)/MinGHC-7.10.1/ghc-7.10.1/lib/include"
运行qmake以生成Visual Studio项目。
这个疯狂的过程来自维基上的this page。它假定C ++文件将#include "SpectrumMath_stub.h"
,这是GHC在步骤2中生成的文件。
我在这一点上陷入困境,因为我不知道如何处理m库(来自-lm
标志的那个)。 Visual Studio找不到“m.lib”,但如果我从列表中删除该库,我会收到许多“未解析的外部”消息,因此需要库。如何告诉Visual Studio libm依赖?或者我应该完全遵循另一个过程吗?
答案 0 :(得分:3)
我建议实际使用cabal-install
(或stack
)。如果您计划在没有LPGL兼容许可的情况下分发此DLL,您可能希望使用integer-simple
而不是integer-gmp
(默认值)使用GHC版本。
首先,熟悉this GHC tutorial。
阅读完之后,您会发现某处需要StartEnd.c
的副本。让我们说它在src\
。
所以这是你的MyLibrary.cabal
文件:
...other cabal stuff...
library
exposed-modules: MyModule
--other-modules:
other-extensions: ForeignFunctionInterface
build-depends: base
hs-source-dirs: src
c-sources: src/StartEnd.c --tells cabal to rebuild if this changes
default-language: Haskell2010
--default-extensions:
-- Add whatever options you need, in addition to these:
-- (In my experience, adding `-threaded` can make the DLL
-- behave better during debugging; but that's only a guess.)
ghc-options: -O1 -shared src/StartEnd.c
现在使用cabal build
构建它(如果您使用stack build
,则为stack
。)
期待看到警告。 Cabal并不完全了解你的目标。
相对于您的阴谋项目,您会发现三个对您很重要的文件:
HSdll.dll
HSdll.dll.a
(Windows通常会调用这些.lib
个文件;如果您愿意,可以重命名该文件。build\...\MyLibrary_stub.h
请注意,此DLL静态链接所有Haskell依赖项。因此,除了DLL之外,您无需告诉Visual Studio任何内容。
MyLibrary_stub.h
文件。您还需要在某处包含HsStart
和HsEnd
的原型:
extern "C" {
void HsStart();
void HsEnd();
}
_stub.h
文件包含一些GHC标头,因此您需要在VS项目的“其他包含”中添加一个文件夹:<ghc-folder>\ghc-7.10.1\lib\include
。
where ghc
,删除ghc.exe
,然后将\..\lib\include
附加到该结果中。在您的C ++应用中,请务必在程序的开头和结尾调用HsStart()
和HsEnd()
。我喜欢将它们封装在RAII结构中,所以我不能忘记做正确的事。
我已尝试使用cabal-install
1.2 *版本和stack
。我使用了GHC 7.8.4和7.10.1,每个都有32位和64位版本。
请注意,Cabal
即将推出much better support for this。
在Cabal获得更好的支持之前,我发现为您的DLL命名的唯一方法是将-o MyLibrary.dll
添加到ghc-options
。即使它有效,GHC会在你这样做时给你警告,表明它并不真正知道发生了什么。要提供帮助,您可以将StartEnd.c
预先构建到StartEnd.o
并将其链接起来。要使用的命令是这样的:
cabal exec -- ghc -optc-O -c src/StartEnd.c -o obj/StartEnd.o
现在,在ghc-options
中,将src/StartEnd.c
替换为obj/StartEnd.o
。
如果您使用cabal
,则stack
替换stack
也应该有效。