I asked an online LLM and it said:
Yes, absolutely. You need to link to the dependency library even when building a static library that calls functions within it.
But I tried building a static library that calls another function, and all I did was include the header, and then call the function. It built on Linux with GCC, without any complaints about undefined reference or anything.
Let's say:
MyStaticLibrary:
#include "MyExternalFunction.h"
void my_static_library_function()
{
MyExternalFunction();
}
The above compiled fine with no complaints from compiler.
Then I create an executable, main:
void my_static_library_function();
int main()
{
my_static_library_function();
return 0;
}
I linked main executable to my static library (obviously, because I call it's function) and THEN it complains that MyExternalFunction isn't defined, and so the EXECUTABLE has to link that function. But not the static library. And so therefore the static library doesn't have to link functions it calls. What about other symbols like variables? It's same? A static library never NEEDS to link anything, it only needs declarations?
What brought me to ask this is that I'm building a static library (libcurl) and I'm wondering whether that library only needs the include directory to MBedTLS (the ssl library backend) in order to compile.
The answer you got is broadly correct, but "building" is a vague term that doesn't capture that there are different steps to it.
A static library is just an indexed collection of object files. An object file is produced in the compilation step (I use it in the more strict sense here, not as a synonym to whole build though it's sometimes used this way). It's a dependency for further development, a limited collection of some definitions to link with.
You don't link a static library, it's produced just by making an archive. The linking step takes shared libraries, static libraries, object files, and sometimes specific stuff like linker scripts that you don't need to worry about here. It then produces an executable or shared library, which is a "final product" that'll be installed and used by the OS.
When the OS loads a shared library, it'll find references to some symbols inside and it needs to associate them with actual addresses in memory. A symbol can be either in that shared library itself, or already loaded from somewhere beforehand. That somewhere is usually one of its shared library dependencies (you can see them with readelf -d
on Linux) but you can also preload some shared libraries, which your shared library won't know anything about - this requires additional effort on your side as a user of that final product.
There's also dynamic linking where you put in your code that at some point during run time, you're going to load a library and pick out some symbols from it to use. The whole procedure described above is still performed, but you can delay it, even perform it lazily only for the symbols you actually use.
The point is that everything needs to be defined when it's going to be used, but you have a lot of flexibility in "promising you'll make sure to provide the required definitions".
For the object file (and therefore static library), you only need to specify what will come in from outside. Those are extern declarations. Function declarations are extern
by default, that's why all you need is to include a header file with int f(int x);
and it'll compile, but the symbol for this function will be undefined.
In order to link successfully, it's recommended to provide the definition of f
in one of the shared/static libraries given to the linker. However, if you know what you're doing, you can tell the linker to ignore undefined symbols and everything will be fine... until you need to run it.
Only at that point, when loading your library (let's ignore dynamic linking), you need to make sure that the symbol for f
was defined somewhere - either among the shared library dependencies that get found by the loader functionality of your OS (note: not simply the ones used during the linking step!) or in a library you explicitly provided to your process.
TL;DR for basic, nice software development: you need the declaration to compile and the definition to link.