Make: How can I auto generate rules to build targets? - makefile

The question is a little vague, because I'm not entirely sure of the best way to ask what I'm trying to achieve in such a short summary.
To explain it best this is what I currently have....
common.mk
DESTDIR = ../../install/
tools.mk
CC = gcc
CFLAGS = -fPIC -Wall -Wextra -O2 -g -I.
LDFLAGS = -shared
RM = rm -f
MAKEDIR = mkdir -p
Makefile
include ../../builder/tools.mk
include ../../builder/common.mk
TEST_RUNNERS = test_foo test_bar
test_foo_TESTS = tests/test_foo.c
test_foo_SOURCES = foo.c
test_bar_TESTS = tests/test_bar.c
test_bar_SOURCES = bar.c
# Gather lists of ALL sources and objects required to build test_foo
test_foo_ALL_SOURCES = $(test_foo_TESTS) $(test_foo_SOURCES)
test_foo_ALL_OBJECTS = $(test_foo_ALL_SOURCES:.c=.o)
# Compile All the sources required for test_foo
$(test_foo_ALL_SOURCES:.c=.d):%.d:%.c
$(CC) $(CFLAGS) -MM $< >$#
include $(test_foo_ALL_SOURCES:.c=.d)
# Build test_foo and clean up temporary build files
test_foo: $(test_foo_ALL_OBJECTS)
$(CC) -L$(DESTDIR) -o $(strip $(DESTDIR))$(strip $#) $^
-${RM} ${test_foo_ALL_OBJECTS} ${test_foo_ALL_SOURCES:.c=.d}
# Gather lists of ALL sources and objects required to build test_bar
test_bar_ALL_SOURCES = $(test_bar_TESTS) $(test_bar_SOURCES)
test_bar_ALL_OBJECTS = $(test_bar_ALL_SOURCES:.c=.o)
# Compile All the sources required for test_bar
$(test_bar_ALL_SOURCES:.c=.d):%.d:%.c
$(CC) $(CFLAGS) -MM $< >$#
include $(test_bar_ALL_SOURCES:.c=.d)
# Build test_bar and clean up temporary build files
test_bar: $(test_bar_ALL_OBJECTS)
$(CC) -L$(DESTDIR) -o $(strip $(DESTDIR))$(strip $#) $^
-${RM} ${test_bar_ALL_OBJECTS} ${test_bar_ALL_SOURCES:.c=.d}
What I want to do is remove all the complexity in having to manually add rules for each target, and instead "auto-generate" these rules. It's fairly clean and simple in my own mind...
TEST_RUNNERS = test_foo test_bar
So for each TEST_RUNNER that is specified in the list, a list of SOURCES (the code under test) and a list of TESTS (the unit test sources) must be provided...
test_foo_TESTS
test_foo_SOURCES
I've been playing around with foreach but it's not the right approach, and I'm not entirely sure what I need to do to achieve my goal, so after playing around for a few hours I thought I'd try and ask some of you guys because there's some pretty clever guys here that hopefully may be able to help me!
Another idea I was playing around with was to creating templates that I could call upon to generate these rules:
$(foreach runner,$(TEST_RUNNERS),$(eval $(call COMPILE_ALL_TEST_RUNNER_SOURCES, runner)))
$(foreach runner,$(TEST_RUNNERS),$(eval $(call MAKE_TEST_RUNNER_TEMPLATE, runner)))
define COMPILE_ALL_TEST_RUNNER_SOURCES
$($(1)_ALL_SOURCES:.c=.d):%.d:%.c
$(CC) $(CFLAGS) -MM $< >$#
include $($(1)_ALL_SOURCES:.c=.d)
endef
define MAKE_TEST_RUNNER_TEMPLATE
$(1): $($(1)_ALL_OBJECTS)
$(CC) -L$(DESTDIR) -o $(strip $(DESTDIR))$(strip $#) $^
-${RM} ${$(1)_ALL_OBJECTS} ${$(1)_ALL_SOURCES:.c=.d}
endef

After spending a little more time reading the Make manual, I discovered this very useful page.
https://www.gnu.org/software/make/manual/html_node/Eval-Function.html
which has some really useful information on how I can construct my Makefile exactly how I want to. If anyone is interested, I'm using this as my basis for going forward...
TEST_RUNNERS = test_foo test_bar
test_foo_TESTS = tests/test_foo.c
test_foo_SOURCES = foo.c
test_bar_TESTS = tests/test_bar.c
test_bar_SOURCES = bar.c
.PHONY: test
test: unittest
#
# Template to create rules for each TEST_RUNNER defined within TEST_RUNNERS
#
# $(1) is the name of the test runner
#
define test_TEST_RUNNER_template
THE_$(1)_SOURCES = $$($(1)_TESTS)
.PHONY: unittest unittest_$(1)
unittest: unittest_$(1)
unittest_$(1):
#echo "?(1)=" $(1) "?#=" $$# " THE_$(1)_SOURCES=" $$(THE_$(1)_SOURCES)
endef
# Create a rule for all TEST_RUNNERS when "make test" is invoked...
$(foreach runner,$(TEST_RUNNERS),$(eval $(call test_TEST_RUNNER_template,$(runner))))

If you're willing to add one more line per target:
test_foo_TESTS = tests/test_foo.c
test_foo_SOURCES = foo.c
test_foo_ALL_OBJECTS := tests/test_foo.o foo.o
then a couple of pattern rules can handle all the rest:
%.o: %.c
$(CC) $(CFLAGS) -MMD -c $< -o $#
-include *.d tests/*.d
.PHONY: $(TEST_RUNNERS)
$(TEST_RUNNERS): test_% : $(DESTDIR)test_%
$(DESTDIR)test_%:
$(CC) -L$(DESTDIR) -o $# $^
(Trust me, this approach to dependency handling is much better than what you had. See here for a detailed explanation.)
If you don't like writing three lines per target, notice that the first two aren't actually used for anything and can be omitted.
If you are fond of the first two and really dislike the the third, yes, you can automate the process of constructing the object list, but it isn't worth the effort for this question.

Related

Makefile not executing to the end

I have the following makefile but it just executes the 1st command where it builds me the .o files and not the .so files. What am I doing wrong?
Thanks,
SHELL = /bin/sh
CC = gcc
CFLAGS = -g -Wall
LDFLAGS = -shared
TARGET = Stepper.so
SOURCES = $(shell echo ./*.c)
HEADERS = $(shell echo ./*.h)
OBJECTS = $(SOURCES:.c=.o)
LIBS = liblua523.a
PREFIX = $(DESTDIR)/usr/local
BINDIR = $(PREFIX)/bin
$(OBJECTS): $(SOURCES) $(HEADERS)
$(CC) $(CFLAGS) -c $(SOURCES) -o $(OBJECTS)
$(TARGET): $(OBJECTS)
$(CC) $(LDFLAGS) -o $(TARGET) $(OBJECTS) $(LIBS)
clean:
rm $(OBJECTS)
Unless you specify a different target on the command line, make always builds the first real target found in the makefile. In this case, the first real target is the first object file, so that's all that's built.
This is why you typically see makefiles with a first target of all or similar, which just depends on the various other targets you want built during a standard invocation of "make" with no arguments.
However, your makefile is really not right, in a number of ways. The fact that it's running it all means you actually only have one source file. As soon as you have >1 it will fail.
This:
SOURCES = $(shell echo ./*.c)
is not very efficient; you should use wildcard here:
SOURCES = $(wildcard ./*.c)
This rule:
$(OBJECTS): $(SOURCES) $(HEADERS)
$(CC) $(CFLAGS) -c $(SOURCES) -o $(OBJECTS)
Tells make, "for every object file, if any source file or any header file has changed, recompile it". Basically, it means that if you change ANYTHING in the directory, EVERYTHING will rebuild. If you want that you might as well write a shell script and not bother with make at all.
Further, the compiler will fail when you have >1 source file, as it will try to run:
gcc -g -Wall -c foo.c bar.c -o foo.o bar.o
which is not right.
You don't need to define this rule at all; make has a built-in rule which knows how to build an object file from a source file. Just replace it with this:
$(OBJECTS): $(HEADERS)
(no recipe) so make knows that the objects depend on the headers as well as the source. Note this is not ideal since all objects rebuild if any header changes but it's fine for a simple program.

Make searches dependencies in the wrong place

I'm relatively new to (GNU) Make, and find it incedibly difficult. I consider switching to SCons, but still, I'd like to understand.
I have a makefile in a folder, that contains subdirectories ./src, ./obj/[release|debug] and ./bin[release|debug]. The makefile should be able to grab the C++ sources in ./src, compile them into object files in the appropriate ./obj directory, and link these object files and put the result in the appropriate ./bin directory. Here is my makefile (edited for simplicity):
CONFIG = release
#CONFIG = debug
OBJS = Container.o
OBJDIR = obj/$(CONFIG)
BINDIR = bin/$(CONFIG)
VPATH = src $(BINDIR)
vpath %.o $(OBJDIR)
.PHONY: release
release: $(OBJS)
$(CXX) $(LXXFLAGS) -o $(BINDIR)/$# $^
Container.o: Container.cpp Container.hpp
$(CXX) -c $(CXXFLAGS) -o $(OBJDIR)/$# $<
The first time I run make, the "release" target will search for "Container.o" in the current folder, as well as in $(OBJDIR). Failing to find it, the secong target will be correctly executed, generating the object file in the correct folder. The "release" target will then execute, but the linker will complain that "Container.o" is not found...
The second time I run make, the "release" target will search for "Container.o" and find it in $(OBJDIR). The linker will then execute correctly (the path where "Container.o" has been found is prepended to the filename).
Is there a way to make it work in a single pass? It drives me crazy!
Make does have a long learning curve, and you're attempting something tricky (and which runs right into one of Make's big weaknesses, poor wildcard handling). I'm not sure that my answer will help more than confuse, but at least it will solve your specific problem.
If you want to use the CONFIG approach, this will do it:
CONFIG = release
#CONFIG = debug
OBJS = Container.o
TRUE_OBJS = $(addprefix obj/$(CONFIG)/, $(OBJS))
vpath %.cpp src
.PHONY: $(CONFIG)
$(CONFIG): bin/$(CONFIG)/$(CONFIG)
bin/$(CONFIG)/$(CONFIG): $(TRUE_OBJS)
$(CXX) $(LXXFLAGS) -o $# $^
$(TRUE_OBJS): obj/$(CONFIG)/%.o : %.cpp
$(CXX) -c $(CXXFLAGS) -o $# $<
But you can do without it (and without the chore of editing the makefile whenever you want to change configurations):
OBJS = Container.o
vpath %.cpp src
.PHONY: release debug
release: bin/release/release
debug: bin/debug/debug
bin/release/release: $(addprefix obj/release/, $(OBJS))
bin/debug/debug: $(addprefix obj/debug/, $(OBJS))
bin/release/release bin/debug/debug:
$(CXX) $(LXXFLAGS) -o $# $^
obj/release/%.o obj/debug/%.o: %.cpp
$(CXX) -c $(CXXFLAGS) -o $# $<

Makefile compiling multiple times the same sources

** Question edited **
Here's a typical Makefile template :
TARGET = my_prog # project name
CC = gcc -o
CFLAGS = -Wall
SOURCES := $(wildcard *.c)
INCLUDES := $(wildcard *.h)
OBJECTS := $(SOURCES:.c=*.o)
rm = rm -f
$(TARGET): $(OBJECTS)
#$(CC) $(TARGET) $(CFLAGS) $(SOURCES)
#echo "Compilation complete!"
clean:
#$(rm) $(TARGET) $(OBJECTS)
#echo "Cleanup complete!"
Question : why is the line 11 (#S(CC) $(TARGET) ...) still echoing when calling make ?
Answer : Because the problem is in the default rule and line 11 is fine.
** UPDATE **
I now have this Makefile
# project name
TARGET = my_prog
CC = gcc -c
CFLAGS = -Wall -I.
LINKER = gcc -o
LFLAGS = -Wall
SOURCES := $(wildcard *.c)
INCLUDES := $(wildcard *.h)
OBJECTS := $(SOURCES:.c=*.o)
rm = rm -f
$(TARGET): $(OBJECTS)
$(LINKER) $(TARGET) $(LFLAGS) $(OBJECTS)
$(OBJECTS): $(SOURCES) $(INCLUDES)
$(CC) $(CFLAGS) $(SOURCES)
clean:
$(rm) $(TARGET) $(OBJECTS)
Question : Why is $(CC) $(CFLAGS) $(SOURCES) being executed n times, where n is the number of source files ?
** UPDATE 2 **
Would this be a good way to solve this (seems to work...) ?
$(TARGET): obj
$(LINKER) $(TARGET) $(LFLAGS) $(OBJECTS)
obj: $(SOURCES) $(INCLUDES)
$(CC) $(CFLAGS) $(SOURCES)
The command $(CC) $(CFLAGS) $(SOURCES) is executed n times, because the rule is executed n times, because there are n objects to be built, because the $(TARGET) rule has that many objects as prerequisites. If you want the command to be run only once, replace all those prerequisites with a single PHONY prerequisite, whose rule executes the command.
But there's no reason to do it that way. You can just make the command more selective, so that it builds only the one object that was the actual target. That way Make doesn't waste time rebuilding the same objects over and over, and if one or two source files have been changed, Make will rebuild only the relevant objects, not all of them:
$(OBJECTS): %.o : %.c $(INCLUDES)
$(CC) $(CFLAGS) $<
This rule is conservative-- it assumes that every object depends on every header, so it will sometimes rebuild things unnecessarily. You can make it better, either by hand if you know the real dependencies or automatically with a more advanced technique.
EDIT:
Your "update 2" is a decent solution, but I would suggest you add the line
.PHONY: obj
to tell Make that there will be no file called "obj". Otherwise Make will run the obj rule every time, trying to build that file.
This still has the problem that if you change one source file, e.g. foo.c, Make will rebuild all the objects.
The $< I used above is an automatic variable. It means "the first prerequisite". So when Make tries to build foo.o, it will evaluate to foo.c.
EDIT:
Jack Kelly (curse him!) has pointed out that I am wrong about how PHONY targets work: the obj rule will always run, and so will the TARGET rule, whether any source files have changed or not. So the "update 2" method is effective, but crude.
I think the output is coming from generating the .o files, not geverating my_prog
Looks like you don't have a rule for creating the .o files, so make is using the default one.
Try putting this:
#echo "starting compilation"
on the line before your line 11 build command
And you can see that "starting compilation" is output after the gcc line.
Perhaps line 10 should read:
$(TARGET): $(SOURCES)
?

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.

In gnu make, can the prerequisites in a static pattern rule have different suffixes

Our make file compiles .c source files with a static pattern rule like this:
OBJECTS = foo.o bar.o baz.o
$(OBJECTS): %.o: %.c
$(CC) $< $(C_OPTIONS) -c -o $#
I need to change one of the .c files to an Objective-C .m file. Invoking the compiler is the same for both source types, so I'd like to use the same rule and just tweak it to be more flexible. I'd rather not change the OPTIONS variable because it's also used for the linking step, etc.
Is there a way to make the rule above more flexible to accommodate both .c and .m files?
Thanks
We can add this either-or behavior to the list of things Make should be able to do easily, but isn't. Here's a way to do it, using "eval" to create a seperate rule for each object.
define RULE_template
$(1): $(wildcard $(basename $(1)).[cm])
endef
OBJECTS = foo.o bar.o baz.o
$(foreach obj,$(OBJECTS),$(eval $(call RULE_template,$(obj))))
$(OBJECTS):
$(CC) $&lt $(C_OPTIONS) -c -o $#
Note that this depends on the source files already existing before you run Make (foo.c or foo.m, but not both). If you're generating those sources in the same step, this won't work.
Here's a less clever, more robust method.
CPP_OBJECTS = foo.o bar.o
OBJECTIVE_OBJECTS = baz.o
OBJECTS = $(CPP_OBJECTS) $(OBJECTIVE_OBJECTS)
$(CPP_OBJECTS): %.o: %.c
$(OBJECTIVE_OBJECTS): %.o: %.m
$(OBJECTS):
$(CC) $&lt $(C_OPTIONS) -c -o $#
EDIT: corrected OBJECTS assignment, thanks to Jonathan Leffler.
Not really just copy to
$(OBJECTS): %.o: %.m
$(CC) $< $(C_OPTIONS) -c -o $#
The call to the same compiler is just a happy occasion. Normally you do not compile objective-c code with $(CC). That just feels strange.
But since you go in a harsh way, I won't post do-it-right solution, where you separate objective-C targets from C targets into two different $(OBJECTS)-like variables and make two rules (which you should really do). Too boring. Instead, take a hack!
OBJC_FILES:=$(subst $(wildcard *.m))
real_name = `(test -h $(1) && readlink $(1) ) || echo $(1)`
$(OBJECTS): %.o: %.c
$(GCC) $< $(C_OPTIONS) -c -o $(call real_name,$#)
$(OBJC_FILES): %.c: %.m
ln -s $< $#
And God help those who maintains it!
Btw, this obviously won't work if your m-files are generated.

Resources