Nim:如何使用静态库

3

我尝试将音频库静态链接到我的程序中,我使用这个 nimble 包。为了运行它,我需要按照这里所述的方法构建 soloud 库。简而言之,在下载后,我在“build”文件夹中运行了“genie --with-miniaudio-only --platform=x64 vs2017”,并获得了生成动态库和静态库的源代码。目前,我可以使用生成的 dll 和 nimble 包中提供的以下演示程序:

import solouddotnim, times, os

var i, spin = 0

var sl : ptr Soloud

sl = Soloud_create()

discard Soloud_init(sl)

Soloud_setGlobalVolume(sl, 1)


var stream = WavStream_create()
discard WavStream_load(cast[ptr Wav](stream), "test.ogg")

let currentTime = epochTime()
let length = WavStream_getLength(stream)
discard Soloud_play(cast[ptr Soloud](sl), cast[ptr Wav](stream))

while epochTime() - currentTime <= length:
  sleep(100)

Soloud_deinit(sl)

Soloud_destroy(sl)

现在讲静态链接的部分。在我使用的nimble包的solouddotnim.nim文件中,我看到了这个部分:

when defined(windows):
  const
    libname* = "libsoloud.dll"
elif ...

所以我将 Windows 部分简单更改为以下内容,重新安装了 Nimble 包,并将 "soloud_static_x64.lib" 放置在测试项目的 "main.nim" 旁边:

when defined(windows):
  const
    libname* = "soloud_static_x64.lib"
elif ...

但这并不能解决问题。(在构建时出现“无法打开‘soloud_static_x64.lib’”错误) 每当常量“libname”被使用时,都会有“cdecl”、“importc”和“dynlib”指令。例如:
proc Soloud_create*(): ptr Soloud {.cdecl, importc: "Soloud_create", dynlib: libname.}

因此,“dynlib”告诉Nim在Windows上使用DLL。但是,静态库的编译指示符是什么? 在Nim文档中,我只找到了DynlibOverride用于链接静态库,但我不理解示例,这就是我卡住的地方。我尝试了以下方法:
nim c --dynlibOverride:libname --passL:soloud_static_x64.lib "examples\00-ogg\Example00_ogg.nim"

nim c --dynlibOverride:soloudtotnim --passL:soloud_static_x64.lib "examples\00-ogg\Example00_ogg.nim"

首先,我不知道参数 dynlibOverride 需要什么,其次,两者都编译通过,但并未工作。它期望在exe文件旁边有一个动态库。
我的最后一次尝试是从nimble包中删除所有dynlib pragmas,但现在我无法编译它。

undefined reference to `Soloud_create'
...
Error: execution of an external program failed: 'gcc.exe...

我的知识到此为止了。有人能帮助我吗? 提前感谢。
编辑: 我无法让你们的任何解决方案起作用。我将问题简化,以便每个人都可以重现它: "foo.nim" 包含以下内容:
proc add*(a, b: int): int {.cdecl, exportc.} = 
    a + b
proc sub*(a, b: int): int {.cdecl, exportc.} = 
    a - b

.lib 文件可以通过执行以下命令来生成:"nim c --app:staticlib foo.nim"。
现在,为了使用它,我创建了一个名为 "main.nim" 的文件,并将其内容设置为:
{.passL:"foo.lib".}
proc add*(a, b: int):int {.cdecl, importc.}
proc sub*(a, b: int):int {.cdecl, importc.}

echo add(10, 5)
echo sub(10, 5)

如果我只是用"nim c -r main.nim"来构建它,我会得到以下输出和错误:

P:\Nim\LearnCBinding>nim c -r main.nim
Hint: used config file 'C:\nim-1.5.1\config\nim.cfg' [Conf]    
Hint: used config file 'C:\nim-1.5.1\config\config.nims' [Conf]
....CC: stdlib_io.nim
CC: stdlib_system.nim
CC: main.nim

Hint:  [Link]
foo.lib(@mfoo.nim.c.o):@mfoo.nim.c:(.text+0x1f6): multiple definition of `PreMainInner'     
C:\Users\Peter\nimcache\main_d\@mmain.nim.c.o:@mmain.nim.c:(.text+0x120): first defined here
foo.lib(@mfoo.nim.c.o):@mfoo.nim.c:(.text+0x20a): multiple definition of `PreMain'
C:\Users\Peter\nimcache\main_d\@mmain.nim.c.o:@mmain.nim.c:(.text+0x134): first defined here
foo.lib(@mfoo.nim.c.o):@mfoo.nim.c:(.text+0x240): multiple definition of `NimMainInner'     
C:\Users\Peter\nimcache\main_d\@mmain.nim.c.o:@mmain.nim.c:(.text+0x16f): first defined here
foo.lib(@mfoo.nim.c.o):@mfoo.nim.c:(.text+0x254): multiple definition of `NimMain'
C:\Users\Peter\nimcache\main_d\@mmain.nim.c.o:@mmain.nim.c:(.text+0x183): first defined here
foo.lib(@mfoo.nim.c.o):@mfoo.nim.c:(.text+0x285): multiple definition of `main'
C:\Users\Peter\nimcache\main_d\@mmain.nim.c.o:@mmain.nim.c:(.text+0x1b4): first defined here
foo.lib(@mfoo.nim.c.o):@mfoo.nim.c:(.text+0x2da): multiple definition of `NimMainModule'
C:\Users\Peter\nimcache\main_d\@mmain.nim.c.o:@mmain.nim.c:(.text+0x209): first defined here
collect2.exe: error: ld returned 1 exit status
Error: execution of an external program failed: 'C:\nim-1.5.1\dist\mingw64\bin\gcc.exe   -o P:\Nim\LearnCBinding\main.exe  C:\Users\Peter\nimcache\main_d\stdlib_io.nim.c.o C:\Users\Peter\nimcache\main_d\stdlib_system.nim.c.o C:\Users\Peter\nimcache\main_d\@mmain.nim.c.o  foo.lib   '

由于出现了多个定义错误,我也尝试使用参数"--noMain:on"构建foo.lib,但没有任何变化。

你有同样的问题吗?顺便说一下,我使用的是当前版本的Nim "nim-1.5.1",并重新安装了MingW(使用来自nim的finish.exe)。


1
使用passC来传递选项给C编译器,使用passL来传递选项给链接器。有clib来链接C库。你可以使用它来几乎完全像在C中链接库一样。 - Clonk
如果你还没有找到它,它在手册中有记录 ;) https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-passc-pragma. - Clonk
1
删除所有的{.dynlib.},保留{.importc.}并添加一个单独的{.passL.}来链接库。 - genotrance
谢谢您的建议,但我无法让它工作。请查看我的编辑以获取一个简单的示例。 - Aquachain
1
目前看起来像是一个bug。我在这里报告了它:https://forum.nim-lang.org/t/7080。在得到这些回复后,我在这里开了一个漏洞报告:https://github.com/nim-lang/Nim/issues/15955。 - Aquachain
显示剩余2条评论
1个回答

1
我会尝试帮助您解决以下错误:
undefined reference to `Soloud_create'

我假设你已经配置好了环境,这样你就可以使用Visual Studio编译Nim程序(通过将--cc:vcc添加到编译命令中)。这是因为你似乎已经有了Visual Studio 2017,并且正在使用它来编译Soloud静态库。我认为这是最好的选择,当你用一个编译器同时编译静态库和可执行文件时。
打开你的静态库(soloud_static_x64.lib)并使用一些文本/十六进制编辑器搜索"Soloud_create"。我猜你找不到任何东西。那么为什么呢?因为由于某种原因作者决定在静态库项目中不包括"C接口"。因此它只包含C++符号而不是我们solouddotnim.nim模块所需的纯C符号。
让我们尝试找出我们需要的.cpp文件。我在Soloud的官方网站上发现了这个信息 - http://sol.gfxile.net/soloud/c_api.html
所以我猜我们只需要一个文件:soloud_c.cpp
让我们尝试将其包含在你用Genie生成的SoloudStatic.vcxproj文件中。就像这样:
..
<ClCompile Include="..\..\src\c_api\soloud_c.cpp">
</ClCompile>
..

我需要重新编译我们的静态库。在PowerShell中,我使用以下命令:

& 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Preview\MSBuild\Current\Bin\MSBuild.exe' /p:PlatformToolset=v142`;WindowsTargetPlatformVersion=10`;Configuration=Release`;Platform=x64 .\SoloudStatic.vcxproj

但是你可以按照自己的方式编译。只需确保其架构真正为x64。你可以使用以下命令进行检查:

dumpbin /headers soloud_static_x64.lib | more

最后将它与您的Nim文件链接起来。将以下行添加到顶部:

{.link:"soloud_static_x64.lib".}

and compile nim file with this command:

nim c --cc:vcc --dynlibOverride:libsoloud.dll -r "examples\00-ogg\Example00_ogg.nim"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接