Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Header Files in Multiple Directories: Best Practices

I'm a C Newb

I write lots of code in dynamic languages (javascript, python, haskell, etc.), but I'm now learning C for graduate school and I have no idea what I'm doing.

The Problem

Originally I was building all my source in one directory using a makefile, which has worked rather well. However, my project is growing and I would like to split the source into multiple directories (unit tests, utils, core, etc.). For example, my directory tree might look like the following:

.
|-- src
|   |-- foo.c
|   |-- foo.h
|   `-- main.c
`-- test
    `-- test_foo.c

test/test_foo.c uses both src/foo.c and src/foo.h. Using makefiles, what is the best/standard way to build this? Preferably, there would be one rule for building the project and one for building the tests.

Note

I know that there are other ways of doing this, including autoconf and other automatic solutions. However, I would like to understand what is happening and be able to write the makefiles from scratch despite its possible impracticality.

Any guidance or tips would be appreciated. Thanks!

[Edit]

So the three solutions given so far are as follows:

  • Place globally used header files in a parallel include directory
  • use the path in the #include satement as in #include "../src/foo.h"
  • use the -I switch to inform the compiler of include locations

So far I like the -I switch solution because it doesn't involve changing source code when directory structure changes.

like image 642
brad Avatar asked Feb 28 '09 15:02

brad


2 Answers

For test_foo.c you simply need to tell the compiler where the header files can be found. E.g.

gcc -I../src -c test_foo.c

Then the compiler will also look into this directory to find the header files. In test_foo.c you write then:

#include "foo.h"

EDIT: To link against foo.c, actually against foo.o, you need to mention it in the object file list. I assume you have already the object files, then do after that:

gcc test_foo.o ../src/foo.o -o test
like image 59
quinmars Avatar answered Sep 20 '22 01:09

quinmars


I also rarely use the GNU autotools. Instead, I'll put a single hand-crafted makefile in the root directory.

To get all headers in the source directory, use something like this:

get_headers = $(wildcard $(1)/*.h)
headers := $(call get_headers,src)

Then, you can use the following to make the object-files in the test directory depend on these headers:

test/%.o : test/%.c $(headers)
    gcc -std=c99 -pedantic -Wall -Wextra -Werror $(flags) -Isrc -g -c -o $@ $<

As you can see, I'm no fan of built-in directives. Also note the -I switch.

Getting a list of object-files for a directory is slightly more complicated:

get_objects = $(patsubst %.c,%.o,$(wildcard $(1)/*.c))
test_objects = $(call get_objects,test)

The following rule would make the objects for your tests:

test : $(test_objects)

The test rule shouldn't just make the object files, but the executables. How to write the rule depends on the structure of your tests: Eg you could create an executable for each .c file or just a single one which tests all.

like image 35
Christoph Avatar answered Sep 19 '22 01:09

Christoph