Aquachain
Aquachain

Reputation: 31

nim: Use a static library

I've tried to get an audio library statically linked to my program. I use this nimble package. To get it run, i had to build the soloud library as described here. For short after download i ran "genie --with-miniaudio-only --platform=x64 vs2017" in the "build" folder and got the source code to generate the dynamic and the static library. For now i can run the following demo program from the nimble package with the generated dll alongside:

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)

Now to the static-link part. In the solouddotnim.nim file of the nimble package i use, i see this part:

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

So i simple changed the windows part to the following, re-installed the nimble-package and placed the "soloud_static_x64.lib" alongside to the "main.nim" of the testproject:

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

But this doesent make it. (cant open "soloud_static_x64.lib" error when build) Evereywhere where the constant "libname" is used there are the pragmas "cdecl", "importc" and "dynlib". For example:

proc Soloud_create*(): ptr Soloud {.cdecl, importc: "Soloud_create", dynlib: libname.}

So "dynlib" is telling nim to use a dll on windows. But was is the pragma for static libraries? In the nim documentations i only found DynlibOverride to link to static libraries, but i dont understand the example and here is where i stuck. I've tried the followings:

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"

Firstly i dont know what parameter dynlibOverride expects and secondly both compiles, but dont work. It expects a dynamic library alongside the exe. My last try was to remove all dynlib pragmas from the nimble package. But now i cant compile it.

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

My knowlege ends here. Can someone help me? Thanks in advance.

Edit: I could not get any of your solutions work. I break down the problem as small as possible so everybody can reproduce this: "foo.nim" contains this:

proc add*(a, b: int): int {.cdecl, exportc.} = 
    a + b
proc sub*(a, b: int): int {.cdecl, exportc.} = 
    a - b

The .lib is simply generated with this command: "nim c --app:staticlib foo.nim"

Now to use it i created a file "main.nim" with this content:

{.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)

if i simply build it with "nim c -r main.nim", i get the following output and error:

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   '

Because of the multiple definition error i also tried to build foo.lib with parameter "--noMain:on", but it doesnt make any difference.

Do you have the same problem? By the way i use the current version of Nim "nim-1.5.1" and reinstalled MingW with the finish.exe from nim.

Upvotes: 3

Views: 3190

Answers (1)

Юра Векша
Юра Векша

Reputation: 66

I will try to help you with the following error you have:

undefined reference to `Soloud_create'

but i will assume that you have configured your environment so you can compile your nim programs with visual studio compiler (by adding --cc:vcc to your compile command) this is because you already seem to have visual studio 2017 and you are compiling soloud static library with it. I think this is the best option when you are compiling with one compiler both: static library and executable that will use it.

open your static library (soloud_static_x64.lib) with some text/hex editor and search for "Soloud_create". i guess you will not find anything. so why is that? because for some reason author decided to not include "C interfacing" in a static library project. so it contains only C++ symbols and not pure C symbols that are needed for our solouddotnim.nim module.

let's try to find out what .cpp file we need for that. i noticed this information on official web site of Soloud - http://sol.gfxile.net/soloud/c_api.html
so i guess we need only one file: soloud_c.cpp
let's try to just include it in SoloudStatic.vcxproj file generated by you with Genie. like this:

..
<ClCompile Include="..\..\src\c_api\soloud_c.cpp">
</ClCompile>
..

and recompile our static library. i use this command in 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

but you can compile how you want. just make sure that it's architecture is really x64. you can check it with this command:

dumpbin /headers soloud_static_x64.lib | more

finally just link it with your nim file. add this line to the top:

{.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"

Upvotes: 1

Related Questions