我有以下 Haskell 源代码,我想从中编译一个可以链接到 C 程序的静态库:
{-# LANGUAGE ForeignFunctionInterface #-}
module Lib where
printHello :: IO ()
printHello = putStrLn "Hello world"
foreign export ccall printHello :: IO ()
我使用的命令是:
ghc -staticlib Lib.hs
但这会生成一个
main.exe
文件而不是 liba.a
(正如我根据 文档 所期望的那样)。
这不起作用;当我尝试将它与 C 源代码(使用 GHC 或普通 GCC)链接时,我得到未定义的引用。奇怪的是,它找到了 FFI 中预定义的函数(如
hs_init
和 hs_exit
);只有我导出的 (printHello
) 丢失了。
我尝试使用
nm
检查文件,结果得到 Lib.o: file format not recognized
(并且没有 printHello
符号)。
更有趣的是,如果我直接针对
Lib.o
文件 和 main.exe
文件(使用 GHC)进行编译,它似乎可以工作。所以
Lib.o
本身可能没有损坏。我也尝试用
ar
交换对象,但没有帮助。目标是生成一个静态库(带有
.a
或
.lib
扩展名的库),我可以使用 GHC 或 GCC 链接到 C 程序。为此,可能需要查看使用
nm
导出的符号。我已经阅读了标志、FFI、ar、nm、ld 的文档,但我仍然不知道如何处理这个问题。
如果这有帮助,我正在 Windows 10 1903 上使用 GHC 9.2.8。
提前非常感谢您:)
Lib.o
本身就是一个库,而不是一个简单的目标文件。所以,我用
ghc -staticlib -o liba.a Lib.hs
创建了存档。那么,在
ar
中,正确的命令是:
create libb.a
addlib liba.a
addlib Lib.o
save
end
一个虚拟的 C 程序,简单地调用 printHello
:
void hs_init(int* argc, char*** argv);
void hs_exit(void);
void printHello(void);
int main(int argc, char** argv) {
hs_init(&argc, &argv);
printHello();
hs_exit();
return 0;
}
尽管如此,出于某种原因,必须为 GCC 指定系统库。但在那之后,它起作用了:
gcc main.c libb.a -lpthread -lws2_32 -lntdll -lDbghelp -lOle32 -lRpcrt4 -lucrt -lgdi32 -lwinmm
因此,整个批处理脚本会成功:
ghc -staticlib -o liba.a Lib.hs
(echo "create libb.a" && echo "addlib liba.a" && echo "addlib Lib.o" && echo save && echo end) | ar -M
gcc main.c libb.a -lpthread -lws2_32 -lntdll -lDbghelp -lOle32 -lRpcrt4 -lucrt -lgdi32 -lwinmm