Hello! I am new to the nim community, so bear with me if I'm discussing something that's already been debated.
I've been getting into nim recently for game development and I just encountered an interesting issue. I started by bringing in nimGL to get started quicker with the openGL context creation and window management stuff, and that's been working great for me so far. But then I got to the part where I wanted to load in some images to create textures, and grabbed stb_image nim bindings because I've had a good experience with it in C. To my surprise, importing this library caused several linker errors:
C:\Users\chr\nimcache\game_d\read.c.o:read.c:(.text+0x1f8): multiple definition of `stbi_failure_reason' C:\Users\chr\nimcache\game_d\stb_image.c.o:stb_image.c:(.text+0x1f8): first defined here C:\Users\chr\nimcache\game_d\read.c.o:read.c:(.text+0x4e9): multiple definition of `stbi_image_free' C:\Users\chr\nimcache\game_d\stb_image.c.o:stb_image.c:(.text+0x4e9): first defined here C:\Users\chr\nimcache\game_d\read.c.o:read.c:(.text+0x505): multiple definition of `stbi_set_flip_vertically_on_load' C:\Users\chr\nimcache\game_d\stb_image.c.o:stb_image.c:(.text+0x505): first defined here ....
And so on. At first I was confused, then I found the --verbosity flag on the nim compiler and noticed this:
gcc.exe .... -DSTB_IMAGE_IMPLEMENTATION ..... C:\Users\chr\nimcache\game_d\game.c
On all the files in the build. I was like "huh, that's funny" and went and looked at the stb_image implementation, but it was using a #define STB_IMAGE_IMPLEMENTATION in a .c file to activate its implementation, not a compiler flag. So then I sat around confused for a minute and then grepped my entire user folder for it, and voila! Turns out nimGL is packaging stb_image with itself and passing this option via {.passC.}:
{.passC: "-DSTB_IMAGE_IMPLEMENTATION -I" & currentSourcePath().splitPath.head & "/../private/stb", compile: "../private/stb/stb_image.c"}
So the problem is simply that both libraries are defining the same symbols.
On the one hand, maybe one of these libraries is behaving badly, but both seem like they're making pretty reasonable decisions here. Furthermore, I'd rather not just not use the stb_image library, even though it's apparently superfluous, because its nim wrapper is more intuitive to use.
Am I wrong in thinking that this is indicative of a slightly larger problem? In particular, I believe this is an instance of the diamond dependency problem that crops up all over the place. In this case, both libraries depend on stb_image, and that causes redundant symbol definitions. But more generally, any two libraries which both depend on the same C library will cause this. I don't think stb_image is special because it's header-only, consider that two versions of some static library libfoo.a will export the same symbols as well.
Perhaps for a systems language like nim, we can declare that users of a library should be responsible for resolving this and exert maximum control over their code. But that feels unsatisfying to me, because the point of a language like nim is to strike a balance between control and convenience. Is there a nim-language- or nim-compiler-level solution to this? Has this already been brought up/explored? I'm curious what you all think of the matter.
Anyway, cheers from a potential future nim-lover,
-- chr
Nimx also uses it's own inlined version of stb_image, but it inlines it with #define STB_IMAGE_STATIC, so that C symbols remain hidden, and only nim symbols are visible and get their proper mangling. The most proper way for us nim users would likely be using a dedicated stb_image nim wrapper, which unfortunately has some issues too at this point :)
@Araq I don't think hashing C files will save the day, as (e.g.) different versions of stb_* can be used, so the hashes will differ, and you'll get the C symbols collision anyway.
@yglukhov Still better than nothing and one can always align the C file versions.
@CavariuX Edit compiler/extccomp.nim (External C compiler invokations).