Core of the Question: Header File vs Library Name
The core of the question was posted in the comments below the question itself:
For XListInputDevices, the appropriate include-line is #include <X11/extensions/XInput.h>
. I'm wondering how I can tell based on that what -lSomething flag I need
To answer the question appropriately we need to understand the distinction between -lXi
( or rather libXi.so
) and XInput.h
. The XInput.h
is a header file and -l
looks for object files. According to Red Hat documentation on gcc usage, section 16.1,
A special file name convention is used for libraries: A library known as foo is expected to exist as file libfoo.so or libfoo.a. This convention is automatically understood by the linking input options of GCC, but not by the output options
Both headers and libraries are needed and both are related, but not the same(ref):
... if you do #include the header the compiler does not insert the code for the function you call into program.o. It merely inserts a reference to them.
As mentioned, the headers and linkers are somewhat different, since headers store declarations (or in layman terms - descriptions) of functions and other stuff, and libraries - store actual objects (reference) or compiled version of those functions. As this answer puts it:
- The header is a phone number you can call, while...
- ...the library is the actual person you can reach there!
How do the headers and libraries play together ? They are both necessary steps in building an application. First, the description of functions and interfaces is gathered ( aka compiled ) and then the code you write is linked to a library where the actual interfaces and functions are implemented.
The library itself should be compiled with the header included (ref) into the library. In fact you can take a look at an example of how simple static library is created.
So the answer to your question, is as follows: there is no guaranteed relationship between the header and the library name. ( or at least I haven't found any so far ; see also this ref) . And, finding out how to match them is a matter of knowing what's in the package or in the source code.
In fact you can do this to prove the point:
$ echo '#include<errno.h> int main(){return 0;}' | gcc -S -lc
which shows that we're compiling against libc, but the header that is part of libc is different in name.
Another example to prove the point is #include <math.h>
, which is part of libc
but actual implementation is in libm.so.6
, so you will see code compiled with gcc -lm
.
$ apt-file find /lib/x86_64-linux-gnu/libm.so.6
libc6: /lib/x86_64-linux-gnu/libm.so.6
Finding the related .so or .a library file
A lot of mysteries can be solved with documentation. Let's refer to gcc
documentation for link options (note , emphasis in bold is mine):
-llibrary
-l library
Search the library named library when linking. (The second alternative with the library as a separate argument is only for POSIX
compliance and is not recommended.)
The -l option is passed directly to the linker by GCC. Refer to your
linker documentation for exact details. The general description below
applies to the GNU linker.
OK, so the gcc
itself isn't actually looking up the library. It is the linker's job. But let's read on.
The linker searches a standard list of directories for the library.
The directories searched include several standard system directories
plus any that you specify with -L.
So there is a standard list of directories. Great, this narrows down. How do we get that list ? One way among many is to use ldconfig -v
which was generously explained by the user telotorium
But the gcc
documentation goes further to explain:
Static libraries are archives of object files, and have file names
like liblibrary.a. Some targets also support shared libraries, which
typically have names like liblibrary.so. If both static and shared
libraries are found, the linker gives preference to linking with the
shared library unless the -static option is used.
It makes a difference where in the command you write this option; the
linker searches and processes libraries and object files in the order
they are specified. Thus, ‘foo.o -lz bar.o’ searches library ‘z’ after
file foo.o but before bar.o. If bar.o refers to functions in ‘z’,
those functions may not be loaded.
In other words, what you're really instructing gcc
and subsequently ld
to do, is to look for a file libxi.so
(so being a shared object extension) or libxi.a
in the list of standard paths. If say we store something in /opt
and the ld
linker doesn't know about it, passing anything to -l
won't work.
The accepted answer seems to hint at this, by virtue of most debian package names for libraries corresponding to that libwhatever.so
name. But I cannot confirm or deny that bit, as I know enough about debian packaging for my humble needs only, and not these minute details. What I do know is that apt may trigger ld
cache to be rebuilt.
Now, what is also important to realize is that ld
actually has all these directories in cache /etc/ld.so.cache
. So if the directory is removed from cache, linker won't know of library's existence.
But of course if we want to test all the above theory based on the example of libxi-devel
, we can do this:
$ dpkg-query -L libxi-dev | grep -i '.so'
/usr/share/man/man3/XIDefineCursor.3.gz
/usr/lib/x86_64-linux-gnu/libXi.so
/usr/share/man/man3/XIUndefineCursor.3.gz
and then check ldconfig
to confirm that /usr/lib/x86_64-linux-gnu
is on the list of searched directories . . . which it is:
$ ldconfig -p | grep libXi.so
libXi.so.6 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libXi.so.6
libXi.so.6 (libc6) => /usr/lib/i386-linux-gnu/libXi.so.6
libXi.so (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libXi.so
Now this also raises a question, is the .h
file useless ? No, apparently gcc
still needs it to exist at compilation time ( because gcc
does check the include-paths and because some libraries can be header-only )
$ mv /usr/include/X11/extensions/XInput.h /tmp
$ echo '#include <X11/extensions/XInput.h> void main(){return 0;}' | gcc -lXi -E -
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "<stdin>"
<stdin>:1:36: warning: extra tokens at end of #include directive
<stdin>:1:10: fatal error: X11/extensions/XInput.h: No such file or directory
compilation terminated.
XListInputDevices
used in the example compiler error here. – Anko Apr 20 '16 at 16:04man
, as mentioned. ForXListInputDevices
, the appropriate include-line is#include <X11/extensions/XInput.h>
. I'm wondering how I can tell based on that what-lSomething
flag I need. – Anko Apr 20 '16 at 16:12dpkg -S XInput.h -> libxi-dev: /usr/include/X11/extensions/XInput.h
. Ordlocate
would work too. So, I'd first try-lxi
. But I'd personally just check the headers in the preprocessed source file, and run those throughdlocate
. But again, an actual example would help. – Faheem Mitha Apr 20 '16 at 16:18dpkg
anddlocate
are Debian-specific, but that sounds like a decent method for Debian users. Add it? (I use Arch.) – Anko Apr 20 '16 at 16:30pkg-config/pkgconf
. For example to find the linker flags for something likexinerama
, you can dopkgconf --cflags --libs xinerama
and the output would be-lXinerama
. Try it for other commands as well – smac89 Jan 05 '21 at 05:47pkg-config
is very handy! It requires knowing what keyword to give it though, which a source file may not directly indicate. For example,#include <X11/extensions/XInput.h>
requirespkg-config --cflags --libs xi
which isn't obvious from the former. (Grepping throughpkg-config --list-all
can help though.) – Anko Jan 05 '21 at 12:12