Generate debug versions of targets in a DRY method - makefile

I am using GNUmake and I have a makefile with some targets defined and their respective dependencies. I would like to create a debug version of these targets, so I have this:
TARGET := a b c
all: $(TARGET)
a: a.o e.o
$(CC) $(CFLAGS) $(INCDIRS) -c $(^) -o $(#)
b: b.o g.o
$(CC) $(CFLAGS) $(INCDIRS) -c $(^) -o $(#) -pthread
c: f.o c.o
$(CC) $(CFLAGS) $(INCDIRS) -lm -c $(^) -o $(#)
%.o: %.c
$(CC) $(CFLAGS) $(INCDIRS) -c $(^) -o $(#)
So this is all good, but now I would like to create debug versions of these rules, but I don't want to have to rewrite the rules. I just want to add additional flags like -g -DDEBUG to the CFLAGS variable and change the names of the targets.
I tried this static rule stuff
$(TARGET:%=debug_%): %: * #<--- not sure how to specify the dependencies
# CFLAGS += -g -DDEBUG <-- uncommenting this line is an error
This didn't work. I wanted the above rule to match the targets I already specified, but add additional parameters to CFLAGS which will then generate files like debug_a, debug_b, debug_c etc. I am not sure if this is possible with make but that was just the way I was reasoning about this.
Any help will be appreciated, thanks

So I finally found a suitable solution:
DEBUG_TARGET := $(TARGET:%=debug_%)
debug: $(DEBUG_TARGET)
$(DEBUG_TARGET):
debug_%: CFLAGS += -g -DDEBUG
debug_%: %
#mv $* debug_$*
This will generate a debug version of each target without having to create separate rules.
The last line was the sweet spot for me and this accomplishes what I wanted without having to repeat myself too much

Related

Defer evaluation of automatic variables in recipes

I have the following makefile:
CC ?= gcc
LD := gcc
CFLAGS := -Wall -Wextra -Werror -Wfatal-errors
LDFLAGS :=
LIBRARIES := m c
INCLUDEDIRS := .
LIBS = $(addprefix -l,$(LIBRARIES))
INCLUDES = $(addprefix -I,$(INCLUDEDIRS))
SRC := $(wildcard *.c)
TARGET = $(TARGETDIR)/test
OBJDIR = $(TARGETDIR)/obj/
OBJ = $(addprefix $(OBJDIR),$(SRC:%.c=%.c.o))
.SUFFIXES:
.SUFFIXES: .c.o
.PHONY: all debug i7avx i7avx-debug
all: TARGETDIR := generic
all: CFLAGS += -O3
all: LDFLAGS += -s
all: $(TARGET)
debug: CFLAGS += -Og
debug: TARGETDIR := generic/dbg
debug: $(TARGET)
$(OBJDIR):
#mkdir -p $(OBJDIR)
$(OBJ): | $(OBJDIR)
$(OBJDIR)%.c.o : %.c
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $#
$(TARGET) : $(OBJ)
$(LD) $(LDFLAGS) -o $# $^ $(LIBS)
The special thing here is that the output directory depends on the target.
Currently, only all and debug is defined, but the idea is to support a whole slew of architectures, and to define an outputdir per target.
Problem: this does not work. If I run this, I get:
cc -Wall -Wextra -Werror -Wfatal-errors -O3 -I. -c main.c -o /obj/main.c.o
Assembler messages:
Fatal error: can't create /obj/main.c.o: No such file or directory
make: *** [Makefile:37: /obj/main.c.o] Error 1
Which implies that the TARGETDIR variable was expanded too late.
If I replace the automatic variables with real variables, it does work:
$(OBJ): | $(OBJDIR)
$(OBJDIR)%.c.o : %.c
$(CC) $(CFLAGS) $(INCLUDES) -c $(SRC) -o $(OBJ)
$(TARGET) : $(OBJ)
$(LD) $(LDFLAGS) -o $(TARGET) $(OBJ) $(LIBS)
running this:
cc -Wall -Wextra -Werror -Wfatal-errors -O3 -I. -c main.c -o generic/obj/main.c.o
gcc -s -o generic/test generic/obj/main.c.o -lm -lc
Sooo, how can I make the autmatic variables expand after the TARGETDIR was defined?
Make does handle wildcards very deftly, or this would be a much easier problem.
As it is, I think the best solution is to use recursive Make. Just change this:
all: $(TARGET)
debug: $(TARGET)
to this:
all debug:
$(MAKE) $(TARGET) TARGETDIR=$(TARGETDIR) CFLAGS+='$(CFLAGS)' LDFLAGS=$(LDFLAGS)
First, the reason why it does not work :
You're using target-specific variables, but those are only available in the context of a target recipe (I'm quoting the manual here), not during the rules evaluation :
Make will first read your Makefile, evaluate your $(OBJDIR) and $(TARGET) rules (at this point $(TARGETDIR)is not yet defined) then, it will try to update all, and at this point set $(TARGETDIR) to the target-specific value for all (which explains why you're second example work, but it should rebuild every time).
I may have some hints to achieving what you're trying to do (I'm actually planning on doing a similar thing soon) :
you could use the eval function to generate one rule for each build/archi, like this:
#define TARGET_RULE
$(TARGET) : $(OBJ)
$$(RECIPE)
#endif
$(foreach TARGETDIR, $(BUILD_LIST), $(eval $(TARGET_RULE))
($$ is needed for the recipe to avoid it being expanded during the rule evaluation)
You should also be able to define only the rule or rules for the target you are currently building (not sure if that would make a signifiant perf difference).

Identical Makefile nomenclature for using variable on different build commands with different results

There are three programs build by this Makefile. They follow the same pattern, but produce different build commands when run. Specifically, I require compilation with c++11 but can only achieve this on one of the build commands. Why is this?
Makefile:
CXX=g++
RM=rm -f
CFLAGS=-std=c++11 -g -Wall $(shell root-config --cflags)
LDFLAGS=-g $(shell root-config --ldflags)
LDLIBS=$(shell root-config --libs)
SOURCES=generic_queue.cpp map_compare.cpp vector_search.cpp
OBJS=$(SOURCES:.cpp=.o)
all: $(SOURCES) generic_queue_test list_of_lists map_compare_test vector_search_test
# Note that $(CFLAGS) is used in the $(CXX) ... command
# each time that a .o file is built.
vector_search_test: $(OBJS) vector_search_test.o
$(CXX) $(LDFLAGS) -o vector_search_test vector_search_test.o $(LDLIBS)
vector_search_test.o: vector_search.cpp vector_search.h
$(CXX) $(CFLAGS) -c vector_search.cpp -o vector_search_test.o
generic_queue_test: $(OBJS) generic_queue_test.o
$(CXX) $(LDFLAGS) -o generic_queue_test generic_queue_test.o $(LDLIBS)
generic_queue_test.o: generic_queue.cpp generic_queue.h fixed_priority_queue.h
$(CXX) $(CFLAGS) -c generic_queue.cpp -o generic_queue_test.o
list_of_lists: $(OBJS) list_of_lists.o
$(CXX) $(LDFLAGS) -o list_of_lists list_of_lists.o $(LDLIBS)
list_of_lists.o: list_of_lists.cpp list_of_lists.h
$(CXX) $(CFLAGS) -c list_of_lists.cpp -o list_of_lists.o
map_compare_test: $(OBJS) map_compare.o
$(CXX) $(LDFLAGS) -o map_compare map_compare.o $(LDLIBS)
map_compare.o: map_compare.cpp map_compare.h
$(CXX) $(CFLAGS) -c map_compare.cpp -o map_compare.o
clean:
$(RM) $(OBJS) generic_queue_test.o list_of_lists.o map_compare.o
dist-clean: clean
$(RM) generic_queue_test list_of_lists map_compare
Output:
g++ -c -o generic_queue.o generic_queue.cpp
g++ -std=c++11 -g -Wall -pthread -m64 -I/usr/include/root -c map_compare.cpp -o map_compare.o
g++ -c -o vector_search.o vector_search.cpp
We see that only the second g++ command fully utilizes CFLAGS variable. Why? Does it have to do with the $(shell ...) portion of the CFLAGS variable?
Edit:
Was able to solve my problem by changing the name of the object file vector_search_test.o to vector_search.o Why did that work?
You have a couple of bugs in your makefile, adding up to this behavior.
First consider OBJS, which contains
generic_queue.o map_compare.o vector_search.o
These files are prerequisites of other targets, but you never actually use generic_queue.o or vector_search.o. Bug #1: you have extra prerequisites by mistake.
These files are prerequisites of other targets, so Make must build them. But how? You have provided rules for three object files:
vector_search_test.o: vector_search.cpp vector_search.h
...
generic_queue_test.o: generic_queue.cpp generic_queue.h fixed_priority_queue.h
...
list_of_lists.o: list_of_lists.cpp list_of_lists.h
...
map_compare.o: map_compare.cpp map_compare.h
...
That last one will do for map_compare.o, but you have given no rules for generic_queue.o or vector_search.o (and there's really no reason you should, since you never use them). But Make knows how to perform certain standard builds, such as foo.cpp => foo.o. If you don't provide a rule, Make will use its implicit rule, which works out to something like this:
generic_queue.o: generic_queue.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c generic_queue.cpp -o generic_queue.o
This is very similar to the rules you wrote. In fact, you probably wouldn't have noticed the difference -- and you could have omitted your rules and let Make rely on this one, but Bug #2, you added your flags -std=c++11 -g -Wall whatever to the wrong variable. Make uses CXXFLAGS to hold flags for the C++ compiler; you added yours to CFLAGS, which is for the C compiler.
(I've left out pattern rules and automatic variables since you don't seem to know about them-- I urge you to learn them, they're very useful, but that's for another day.)

Run a command for the nth target with the nth dependancy

I am having some confusion about how the '$<' and '$#' macros can be used with a list of elements. My ultimate goal is to compile a directory of C source files into executables with the same name, no extensions. Also I do not want to make object files.
Here is a simple makefile which I would like to upgrade using the macros.
CC = gcc -ansi -std=c99
CCFLAGS = -Wall -pedantic -O3
all : progA progB progC
progA : progA.c
$(CC) $(CCFLAGS) $< -o $#
progB : progB.c
$(CC) $(CCFLAGS) $< -o $#
progC : progC.c
$(CC) $(CCFLAGS) $< -o $#
This works fine, but I don't like the redundancy of the commands. I have found a workaround that works, and a near solution, but was hoping there might be a clear option.
workaround:
CC = gcc -ansi -std=c99
CCFLAGS = -Wall -pedantic -O3
PRGS := $(patsubst %.c,%,$(wildcard *.c))
all :
make $(PRGS)
% : *.c
$(CC) $(CCFLAGS) $#.c -o $#
What I don't like here is the call to make within the command. Running 'make' delivers a message in my terminal that looks something like this:
make[1]: Entering directory '/path/to/dir'
...actual commands...
make[1]: Leaving directory '/path/to/dir'
I am assuming this has to do with opening the same makefile and the [1] refers to the second file descriptor in the open file table (or something along those lines).
near solution:
CC = gcc -ansi -std=c99
CCFLAGS = -Wall -pedantic -O3
SRCS := $(wildcard *.c)
PRGS := $(patsubst %.c,%,$(SRCS))
all : $(PRGS)
$(PRGS) : $(SRCS)
$(CC) $(CCFLAGS) $< -o $#
This almost works, except it is always grabbing the first dependancy!
..... progA.c -o progA
..... progA.c -o progB
..... progA.c -o progC
So, does anyone have a cleaner method to my 'workaround' or perhaps a solution to my 'near solution'? Is there anyway to match the nth target to the nth dependency when running the command?
Thanks!
The $(PRGS): $(SRCS) change is just incorrect. It lists every source file as the prerequisites of every target.
You want to set the prerequisites of the all target to be every program you want built by default. To do that you want to use all: $(PRGS). Not a recipe of calling make again like you had originally.
(If you did want to keep the manual recursive call to make you could use make --no-print-directory on that call to avoid the message but that is still the wrong approach (and if you were going to do that you'd want to use $(MAKE) to handle arguments to the original make correctly.)
A second problem with your original makefile is the use of *.c in the prerequisite list of the % target. That sets the prerequisites of every target to be every *.c file in the directory. That's not what you want. You want each target to have its own .c file as its prerequisite.
You want:
all: $(PRGS)
%: %.c
$(CC) $(CCFLAGS) $#.c -o $#
That being said make has a built-in rule for exactly that foo.c -> foo compilation so you should just use that. That rule uses the $(CC) and $(CFLAGS) variables. So just set those to what you want and you are done.
So this makefile should do what you want. (Note how I moved some arguments to CFLAGS instead of CC. CC should generally, to my knowledge, by the compiler itself and not any arguments.)
CC = gcc
CFLAGS = -ansi -std=c99 -Wall -pedantic -O3
PRGS := $(patsubst %.c,%,$(wildcard *.c))
all : $(PRGS)

Makefile: target with pattern does not work

My Makefile looks like this:
BIN = bin
OBJECTS = object1.o \
object2.o \
object3.o
HDR = $(OBJECTS:%.o=%.h) header1.h header2.h
MAIN = main.c
CC = gcc
CFLAGS = -Wall -g -std=c99 -fstack-protector-all
LDFLAGS = -lpthread
$(BIN): $(OBJECTS) $(MAIN)
$(CC) $(CFLAGS) $(LDFLAGS) -o $# $^
%.o: %.c $(HDR)
$(CC) $(CFLAGS) -c $< -o $#
It seems that the %.o: %.c $(HDR) rule is not used. When invoking with option make -r it says that there's no rule to make target object.o. The build of each object file should depend on every header file. What am I missing?
Edit: I should mention that when doing echo $(HDR) than it looks like the variable contains the right values:
object1.h object2.h object3.h header1.h header2.h
In the declaration of HDR, try $(OBJECTS:.o=.h) instead. Or, better yet, use gcc -MM or the like to generate your dependencies instead.
A pattern rule can't have auxilliary dependencies like ${HDR}.
Use:
%.o : %.c
$(CC) $(CFLAGS) -c $< -o $#
${OBJECTS}: ${HDR}
Ok, the given Makefile should work, I had a typo in one of the header file names.
It's a pitty, but make doesn't warn about that. It seems that when a pattern based rule is missing a prerequisite than it's just ignored. The built-in .o creation rule is used instead.
Jonathan Leffler's proposal of ${OBJECTS}: ${HDR} brought that up, because than there's an error regarding "no rule to make target misspelled.h" - I would have expected that from my rule too.
So I can just agree to fluffy, it's better to use auto-generated dependencies instead.

How to instruct Makefile to use different compilers

I have to build my code for two different platforms at once using two different compilers and libraries. How can I do this with single make file.
Currently my makefile contents are given below. How can I instruct it to change the compiler to gcc within the same makefile?
VPATH = /root/Documents/project/src
CROSS_COMPILE = /opt/compilers/bin/compiler1
CXX = $(CROSS_COMPILE)-g++
CC = $(CROSS_COMPILE)-gcc
AS = $(CROSS_COMPILE)-as
AR = $(CROSS_COMPILE)-ar
NM = $(CROSS_COMPILE)-nm
LD = $(CROSS_COMPILE)-ld
OBJDUMP = $(CROSS_COMPILE)-objdump
OBJCOPY = $(CROSS_COMPILE)-objcopy
RANLIB = $(CROSS_COMPILE)-ranlib
STRIP = $(CROSS_COMPILE)-strip
CFLAGS = -c -Wall -D_REENTRANT -DACE_HAS_AIO_CALLS -D_GNU_SOURCE -DACE_HAS_EXCEPTIONS -D__ACE_INLINE__
LDFLAGS = -L. -L/etc/ACE_wrappers/lib
CPPFLAGS += -I/etc/ACE_wrappers -I/etc/ACE_wrappers/ace
LDLIBS = -lACE
OUTPUT_DIRECTORY=/root/Documents/bin
OBJ=/root/Documents/obj
ifneq ($(OUTPUT_DIRECTORY),)
all: $(OUTPUT_DIRECTORY)
$(OUTPUT_DIRECTORY):
-#$(MKDIR) "$(OUTPUT_DIRECTORY)"
endif
ifneq ($(OBJ),)
all: $(OBJ)
$(OBJ_DIR):
-#$(MKDIR) "$(OBJ)"
endif
SOURCES=File_1.cpp File_2.cpp
OBJECTS=$(SOURCES:%.cpp=$(OBJ)/%.o)
$(OBJ)/%.o: %.cpp
#echo Building Objects
$(CC) $(CFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c -o $# $<
EXECUTABLE=MyExecutable
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
#echo $(SOURCES)
$(CC) $(LDFLAGS) $(OBJECTS) $(LDLIBS) -o $(OUTPUT_DIRECTORY)/$#
File_1.o:File_1.cpp
File_1.o:File_1.cpp
.PHONY: clean
clean:
rm $(OBJECTS) $(OUTPUT_DIRECTORY)/$(EXECUTABLE)
I'd start by putting all the platform-specific defines in a separate makefile. That way you can do:
include $(TARGET).make
Where $(TARGET).make defines CC and other variables for each particular platform. Then you can call make recursively setting TARGET to what you want. Something like:
build:
$(MAKE) TARGET=platform1 all
$(MAKE) TARGET=platform2 all
But really there are many, many ways in which you can achieve the same thing.
Use autoconf. When you configure the project with ./configure, it will automatically choose the one available.
That would be a lot easier to read if the linebreaks were properly preserved in your post.
Anyway, I expect that you're going to have to have two copies of a number of things in the Makefile, such that it's of the form
all : all-platform1 all-platform2
stuff-platform1 : requirements
$(CC1) whatever
stuff-platform2 : requirements
$(CC2) whatever
It's possible that you might be able to auto-generate a makefile like this, to save some effort though.

Resources