How to parameterize makefile targets - makefile

I got a Makefile that does build a 32 and a 64 bit version of a project:
#############################
# 32 bit variant of test tool
#############################
hidtest_32: $(OBJS_32)
$(CXX_32) -m32 -g $^ $(LIBS) -o hidtest_32
hid_32.o: hid.c
$(CC_32) -m32 $(CFLAGS) $< -o hid_32.o
../hidtest/hidtest_32.o: ../hidtest/hidtest.cpp
$(CC_32) -m32 $(CFLAGS) $< -o ../hidtest/hidtest_32.o
#############################
# 64 bit variant of test tool
#############################
hidtest_64: $(OBJS_64)
$(CXX_64) -m64 -g $^ $(LIBS) -o hidtest_64
hid_64.o: hid.c
$(CC_64) -m64 $(CFLAGS) $< -o hid_64.o
../hidtest/hidtest_64.o: ../hidtest/hidtest.cpp
$(CC_64) -m64 $(CFLAGS) $< -o ../hidtest/hidtest_64.o
As you can see both variants use the exact same build procedure, except that the number 32 has been replaced with 64.
I've tried something like
hidtest64: ARCH=64
hidtest64: hidtest_64
hidtest32: ARCH=32
hidtest32: hidtest_32
hidtest_%: $(OBJS_$(ARCH))
$(CXX_$(ARCH)) -m$(ARCH) -g $^ $(LIBS) -o $#
which does not work as expected. I guess I would need to access the part of the target that matched %, which I wasn't able to do.
Is there a way to consolidate the two variants into a single (parameterized) one?

If you are using GNU Make You can write a function (a callable macro). Single $ is substituted during $(call ...) double $ is substituted when doing $(eval $(call ...)). Background information here.
define add_target
$(info compiling hidtest_$(1)) # logged when doing $(call ...)
$$(info evaluating hidtest_$(1)) # logged when doing $(eval $(call ...))
hidtest_$(1): $(OBJS_$(1))
$(CXX_$(1)) -m$(1) -g $$^ $(LIBS_$(1)) -o $$#
endef
$(eval $(call add_target,32))
$(eval $(call add_target,64))

Related

Makefile with `ifeq` test for specific file

I am trying to set an if test for a specific file in my Makefile to compile it with different flags:
.f90.o:
ifeq ($<,main.f90)
#echo ok $<
$(F90) -c $< -o $#
else
#echo nope $<
$(F90) $(F90FLAGS) $(DEBUG) $(INCL) -c $< -o $#
endif
..and despite my efforts I am getting only:
nope main.f90
mpif90 -O2 -g -fbacktrace -fPIC -c main.f90 -o main.o
The ifeq conditional you have used is processed by Make at parse time, not when the recipe is executed. The macro $< is empty when your script is parsed so your recipe only ever contains the latter two lines.
One solution is to provide two recipes, one for your special case and then a pattern recipe for the rest:
main.o:main.f90
$(F90) -c $< -o $#
.f90.o:
$(F90) $(F90FLAGS) $(DEBUG) $(INCL) -c $< -o $#

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.)

How to write a makefile for llvm IR

If I have 3 files, function.h, function.c and my_program.c which calls a method in function.h all in the same directory, what would be the best way to write a makefile so that I end up with a my_program.bc that would actually run when I type in lli my_program.bc? (I need to run a user defined pass that would insert stuff into the functions - should I run the pass on function.bc and test.bc, or should I link before running the pass?)
I've tried llvm-link function.bc my_program.bc with no luck. I feel I'm either missing something simple or going about the whole thing wrong.
Current terrible none-working makefile:
.PHONY: all clean
CC = clang
CFLAGS = -std=gnu99 -D_POSIX_C_SOURCE=200809L -g -Wall
IRFLAGS = -O3 -emit-llvm
TARGET = test
DEPS = functions.h
all: $(TARGET)
bc: test2
%.o: %.c $(DEPS)
$(CC) -c -o $# $< $(CFLAGS)
%.bc: %.c $(DEPS)
$(CC) $(IRFLAGS) -c -o $# $<
test2: test.bc functions.bc
llvm-link -o test2.bc $< functions.bc
test: test.o functions.o
$(CC) $(CFLAGS) -o $# $^
clean:
$(RM) $(TARGET) *.o *.bc
Why not just write a normal Makefile to produce the desired executable,
then use wllvm?
Shameless plug for wllvm:
https://github.com/SRI-CSL/whole-program-llvm
I do not use lli, so I would be interested to hear about how it resolved
any reliance on stdlibc that your program may have.

Makefile with generic intermediate rules

I have a makefile that works that looks like this:
TARGET:=prog
SOURCES:=a.c b.c
CFLAGS:=-Wall -g -O2
OBJECTS:=$(SOURCES:%.c=%.o)
$(TARGET): $(OBJECTS)
gcc -o $# $^
%.o: %.c
gcc $(CFLAGS) -c -o $# $<
clean:
rm -f $(TARGET) $(OBJECTS)
The only variables that the user needs to change are the target name and the sources required to build it. The objects that need to be generated are automatically determined from the sources. I would like to extend this to support multiple targets, each with its own list of sources. I'm having trouble getting the syntax right, though. This is the general idea:
TARGETS:=prog1 prog2
SOURCES_prog1:=a.c b.c
SOURCES_prog2:=a.c c.c
CFLAGS:=-Wall -g -O2
OBJECTS_$#=$(SOURCES_$#:%.c=%.o)
$(TARGETS): $(OBJECTS_$#)
gcc -o $# $^
%.o: %.c
gcc $(CFLAGS) -c -o $# $<
clean:
rm -f $(TARGET) $(OBJECTS)
But I can't get the object list to be generated correctly. I'm also unsure how to write the clean rule to clean all of the objects. Is this possible?
There are two primary ways that I see to do this.
The first involves dynamically creating the target/prerequisite mappings using the $(eval) function.
TARGETS:=prog1 prog2
SOURCES_prog1:=a.c b.c
SOURCES_prog2:=a.c c.c
CFLAGS:=-Wall -g -O2
$(TARGETS):
gcc -o $# $^
$(foreach t,$(TARGETS),$(eval OBJECTS_$t := $(SOURCES_$t:.c=.o))$(eval $t: $(OBJECTS_$t)))
The second involves using Secondary Expansion.
TARGETS:=prog1 prog2
SOURCES_prog1:=a.c b.c
SOURCES_prog2:=a.c c.c
CFLAGS:=-Wall -g -O2
.SECONDEXPANSION:
$(TARGETS): $$(OBJECTS_$$#)
gcc -o $# $^
$(foreach t,$(TARGETS),$(eval OBJECTS_$t := $(SOURCES_$t:.c=.o)))
In either case the clean target becomes:
clean:
rm -f $(TARGETS) $(foreach t,$(TARGETS),$(OBJECTS_$t))

Making a better Makefile

so I learned what a Makefile was some time ago, created a template Makefile and all I do is copy and alter the same file for every program I'm doing. I changed it a few times, but it's still a very crude Makefile. How should I improve it? This is an example of my current version:
CC = g++
CFLAGS = -std=gnu++0x -m64 -O3 -Wall
IFLAGS = -I/usr/include/igraph
LFLAGS = -ligraph -lgsl -lgslcblas -lm
DFLAGS = -g -pg
# make all
all: run test
# make a fresh compilation from scratch
fresh: clean test
#makes the final executable binary
run: main.o foo1.o foo2.o
$(CC) $(CFLAGS) $(LFLAGS) $^ -o $#
#makes the test executable with debugging and profiling tags
test: test.o foo1.o foo2.o
$(CC) $(DFLAGS) $(CFLAGS) $(LFLAGS) $^ -o $#
#makes teste.o
teste.o: teste.cpp
$(CC) $(CFLAGS) $(IFLAGS) -c $^ -o $#
#makes main.o
main.o: main.cpp
$(CC) $(CFLAGS) $(IFLAGS) -c $^ -o $#
#file foo1
foo1.o: foo1.cpp
$(CC) $(CFLAGS) $(IFLAGS) -c $^ -o $#
#file foo2
foo2.o: foo2.cpp
$(CC) $(CFLAGS) $(IFLAGS) -c $^ -o $#
clean: clean-test clean-o clean-annoying
clean-test:
rm test-rfv
clean-o:
rm *.o -rfv
clean-annoying:
rm *~ -rfv
Just by visually comparing with other makefiles I saw around in the web, this seems to be not a very bright Makefile. I don't know how they work, but I can see there's significantly less boilerplate and more generic code in them.
Can this can be made better, safer, and easier to particularize for each project?
You don't want to name specific files in a makefile if you can get away with it, and 99% of the time you can. This page shows how to develop a very general makefile. The following is my own makefile, based on that page's info:
SHELL := bash
PROG := pathed.exe
OUTDIRS := bin/debug bin/rel obj/debug obj/rel
PROG_REL := bin/rel/$(PROG)
PROG_DEBUG := bin/debug/$(PROG)
SRCFILES := $(wildcard src/*.cpp)
OBJFILES_REL := $(patsubst src/%.cpp,obj/rel/%.o,$(SRCFILES))
OBJFILES_DEBUG := $(patsubst src/%.cpp,obj/debug/%.o,$(SRCFILES))
DEPFILES := $(patsubst src/%.cpp,obj/%.d,$(SRCFILES))
CFLAGS := -Iinc -Wall -Wextra -MMD -MP
DBFLAGS := -g
RELFLAGS :=
CC := g++
.PHONY: default all testmake debug release clean dirs
default: debug
all: dirs clean debug release
dirs:
#mkdir -p $(OUTDIRS)
debug: $(PROG_DEBUG)
release: $(PROG_REL)
testmake:
#echo OBJFILES_REL = $(OBJFILES_REL)
#echo OBJFILES_DEBUG = $(OBJFILES_DEBUG)
#echo SRCFILES = $(SRCFILES)
#echo DEPFILES = $(DEPFILES)
clean:
rm -f $(OBJFILES_REL) $(OBJFILES_DEBUG) $(DEPFILES) $(PROG)
$(PROG_REL): $(OBJFILES_REL)
$(CC) $(OBJFILES_REL) -o $(PROG_REL)
strip $(PROG_REL)
#echo "---- created release binary ----"
$(PROG_DEBUG): $(OBJFILES_DEBUG)
$(CC) $(OBJFILES_DEBUG) -o $(PROG_DEBUG)
#echo "---- created debug binary ----"
-include $(DEPFILES)
obj/rel/%.o: src/%.cpp
$(CC) $(RELFLAGS) $(CFLAGS) -MF $(patsubst obj/rel/%.o, obj/%.d,$#) -c $< -o $#
obj/debug/%.o: src/%.cpp
$(CC) $(DBFLAGS) $(CFLAGS) -MF $(patsubst obj/debug/%.o, obj/%.d,$#) -c $< -o $#
Do NOT use CC for the C++ compiler. The standard convention is that CC is the C compiler, CXX is the C++ compiler. CFLAGS are flags for the C compiler, CXXFLAGS are flags for the C++ compiler, and CPPFLAGS are flags for the pre-processor (eg, -I or -D flags). Use LDFLAGS for -L flags to the linker, and LDLIBS (or LOADLIBES) for -l flags.
Using the standard conventions is good not just because it makes things easier for others to understand, but also because it allows you to take advantage of implicit rules. If make needs to make a .o file from a .c file and you have not provided a rule, it will use a standard rule and honor the settings of CC, CFLAGS, and CPPFLAGS. If CC is a C++ compiler, things will probably not work.

Resources