optimization and debugging options in Makefile - debugging

I wonder where to put the optimization and debugging options in Makefile: linking stage or compiling stage? I am reading a Makefile:
ifeq ($(STATIC),yes)
LDFLAGS=-static -lm -ljpeg -lpng -lz
else
LDFLAGS=-lm -ljpeg -lpng
endif
ifeq ($(DEBUG),yes)
OPTIMIZE_FLAG = -ggdb3 -DDEBUG
else
OPTIMIZE_FLAG = -ggdb3 -O3
endif
ifeq ($(PROFILE),yes)
PROFILE_FLAG = -pg
endif
CXXFLAGS = -Wall $(OPTIMIZE_FLAG) $(PROFILE_FLAG) $(CXXGLPK)
test: test.o rgb_image.o
$(CXX) $(CXXFLAGS) -o $# $^ $(LDFLAGS)
Makefile.depend: *.h *.cc Makefile
$(CC) -M *.cc > Makefile.depend
clean:
\rm -f absurdity *.o Makefile.depend TAGS
-include Makefile.depend
What surprises me is CXXFLAGS is used in linking. I know it is also used in the implicit rule for compiling to generate .o files but is it necessary to use it again for linking? Specifically, where should I put optimization and debugging: linking stage or compiling stage?

Short answer:
optimization: needed at compiler time
debug flag: needed at compile time
debugging symbols: need at both compile and linking time
Take note that the linker decides what bits of each object file and library need to be included in the final executable. It could throw out the debugging symbols (I don't know what the default behavior is), so you need to tell it not to.
Further, the linker will silently ignore options which do not apply to it.
To the comments:
The above are very general claims based on knowing what happens at each stage of compilation, so no reference.
A few more details:
optimization: takes two major forms: peephole optimization can occur very late, because it works on a few assembly instructions at a time (I presume that in the GNU tool chain the assembler is responsible for this step), but the big gains are in structural optimizations that are generally accomplished by re-writing the Abstract Syntax Tree (AST) which is only possible during compilation.
debug flag: In your example this is a preprocessor directive, and only affects the first part of the compilation process.
debugging symbols: Look up the ELF file format (for instance), you'll see that various bits of code and data are organized into different blocks. Debugging symbols are stored in the same file along as the code they relate to, but are necessarily kept separate from the actual code. As such, any program that manipulates these files could just dump it. Therefore both the compiler and the linker need to know if you want them or not.

Related

Cannot pass flags to Makefile to compile my code

I have a project that basically compiles from the command line in the following form:
g++ -o stack_raster stack_raster.cpp -lgdal -lboost_filesystem -lboost_system
I made a Makefile, and this is the content:
CXX =g++
LDDFLAGS = -lgdal -lboost_system -lboost_filesystem
all: clean stack_raster
clean:
rm -f stack_raster
However I got a collect2: error: ld returned 1 exit status.
A second variation of my Makefile I tried was:
CXX = g++
CPPFLAGS = -lgdal -lboost_system -lboost_filesystem
all: clean stack_raster
clean:
rem -f stack_raster
but I still receive the following message (even though the compile flags appear as they should for my program to compile successfully).
collect2: error: ld returned 1 exit status
<builtin>: recipe for target `stack_raster` failed
make: *** [stack_raster] Error 1
Does anyone could help me with a reference or hint about my problem, and how could I tackle it?
Does anyone could help me with a reference or hint about my problem, and how could I tackle it?
To begin with, you should have a look at the actual link command that make executed. It should have been echoed to make's output just before the error message from collect2. Understanding what's wrong with the command is the first step in determining how to fix your makefile.
In the first case, the command is probably something like
g++ stack_raster.cpp -o stack_raster
In the second, it is probably something like
g++ -lgdal -lboost_system -lboost_filesystem stack_raster.cpp -o stack_raster
The latter is probably also very similar to what you would get with the first makefile if you corrected the spelling of LDDFLAGS to LDFLAGS.
You will note that the library flags come in a different place in that command than they do in your manual command, and I assume you know that the order of objects and library flags on the linker command line is significant to Unix-style linkers such as GNU's (which is the one that the g++ driver will use).
You can certainly fix this by writing an explicit rule, as you describe in your own answer, but your makes' built-in rules may be up to the task, too. If you are using GNU make then they certainly are. For this purpose it is useful to know what the built-in rules actually are, and essential to know what the variables on which these rules depend mean.
Specifically,
LDFLAGS provides options to pass when invoking the linker, and conventionally, they appear on the command line before the objects being linked. As a result, this variable typically is not appropriate for specifying libraries (but it is fine for other link-specific options, such as -L to add directories to the library search path).
CPPFLAGS provides options for modulating the behavior of the C preprocessor (including when compiling C++). These do not typically appear at all in link(-only) commands executed by make, but they will appear (early) in commands for compiling object files from C or C++ sources, and in rules for building executables directly from C or C++ sources.
Neither of those is what you want, but if you are using GNU make, then its documentation for the former explicitly tells you what (with that make implementation) you should do instead:
Extra flags to give to compilers when they are supposed to invoke the
linker, ‘ld’, such as -L. Libraries (-lfoo) should be added to the
LDLIBS variable instead.
(emphasis added)
In GNU make, and perhaps some others, the LDLIBS variable serves exactly the purpose you need: to specify the libraries to link. These will appear at the end of the link command line from built-in rules, as you can confirm from GNU make's catalog of implicit rules, or from the list obtainable by running make -p in a directory containing no makefile.
So, with GNU make you can get the build you seem to want from the built-in rules, with this:
CXX = g++
LDLIBS = -lgdal -lboost_system -lboost_filesystem
all: clean stack_raster
clean:
rm -f stack_raster
In closing, I note that cleaning before building by default, as your examples do and mine imitates, largely defeats the purpose of using make instead of a simple script. Part of the point of make is to do the minimum work necessary, and if your target executable is present and not out of date with respect to its sources then there is no reason to force it to be rebuilt.
Check out the answer:
Set up my makefile to compile C with just "make"
YOu have to specify in the Makefile the file you want to create in this case stack_raster.exe and the objective file in this case stack_raster.cpp and specify the command line arguments you normally pass for compiling. So the Makefile would be something like:
CXX=g++
stack_raster.exe: stack_raster.cpp
g++ -o stack_raster.exe stack_raster.cpp -lgdal -lboost_filesystem -lboost_system
all: clean stack_raster.exe
clean:
rm -f stack_raster.exe

Should CXXFLAGS, CPPFLAGS and LDFLAGS be overridden in a Makefile?

Situation
I am using a handwritten GNUmakefile in which CXXFLAGS, CPPFLAGS and LDFLAGS are appended to by the += assignment, as in:
CXXFLAGS += -std=c++11 $(MODENV) $(WARNINGS) $(OPTIMS)
CPPFLAGS += $(DMACROS) $(INCDIRS)
LDFLAGS += $(MODENV) $(LIBDIRS) $(EXTRA_LIBS)
Problem
When the user defines his own flags at the command-line, the appending in the Makefile will be ignored. This leaves the variables to exactly what the user set them. (And in my case, the build will fail.) The generic solution for this problem is to override the variables, as in:
override CXXFLAGS += -std=c++11 $(MODENV) $(WARNINGS) $(OPTIMS)
override CPPFLAGS += $(DMACROS) $(INCDIRS)
override LDFLAGS += $(MODENV) $(LIBDIRS) $(EXTRA_LIBS)
This way, the necessary content will be appended to the user's variable.
Questions
Is overriding variables considered bad practice?
Is setting the above flags inside the Makefile considered bad practice?
If "yes" to both questions above, then where do I put -std=c++11, if not in CXXFLAGS?
If a user overrides the variables, assume the user knows what they're doing. The system they're working on may have very different requirements and need to override the variables. Don't make the difficult impossible.
I'm not going to comment on good/bad practice, as it depends on the use case (who are the users of this makefile? Do they all work with you, so you could just tell them how it's meant to be used? Can you just document your conventions, in the makefile comments or a project README?)
But ...
where do I put -std=c++11, if not in CXXFLAGS?
If you have flags that are essential and must not be overridden by the user, you can find another way to put them in the compilation command.
For example:
CXX := g++ -std=c++11
Or:
foo.o: foo.cc
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -std=c++11 -o $# $^
Even better might be to use a variable so that it can still be set by the user (e.g. to use -std=c++14 instead) but isn't set by CXXFLAGS:
STD := -std=c++11
foo.o: foo.cc
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(STD) -o $# $^
(Or just put the -std option before CXXFLAGS so that a different std option in CXXFLAGS will take precedence).
As the use cases for build management with make are so diverse, there simply is no ground to objectively judge this as "bad practice". If you are writing OSS for a multitude of platforms and for an unknown audience and timeframe, the POLA/POLS should be applied. That said, a user who is surprised to find more flags than the ones she gave on the command line is a rather unrealistic corner case, so -override has its place. In the end the values you add are absolutely necessary for the build, aren't they?
PS: the POLA should be applied to all engineering activities of course - its just that the definition of "surprise" shifts with the task in question.

What are LINK.cpp / COMPILE.cpp Variables good for in make?

Shortly before I asked this question : g++ undefined reference to library symbols
It seems I misused predefined variables of make, namely LINK.cpp.
The error was that I linked the library before the objects instead of afterwards.
Now this raised the question what those variables, particularly the LINK.cpp one, are good for because they expand like this :
LINK.cpp = $(LINK.cc)
LINK.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
^
I usually store my libs in LDFLAGS variable, which I thought was common, so LINK.cc will always link the libs before , because I can add my object files only after, and will cause errors like in the posted question.
Where do I have to put my objects then to use the LINK.cpp variable properly?
Candidates are :
CXXFLAGS
CPPFLAGS
,which both seem not to be right.
Have I gotten the use case of the variable wrong ?
To be clear : The question does not aim at the errors a linker can throw at you, but the usage of the variables in make.
I usually store my libs in LDFLAGS variable, which I thought was common
It is a fairly common mistake. The conventional meanings of the
make variables that figure in C or C++ compilation are:-
CFLAGS: Compilation options for C
CXXFLAGS: Compilation options for C++
CPPFLAGS: Preprocessor options for C or C++
LDFLAGS: Linkage options, excluding library (-l) options
LDLIBS: Libraries or library (-l) options for linkage.
These are the meanings assumed in GNU Make's builtin rules.
The intended use of the variable:
LINK.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
is therefore as in:
prog: $(OBJS)
$(LINK.cc) $^ $(LDLIBS) -o $#
with the libraries following the object files.
(The variable TARGET_ARCH appears in builtin rules evidently with the
intended meaning target-architecture options, but is nontheless undocumented).

Cmake vs make sample codes?

I was wondering if there was any sample code for Makefiles (make) and CMakeLists.txt (cmake) that both do the same thing (the only difference being that one is written in make and the other in cmake).
I tried looking for 'cmake vs make', but I never found any code comparisons. It would be really helpful to understand the differences, even if just for a simple case.
The following Makefile builds an executable named prog from the sources
prog1.c, prog2.c, prog3.c and main.c. prog is linked against libmystatlib.a
and libmydynlib.so which are both also built from source. Additionally, prog uses
the library libstuff.a in stuff/lib and its header in stuff/include. The
Makefile by default builds a release target, but offers also a debug target:
#Makefile
CC = gcc
CPP = g++
RANLIB = ar rcs
RELEASE = -c -O3
DEBUG = -c -g -D_DEBUG
INCDIR = -I./stuff/include
LIBDIR = -L./stuff/lib -L.
LIBS = -lstuff -lmystatlib -lmydynlib
CFLAGS = $(RELEASE)
PROGOBJS = prog1.o prog2.o prog3.o
prog: main.o $(PROGOBJS) mystatlib mydynlib
$(CC) main.o $(PROGOBJS) $(LIBDIR) $(LIBS) -o prog
debug: CFLAGS=$(DEBUG)
debug: prog
mystatlib: mystatlib.o
$(RANLIB) libmystatlib.a mystatlib.o
mydynlib: mydynlib.o
$(CPP) -shared mydynlib.o -o libmydynlib.so
%.o: %.c
$(CC) $(CFLAGS) $(INCDIR) $< -o $#
%.o: %.cpp
$(CPP) $(CFLAGS) $(INCDIR) -fPIC $< -o $#
Here is a CMakeLists.txtthat does (almost) exactly the same, with some comments to underline the
similarities to the Makefile:
#CMakeLists.txt
cmake_minimum_required(VERSION 2.8) # stuff not directly
project(example) # related to building
include_directories(${CMAKE_SOURCE_DIR}/stuff/include) # -I flags for compiler
link_directories(${CMAKE_SOURCE_DIR}/stuff/lib) # -L flags for linker
set(PROGSRC prog1.c prog2.c prog3.c) # define variable
add_executable(prog main.c ${PROGSRC}) # define executable target prog, specify sources
target_link_libraries(prog mystatlib mydynlib stuff) # -l flags for linking prog target
add_library(mystatlib STATIC mystatlib.c) # define static library target mystatlib, specify sources
add_library(mydynlib SHARED mydynlib.cpp) # define shared library target mydynlib, specify sources
#extra flags for linking mydynlib
set_target_properties(mydynlib PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
#alternatively:
#set_target_properties(mydynlib PROPERTIES COMPILE_FLAGS "-fPIC")
In this simple example, the most important differences are:
CMake recognizes which compilers to use for which kind of source. Also, it
invokes the right sequence of commands for each type of target. Therefore, there
is no explicit specification of commands like $(CC) ..., $(RANLIB) ... and so on.
All usual compiler/linker flags dealing with inclusion of header files, libraries, etc.
are replaced by platform independent / build system independent commands.
Debugging flags are included by either setting the variable CMAKE_BUILD_TYPE to "Debug",
or by passing it to CMake when invoking the program: cmake -DCMAKE_BUILD_TYPE:STRING=Debug.
CMake offers also the platform independent inclusion of the '-fPIC' flag (via
the POSITION_INDEPENDENT_CODE property) and many others. Still, more obscure settings can be implemented by hand in CMake just as well as in a Makefile (by using COMPILE_FLAGS
and similar properties). Of course CMake really starts to shine when third party
libraries (like OpenGL) are included in a portable manner.
The build process has one step if you use a Makefile, namely typing make at the command line. For CMake, there are two steps: First, you need to setup your build environment (either by typing cmake <source_dir> in your build directory or by running some GUI client). This creates a Makefile or something equivalent, depending on the build system of your choice (e.g. make on Unixes or VC++ or MinGW + Msys on Windows). The build system can be passed to CMake as a parameter; however, CMake makes reasonable default choices depending on your system configuration. Second, you perform the actual build in the selected build system.
Sources and build instructions are available at https://github.com/rhoelzel/make_cmake.
Grab some software that uses CMake as its buildsystem (there's plenty of opensource projects to choose from as an example). Get the source code and configure it using CMake. Read resulting makefiles and enjoy.
One thing to keep in mind that those tools don't map one-to-one. The most obvious difference is that CMake scans for dependencies between different files (e.g. C header and source files), whereas make leaves that to the makefile authors.
If this question is about a sample Makefile output of the CMakeList.txt file then please check the cmake-backend sources and generate one such Makefile. If it is not then adding to the reply of #Roberto I am trying to make it simple by hiding the details.
CMake function
While Make is flexible tool for rules and recipe, CMake is a layer of abstraction that also adds the configuration feature.
My plain CMakeLists.txt will look like the following,
cmake_minimum_required(VERSION 2.8)
project(example)
file(GLOB testapp_SOURCES *.cc)
add_executable(testapp ${testapp_SOURCES})
Note, that CMake hides how the build can be done. We only specified what is the input and output.
The CMakeLists.txt contains list of function-calls that are defined by cmake.
(CMake function) Vs Make rules
In Makefile the rules and recipes are used instead of functions . In addition to function-like feature, rules and recipes provide chaining. My minimalistic Makefile will look like the following,
-include "executable.mk"
TARGETS=testapp.bin
all:${TARGETS}
While the executable.mk will look like the following,
SOURCES=$(wildcard *.cpp)
OBJECTS=$(SOURCES:.cpp=.o)
DEPS=$(SOURCES:.cpp=.d)
%.bin:$(OBJECTS)
$(CC) $(CFLAGS) -o $# $^ $(LFLAGS) $(LIBS)
.PHONY: all clean
clean:
$(RM) $(OBJECTS) $(DEPS) $(TARGETS)
-include $(DEPS)
Starting from the scratch I shall start with a Makefile like the following,
all: testapp.bin
testapp.bin:sourcea.o sourcb.o
$(CC) $(CFLAGS) -o $# $^ $(LFLAGS) $(LIBS)
.PHONY: all clean
clean:
$(RM) $(OBJECTS) testapp.bin
I got this snippet from here and modified it. Note that some implicit-rules are added to this file which can be found in the makefile-documentation. Some implicit variables are also relevant here.
Note, that Makefile provides the detail recipe showing how the build can be done. It is possible to write executable.mk to keep the details defined in one file. In that way the makefile can be reduced as I showed earlier.
Internal Variables in CMake and Make
Now getting little advanced, in CMake we can set a compiler flag like the following,
set(CMAKE_C_FLAGS "-Wall")
Please find out more about CMake default variables in CMakeCache.txt file.
The CMake code above will be equivalent to Make code below,
CFLAGS = -Wall
Note that CFLAGS is an internal variable in Make, the same way, CMAKE_C_FLAGS is internal variable in CMake .
adding include and library path in CMake
We can do it in cmake using functions.
target_include_directories(testapp PRIVATE "myincludes")
list(APPEND testapp_LIBRARIES
mytest mylibrarypath
)
target_link_libraries(testapp ${testapp_LIBRARIES})
Vs adding include and library path in Make
We can add include and libraries by adding lines like the following,
INCLUDES += -Imyincludes
LIBS += -Lmylibrarypath -lmytest
Note this lines above can be generated from auto-gen tools or pkg-config. (though Makefile is not dependent of auto-config tools)
CMake configure/tweek
Normally it is possible to generate some config.h file just like auto-config tools by using configure_file function. It is possible to do more trick writing custom functions. And finally we can select a config like the following,
cmake --build . --config "Release"
It is possible to add some configurable option using the option function.
Makefile configure/tweak
If somehow we need to compile it with some debug flag, we can invoke the make like,
make CXXFLAGS=NDEBUG
I think internal variables, Makefile-rules and CMake-functions are good start for the comparison, good luck with more digging.

CFLAGS vs CPPFLAGS

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?)
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++
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.
You are after implicit make rules.
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.
C_INCLUDE_PATH is system-wide, set it in your shell's *.rc.
$(CPPFLAGS) is for the preprocessor include path.
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.
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.
CPPFLAGS seems to be an invention of GNU Make, referenced in some of its built-in recipes.
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.
This separation is a poor idea and completely unnecessary in the GNU environment because:
There is a way to run gcc to do preprocessing only (while ignoring compiler options unrelated to preprocessing).
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 can just pass it $(CFLAGS).
As an application developer writing a Makefile, you cannot rely on the existence of CPPFLAGS. Users who are not insider experts in open source building won't know about CPPFLAGS, and will do things like make CFLAGS=-Dfoo=bar when building your program. If that doesn't work, they will be annoyed.
As a distro maintainer, you cannot rely on programs to pull in CPPFLAGS; even otherwise well-behaved ones that pull in CFLAGS, LDFLAGS and LDLIBS.
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.

Resources