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?

Ethan McTague
  • 2,236
  • 3
  • 21
  • 53

1 Answers1

38

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.
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 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. – Ethan McTague Dec 26 '14 at 18:57
  • 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). – Jonathan Leffler Dec 26 '14 at 18:59
  • Oh, ok. Thank you for clarifying that. But then why, for example, can I include, for example, "ncurses.h" from the ncurses library? – Ethan McTague Dec 26 '14 at 19:00
  • You can write `#include ` 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. – Jonathan Leffler Dec 26 '14 at 19:03
  • 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? – Royi Oct 11 '17 at 21:43
  • @Royi: try to avoid that notation (see [What are the benefits of a relative path such as `../include/header.h` for a header?](https://stackoverflow.com/questions/597318/) 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. – Jonathan Leffler Oct 12 '17 at 00:43
  • POSIX says for the [`c99`](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/c99.html) 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…] – Jonathan Leffler Oct 12 '17 at 00:48
  • […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._ – Jonathan Leffler Oct 12 '17 at 00:48
  • 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 `../../..`. – Royi Oct 12 '17 at 09:23
  • 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. – Jonathan Leffler Oct 12 '17 at 09:31
  • What does `-I` mean? What does `-L.` mean? What do I put for `-l` for linking a `.so` file? – Aaron Franke Aug 06 '20 at 22:54
  • @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. – Jonathan Leffler Aug 07 '20 at 03:20
  • 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? – Aaron Franke Aug 09 '20 at 04:39
  • 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. – Jonathan Leffler Aug 09 '20 at 12:33