6

I know it is possible to specify #include filepaths either relative to the directory the file is located in, as an absolute file path, or relative to any of the directories in the $PATH system variable. Is there a way to instead specify it relative to the user's current directory when the program is compiled? Let's say I have the following file structure:

|--dir_a/
|  |--a.c
|  |--a.h
|--dir_b/
|  |--b.c
|  |--b.h
|--makefile

Now let's say I want to #include the file dir_a/a.h from dir_b/b.h. Using the location of dir_b/b.h, this can be written like this:

#include ../dir_a/a.h

However, this approach has a major flaw in my opinion since it hardcodes the locations of files relative to each other, meaning that relocating a file would require updating the file path everywhere that file was included from.

Using absolute file paths would avoid this problem, but would instead hardcode the location of the project within the filesystem, which seems like bad practice.

Finally, using the <> tags to specify the file path isn't feasible either since I can't assume the project will be listed in the $PATH variable.

So what I want to do is to be able to specify the paths relative to where the user compiles from (or even better, from the location of the makefile). In the above example, this would let me use the following statement to #include dir_a/a.h from dir_b/b.h:

#include dir_a/a.h

This I think would be the ideal solution. It would make the #include statements more consistent and easier to follow, as well as avoid the drawbacks I listed above. Is it possible to do this in any way, eg. with a compiler flag or something? I'm using gcc as my compiler.

PandaConda
  • 3,396
  • 2
  • 20
  • 22

3 Answers3

5

If you consistently use <> includes, then the -I options in the makefile should be enough. The directory layout shows only one makefile, in the parent directory. That could use

-Idir_a -Idir_b

in the compiler options, and the .c files could just do

#include <a.h>
#include <b.h>

One of the problems with quoted includes is that their behavior with other compilers may differ, as noted in What is the difference between #include <filename> and #include “filename”? (the standard was not explicit enough). Using a gcc extension probably does not improve that situation.

Community
  • 1
  • 1
Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
  • Good point about cross-compiler compatibility. I hadn't considered that. At the same time, the compiler (`gcc` in this case) is already hardcoded into the makefile. This approach also goes against the standard practice of using `<>` for system libraries and `""` for header files local to the program, which is mentioned in the top answer to the question you linked. Finally, using the `-I` flag on every directory could potentially lead to name conflicts in a situation with files where there are identically namedfiles in different directories. – PandaConda Apr 12 '15 at 18:04
  • The OP's example does not have the problem you suggest. I stopped using quoted includes quite a while ago, because gcc differs from most other compilers in this area. – Thomas Dickey Apr 12 '15 at 18:13
  • Ok, thought quoted includes were part of the C standard, but I guess not. How are naming conflicts caused by identically named files in different directories resolved using this answer though? – PandaConda Apr 12 '15 at 18:32
  • They are - but whether a quoted include will get a file in the directory containing the file which includes it (without a compiler option) is *not*. Bracketed includes are more predictable. Conflicts in any case are resolved by the order of `-I` options. It is easiest to see the difference when considering nested includes - gcc looks first in the including file's directory, while other compilers may not. Likely someone's written a page about it. – Thomas Dickey Apr 12 '15 at 18:34
  • 1
    For reference - see xterm [patch #85](http://invisible-island.net/xterm/xterm.log.html#xterm_85) (1998), where quoted includes were changed to bracketed includes. – Thomas Dickey Apr 12 '15 at 18:41
  • Nice find. Now how do you specify the project's root directory using the `-I` flag? The examples given just specify the subdirectories. – PandaConda Apr 12 '15 at 18:47
  • The usual way is `-I.` (occasionally I use full paths with `-I` options, but usually in that case they are generated by a build-time script). – Thomas Dickey Apr 12 '15 at 18:55
1

I managed to solve my problem.

The first part of the solution involves specifying the -iquote flag in gcc when compiling. From man gcc:

-iquotedir
    Add the directory dir to the head of the list of directories to be searched for header files only for the case of #include "file"; they are not searched for #include <file>, otherwise just like -I.

The second part of the puzzle was how to get the path to the makefile within the makefile itself. This answer worked for me. I'm pasting the solution here for convenience:

ROOT_DIR = $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))

edit: While this approach works, this answer is more cross-compiler friendly, so I'm personally going to use that.

Community
  • 1
  • 1
PandaConda
  • 3,396
  • 2
  • 20
  • 22
0

Yes. Any include file, which is not directly in your include path specified in your project linker settings, should have all subfolders up to it specified, like:

#include "first/second/third/folder/library.h"
Michał Szydłowski
  • 3,261
  • 5
  • 33
  • 55