Including header file from static library
Asked Answered
C

1

23

I am making a test setup of a C static library and program. The library code, located in a subdirectory 'foo' of my project, contains the following files:

foo/foo.c:

#include <stdio.h>
void foo(void) {
    printf("something");
}

foo/foo.h:

#ifndef foo_h__
#define foo_h__
extern void foo(void);
#endif

My progam code is as follows:

test.c:

#include "foo.h"
int main() {
    foo();
    return 0;
}

I have a build script, called 'build', which contains the following:

build:

#!/bin/bash
gcc -c -Wall -Werror foo/foo.c
ar rcs libfoo.a foo.o
gcc -static -o test test.c libfoo.a # I have also tried -L. -lfoo

But when I run build, it gives me the following error:

test.c:1:17: fatal error: foo.h: No such file or directory
  #include "foo.h"
                  ^
Compilation terminated

It does, however, work when I omit the #include line, but I would prefer if I could use header files in my static libraries. What am I doing wrong, and how can I fix it?

Curch answered 26/12, 2014 at 18:43 Comment(0)
O
39

Headers are not stored in libraries. Headers are stored separately from libraries. Libraries contain object files; headers are not object files. By default, standard headers on a Unix system are stored in /usr/include — you'll normally find /usr/include/stdio.h and /usr/include/string.h and /usr/include/stdlib.h, for example. By default, libraries are stored in /usr/lib (but you may also find some in /lib). Often, compilers are configured to look in some other places too. One common alternative location is under /usr/local, so /usr/local/include for headers and /usr/local/lib for libraries. Note, too, that a single library may have many headers defining the services. The default library is an example. It has the functions corresponding to those found in <stdio.h>, <string.h>, <stdlib.h> and many other headers too.

Looking at your code:

  1. If your header file is in ./foo/foo.h, then you need to write:

    #include "foo/foo.h"
    

    Or if you continue to use #include "foo.h", you need to specify where to find the header on the compiler command line with the argument:

    gcc -Ifoo -o test test.c -L. -lfoo
    

    I deliberately excluded the -static; it's only necessary when there's a choice between a static and a shared library, but you only have libfoo.a, so the linker will use that anyway.

    Note that the problem is a compilation error, not a linking error. This would be clearer if you split the program building into two steps: (1) create test.o and (2) link program:

    gcc -c -Ifoo test.c
    gcc -o test test.o -L. -lfoo
    
  2. Your header guard is faulty. You originally had (but have updated the question so this typo is no longer present):

    #ifndef foo_h__
    #define foo_h_
    

    You need:

    #ifndef foo_h__
    #define foo_h__
    

    The macro names must be the same in both lines. Note that in this case, the misspelling is mostly harmless — but on Mac OS X, clang (masquerading as gcc) did give a warning about it (though I'd spotted it before I did any compilation). In some other cases, you wouldn't get the protection that the header guards are designed to provide.

    ./foo/foo.h:1:9: warning: 'foo_h__' is used as a header guard here, followed by #define of a
          different macro [-Wheader-guard]
    #ifndef foo_h__
            ^~~~~~~
    ./foo/foo.h:2:9: note: 'foo_h_' is defined here; did you mean 'foo_h__'?
    #define foo_h_
            ^~~~~~
            foo_h__
    1 warning generated.
    

You might legitimately wonder:

  • If I need -Ifoo when compiling test.c, why wasn't it necessary when compiling foo/foo.c?

Good question!

  1. It would not have hurt the compilation of foo/foo.c
  2. GCC looks for headers in the directory where the source code of the translation unit is found (so, when compiling foo/foo.c, it looks in foo directory for headers included as #include "foo.h" anyway.
  3. The source file foo/foo.c should have included foo.h too; it is very important that it does as that is how the compiler provides the cross-checking necessary to ensure consistency. If you had written #include "foo.h", the compilation would work as described. If you wrote (in foo/foo.c) #include "foo/foo.h", then the command line for creating foo.o would have needed -I. so the header could be found.
Overlooker answered 26/12, 2014 at 18:47 Comment(14)
But the header foo.h is part of the library libfoo.a, isn't it? Also, the header guard issue was a typo when I retyped it on stackoverflow, which I did because I am on windows SSHing into an ubuntu system.Curch
No. A library contains solely and only object files. Headers are not object files. Headers are not contained in libraries. (OK: to be utterly pedantically accurate, you could add a header file to a library, but the compiler would never look in a library — only the linker (ld, usually) looks in libraries, and it only looks for object files, not headers).Overlooker
Oh, ok. Thank you for clarifying that. But then why, for example, can I include, for example, "ncurses.h" from the ncurses library?Curch
You can write #include <ncurses.h> because the header is installed in a standard location, /usr/include, which the compiler searches anyway for headers. And you can write -lncurses because the library is in a standard location that the linker searches anyway, usually /usr/lib. Or it might be /usr/local/include and /usr/local/lib. But the headers are not stored in the library; they are stored independently of the library. You can have the libraries installed without the headers. You can't then compile code that uses the header, but you can run code that uses the shared library.Overlooker
What if I have h file a.h and my C file calls it using relative path: $include "../../SomeFolderA/SomeFolderB/a.h. Now the header file a.h includes an h file called b.h by using SomeFolderC\b.h. Will GCC parse it right or all paths are resolved relative to the C file path?Distended
@Royi: try to avoid that notation (see What are the benefits of a relative path such as ../include/header.h for a header? for more information. Note that one of the problems is that the interpretation is not necessarily consistent across compilers, though POSIX does define the behaviour more strictly, and GCC more or less adheres to that. AFAICR, GCC will first look for b.h in the directory where it found a.h, and thereafter will look in the directories indicates by where the source file is and the -I options and the default search path.Overlooker
POSIX says for the c99 command: -I directory — Change the algorithm for searching for headers whose names are not absolute pathnames to look in the directory named by the directory pathname before looking in the usual places. Thus, headers whose names are enclosed in double-quotes ("") shall be searched for first in the directory of the file with the #include line, then in directories named in -I options, and last in the usual places. […continued…]Overlooker
[…continuation…] _ For headers whose names are enclosed in angle brackets (<>), the header shall be searched for only in directories named in -I options and then in the usual places. Directories named in -I options shall be searched in the order specified. If the -I option is used to specify a directory that is one of the usual places searched by default, the results are unspecified. Implementations shall support at least ten instances of this option in a single c99 command invocation._Overlooker
The question is if a file uses #include "SomePathHere" will the path always be resolved relative to that file? On Windows it seems GCC sometimes has issues with it in case the path has many ../../...Distended
It depends. It depends on the compiler, which means it also depends on the platform. On the whole, avoid relative paths that include .. elements, and avoid absolute paths. Relative names such as sys/types.h are OK. Specify the directory via -I options if necessary.Overlooker
What does -I mean? What does -L. mean? What do I put for -l for linking a .so file?Celeriac
@AaronFranke: -I /some/directory tells the compiler to look for header files in the named directory before looking in the standard locations. The -L . option means look for libraries (either .a or .so) in the named directory (., the current directory) before looking in the standard locations. The -l name option looks for either the shared library libname.so or the static library libname.a in the directories specified by -L options and in the standard locations. If both libname.a and libname.so exist in the same directory, the linker chooses libname.so by default.Overlooker
So with -l, it auto-prepends lib and auto-appends .so? My library is called libgit2.so.1.0.1. If I put -l git2 wouldn't this look for libgit2.so and not libgit2.so.1.0.1? How do I specifically tell it to look for a file name?Celeriac
Yes, it would look for libgit2.so. You make it work by creating a symlink called libgit2.so that points to libgit2.so.1.0.1. If you install a ”development” package for a library, that's one of the things the package does. Look in /usr/local/lib and you should find .so files that are a symlink to versioned files.Overlooker

© 2022 - 2024 — McMap. All rights reserved.