CFLAGS vs CPPFLAGS
Asked Answered
Q

6

126

I understand that CFLAGS (or CXXFLAGS for C++) are for the compiler, whereas CPPFLAGS is used by the preprocessor.

But I still don't understand the difference.

I need to specify an include path for a header file that is included with #include -- because #include is a preprocessor directive, is the preprocessor (CPPFLAGS) the only thing I care about?

Under what circumstances do I need to give the compiler an extra include path?

In general, if the preprocessor finds and includes needed header files, why does it ever need to be told about extra include directories? What use is CFLAGS at all?

(In my case, I actually found that BOTH of these allow me to compile my program, which adds to the confusion... I can use CFLAGS OR CPPFLAGS to accomplish my goal (in autoconf context at least). What gives?)

Quadrifid answered 2/5, 2010 at 21:2 Comment(1)
possible duplicate of #496098Ribbing
R
179

The implicit make rule for compiling a C program is

%.o:%.c
    $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

where the $() syntax expands the variables. As both CPPFLAGS and CFLAGS are used in the compiler call, which you use to define include paths is a matter of personal taste. For instance if foo.c is a file in the current directory

make foo.o CPPFLAGS="-I/usr/include"
make foo.o CFLAGS="-I/usr/include"

will both call your compiler in exactly the same way, namely

gcc -I/usr/include -c -o foo.o foo.c

The difference between the two comes into play when you have multiple languages which need the same include path, for instance if you have bar.cpp then try

make bar.o CPPFLAGS="-I/usr/include"
make bar.o CFLAGS="-I/usr/include"

then the compilations will be

g++ -I/usr/include -c -o bar.o bar.cpp
g++ -c -o bar.o bar.cpp

as the C++ implicit rule also uses the CPPFLAGS variable.

This difference gives you a good guide for which to use - if you want the flag to be used for all languages put it in CPPFLAGS, if it's for a specific language put it in CFLAGS, CXXFLAGS etc. Examples of the latter type include standard compliance or warning flags - you wouldn't want to pass -std=c99 to your C++ compiler!

You might then end up with something like this in your makefile

CPPFLAGS=-I/usr/include
CFLAGS=-std=c99
CXXFLAGS=-Weffc++
Ronen answered 3/5, 2010 at 7:29 Comment(2)
Note that you cannot run stand-alone cpp using CPPFLAGS and expect any reasonable result because -std=c99 affects which symbols are defined (especially in lieu of feature test macros). Instead, you need $(CC) $(CPPFLAGS) $(CFLAGS) -E.Mascagni
"The implicit make rule for compiling a C program is" "$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<" Not sure if it makes a difference, but on gmake, currently CPPFLAGS appears after CFLAGS: $ make --version | head -n 1 -> GNU Make 4.3; $ make -p 2>/dev/null | grep -F 'COMPILE.c =' -> COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -cModestomodesty
B
14

The CPPFLAGS macro is the one to use to specify #include directories.

Both CPPFLAGS and CFLAGS work in your case because the make(1) rule combines both preprocessing and compiling in one command (so both macros are used in the command).

You don't need to specify . as an include-directory if you use the form #include "...". You also don't need to specify the standard compiler include directory. You do need to specify all other include-directories.

Bergmans answered 2/5, 2010 at 21:45 Comment(2)
This makes more sense, but I still don't see what CFLAGS does, then. If, as you seem to imply, compilation in more complex projects is done in a separate step from preprocessing, will preprocessing succeed but compilation fail if CFLAGS doesn't add the same paths that CPPFLAGS added for the preprocessor? I guess I don't understand what the compiler does with include paths if the preprocessor already processed the #include directives?Quadrifid
@EB If you're compiling preprocessed files then include paths are unnecessary - the required headers have already been added to the preprocessed source (Have a look at the output when you run gcc -E on a file - there are no #includes). Most modern compilers combine the pre-processing and compilation steps, so you don't have to worry about this.Ronen
E
3

You are after implicit make rules.

Essentialism answered 2/5, 2010 at 21:7 Comment(0)
C
1

To add to those who have mentioned the implicit rules, it's best to see what make has defined implicitly and for your env using:

make -p

For instance:

%.o: %.c
    $(COMPILE.c) $(OUTPUT_OPTION) $<

which expands

COMPILE.c = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c

This will also print # environment data. Here, you will find GCC's include path among other useful info.

C_INCLUDE_PATH=/usr/include

In make, when it comes to search, the paths are many, the light is one... or something to that effect.

  1. C_INCLUDE_PATH is system-wide, set it in your shell's *.rc.
  2. $(CPPFLAGS) is for the preprocessor include path.
  3. If you need to add a general search path for make, use:
VPATH = my_dir_to_search

... or even more specific

vpath %.c src
vpath %.h include

make uses VPATH as a general search path so use cautiously. If a file exists in more than one location listed in VPATH, make will take the first occurrence in the list.

Chlorate answered 28/7, 2020 at 20:22 Comment(0)
R
1

CPPFLAGS seems to be an invention of GNU Make, referenced in some of its built-in recipes. Unlike CFLAGS, it is used in recipes for multiple languages, including, for example, Fortran.

If your program is built by some Free software distributions, you may find that some of them require packages to interpolate this variable, using CPPFLAGS for passing down options like -D_WHATEVER=1 for passing down a macro definition.

That said, I've never seen a project advertise CPPFLAGS as something that is a good idea to use in order to customize its build behavior.

It seems uncommon for a distro to pass CPPFLAGS to all packages, and requires them to handle it (as a matter of documented fact about that distro). I seem to remember Gentoo does this; I'd love to be informed about any other.

It's not uncommon for custom build rules in projects to neglect to interpolate $(CPPFLAGS) into the $(CC) command line. Neglecting to interpolate $(CPPFLAGS) has no effect on a program being build by any of the common distros, from Debian to Yocto or what have you.

Furthermore, when preprocessor flags are passed, they appear in CFLAGS. So even if you support CPPFLAGS (whether by way of using built-in GNU Make rules, or imitating them) you cannot assume that only CPPFLAGS will contain flags for preprocessing.

Separation of preprocessor flags from CFLAGS is unnecessary because:

  • There is a way to run gcc to do preprocessing only. That gcc command can still take compiler options not related to preprocessing.

  • Conversely, the stand-alone GNU cpp is tolerant to compiler options, such as -W warnings that do not pertain to preprocessing and even code generation options like -fstrict-aliasing and the linker-pass through like -Wl,--whatever.

So generally speaking, build systems that need to call the stand-alone preprocessor for whatever reason on C code can just pass it $(CFLAGS).

It's easy enough for the application developers to write GNU Make code to separate preprocessor flags out of $(CFLAGS):

cpp_only_flags := $(foreach arg,                        \
                     $(CFLAGS),                         \
                     $(or $(filter -D%,$(arg)),         \
                          $(filter -U%,$(arg)),         \
                          $(filter -I%,$(arg)),         \
                          $(filter -iquote%,$(arg)),    \
                          $(filter -W%,$(arg)),         \
                          $(filter -M%,$(arg))))        \
                          $(CPPFLAGS) # also pull this in

all:
    @echo cpp_only_flags == $(cpp_only_flags)

Demo:

$ make CFLAGS="-Wall -I/path/to/include -W -UMAC -DFOO=bar -o foo.o -lm"
cpp_only_flags == -Wall -I/path/to/include -W -UMAC -DFOO=bar

In the case of the GNU compiler and preprocessor, this is probably unnnecessary; but it illustrates a technique that could be used for non-GNU compilers and preprocessors, in a build system based on GNU Make.

Nonetheless, interpolating $(CPPFLAGS) into build rules seems like a good idea just because GNU Make does it in its own rules.

Also, as mentioned already, GNU Make pulls in the same CPPFLAGS variable into build rules for different languages, whereas CFLAGS is only for C. For instance if we have a project that has preprocessed Fortran, as well as C, then CPPFLAGS will act globally across the C and Fortran, which could be something useful in that kind of project.

In summary,

  • there is no harm in supporting CPPFLAGS;

  • some distros out there use it (Gentoo?) which makes it a good idea to support it in your Makefile, similarly to the GNU Make built-in recipes;

  • it's probably a bad idea to advertise the variable as something that should be used;

  • when building other people's projects, probably avoid relying on CPPFLAGS to do anything that can be done with CFLAGS.

Radioactive answered 5/1, 2022 at 20:37 Comment(2)
"As an application developer writing a Makefile, you cannot rely on the existence of CPPFLAGS" -- surely you can add it to your installation instructions? Makefiles can have so many quirks that you pretty much always need to double-check the instructions when building or packaging an unfamiliar program.Calends
@Calends If you're writing a project that you hope is packaged into distros, you should avoid unusual build instructions and stick to conventions. You will be handed CFLAGS for sure. If you need to call cpp separately from the compiler for whatever reason, you should pull the cpp-specific flags from CFLAGS yourself. It's not reasonable to ask the the distro maintainer to do that. They will likely have to write code in order to do that. If your program goes into seven distros, seven people will do it differently, and two will get it wrong in some way.Radioactive
M
0

I installed httpd on Ubuntu 18.04 using the CPPFLAGS variable for the -DLINUX flag. When run, CPPFLAGS scans the code from top to bottom, file by file, looking for directives before compiling, and will not be extended by other meaningful things like size optimization, flags that do not increase the size of the output file; under the type of processor; to reduce the size of the code and speed up the program; disable all variables except case. The only difference between CPPFLAGS and CFLAGS is that CFLAGS can be set to specify additional switches to be passed to the compiler. That is, the CFLAGS environment variable creates a directory in the installation path (eg CFLAGS=-i/opt/include) to add debugging information to the executable target's path: include general alarm messages; turning off alarm information; independent location generation; display compiler driver, preprocessor, compiler version number.

Standard way to set CPPFLAGS:

sudo ./configure --enable-unixd=DLINUX #for example

list of some known variables:

CPPFLAGS - is the variable name for flags to the C preprocessor.
CXXFLAGS - is the standard variable name for flags to the C++ compiler.
CFLAGS is - the standard name for a variable with compilation flags.
LDFLAGS - should be used for search flags/paths (-L) - i.e. -L/usr/lib (/usr/lib are library binaries).
LDLIBS - for linking libraries. 
Marquismarquisate answered 17/1, 2022 at 21:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.