makefile compile multiple source files in one call - makefile

Imagine that I have 2 source files that both include one header file.
a.c:
#include "ab.h"
int a() {
return RETURN_VAL;
}
b.c:
#include "ab.h"
int b() {
return !RETURN_VAL;
}
ab.h:
#define RETURN_VAL (0)
I want a makefile that can compile in one call a.c and b.c. If ab.h is new or a.c and b.c are both new, then I want:
gcc -c a.c b.c
touch ab.timestamp
Otherwise I want to recompile only the out of date files.
This is a tiny example, I need something that scales. I have attempted to resolve this, but I can't figure out how.
This is an example using C, but my actual use is for compiling large-scale HDL projects. As the invocation of the HDL compilers has a high overhead, it is much better to call the tool once for many files instead of separately for each.
What I have tried are things like:
a.c: ab.h ;
b.c: ab.h ;
ab.timestamp: a.c b.c
gcc -c $?
touch ab.timestamp
This won't work, and instead I need something like this:
ab.timestamp: a.c b.c ab.h
gcc -c a.c b.c
touch ab.timestamp
This is what I don't want. I would like $? to indicate the correct files that I need to recompile. I know that I can use $(if and $(filter functions to figure this out, but I was hoping for something more seamless.
I am using GNU Make.
Thanks,
Nachum

This is a trick, but it might work:
all: ab.timestamp
SOURCES = a.c b.c ab.h
a.o: a.c ab.h
b.o: b.c ab.h
CHANGED :=
%.o: %.c ; $(eval CHANGED += $<)
ab.timestamp: $(SOURCES) $(patsubst %.c,%.o,$(filter %.c,$(SOURCES)))
gcc -c $(CHANGED)
touch $#

This is a kludge, but it seems to work. Touch the source files to bring them up to date with the header:
a.c b.c: ab.h
#touch $#
ab.timestamp: a.c b.c
gcc -c $?
touch ab.timestamp

Related

How to conditionally append to a variable in a Makefile function?

I have the following construct in the common make file which is to be included by
the module specific Makefile -
# Conditionally add Logger as a MUT dependency
define COND_ADD_LOGGER
$if($$(findstring -DLOGGER, $$(DEFINES)), SOURCES += logger.c)
endef
define CMN_TESTS_RULE
$(COND_ADD_LOGGER)
$(eval OBJECTS = $(SOURCES:.cpp=.o))
$(eval OBJECTS := $(OBJECTS:.c=.o))
$(TARGET): $(OBJECTS)
$(COMPILE_RULE_CMN)
endef
In my module's Makefile I do this -
SOURCES = a.c b.c test.cpp
TARGET = generic_tests
$(eval $(CMN_TESTS_RULE))
This isn't adding the logger.c files to the SOURCES list as I had expected.
(The DEFINES variable definitely has the sub-string "-DLOGGER" in it.)
This is the output when I use info -
$if($(findstring -DLOGGER, $(DEFINES)), SOURCES += logger.c)
generic_type_abstraction_tests: a.o b.o test.o
g++ -o $# $^ D:/TEST/gtest-1.7.0/make/gtest_main.a
$if($(findstring -DLOGGER, $(DEFINES)), SOURCES += logger.c)
generic_type_abstraction_tests: a.o b.o test.o
g++ -o $# $^ D:/TEST/gtest-1.7.0/make/gtest_main.a
I am using GNU Make version 3.81 from CYGWIN on a Windows 8 machine.
There are several things that do not make sense to me in your Makefile (especially strange use of nested eval). I suggest to fix them with something like:
# Conditionally add Logger as a MUT dependency
define COND_ADD_LOGGER
SOURCES += $(if $(findstring -DLOGGER,$(DEFINES)),logger.c)
endef
define CMN_TESTS_RULE
$(COND_ADD_LOGGER)
OBJECTS = $$(patsubst %.c,%.o,$$(patsubst %.cpp,%.o,$$(SOURCES)))
$(TARGET): $$(OBJECTS)
$(COMPILE_RULE_CMN)
endef
SOURCES = a.c b.c test.cpp
TARGET = generic_tests
$(eval $(CMN_TESTS_RULE))
.PHONY: debug
debug:
$(info SOURCES: $(SOURCES))
$(info OBJECTS: $(OBJECTS))
Demo:
$ make debug
SOURCES: a.c b.c test.cpp
OBJECTS: a.o b.o test.o
gmake: 'debug' is up to date.
$ make DEFINES=-DLOGGER debug
SOURCES: a.c b.c test.cpp logger.c
OBJECTS: a.o b.o test.o logger.o
gmake: 'debug' is up to date.

How to generate multiple executable files in one Makefile?

My directory contains 2 source files: a.c and b.c. I want to generate executable file a from a.c and b from b.c. Now I can only figure out one method to write Makefile:
all:
gcc -o a a.c
gcc -o b b.c
It seems a little awkward, is it better method?
The answers are fine, still I think you need some insight in how make works:
The basic functionality of make is to create output files from input files if necessary. make decides what is necessary by comparing timestamps: If any input file is newer than an output file created from it, the recipe for this output file is executed.
This means with just a rule named all, this rule is always executed (except when you happen to have a recent file actually called all -- to prevent this behavior, you have to list all as a .PHONY target, that is one that doesn't actually create a file). Your original Makefile is equivalent to a simple shell script, so it doesn't use make properly.
The minimal "correct" version of your Makefile should look like this:
all: a b
a: a.c
gcc -o a a.c
b: b.c
gcc -o b b.c
.PHONY: all
So, all is "phony" and depends on a and b. a is only rebuilt when a.c changed, b is only rebuilt when b.c changed.
In a real project, your programs are probably made from more than just one source file and in this case, you can really take advantage of make: Have it build object files of your translation units, so only the parts that changed are actually rebuilt. It's overkill for your tiny example, but could e.g. look like this:
a_OBJS:= a.o
b_OBJS:= b.o
all: a b
a: $(a_OBJS)
gcc -o$# $^
b: $(b_OBJS)
gcc -o$# $^
%.o: %.c
gcc -c -o$# $<
clean:
rm -f *.o
.PHONY: all clean
You would just have to add more object files to a_OBJS and b_OBJS to include new translation units in your build. The pattern rule %.o: %.c will match them. There's a lot more to discover, I suggest starting with the GNU make manual.
I think the follow method is better:
all: a b
a: a.c
gcc -o a a.c
b: b.c
gcc -o b b.c
In your version, make all will always run gcc twice, whether or not a.c and b.c are modified. In this version gcc will be run only when necessary.
Of course you can use some magic (for-loop or similar) to create the rules but I think the difference between my and your method is clear.
To me
all:
gcc -o a a.c
gcc -o b b.c
looks fine.
Or may be the following for better control
all: a b
a: a.c
gcc -o a a.c
b: b.c
gcc -o b b.c
clean:
-rm a b
A lesser known trick to compile without makefile
make a #run cc -o a a.c by make or
make b #run cc -o b b.c by make
Or to generate both a and b
make a b
make uses implicit rule here, just like magic. But prefer a makefile with rule specified

Define a choice of prerequisites in a pattern rule

For example, lets say I have a compiler that can build foo files from either bar or baz sources.
The rules for this might look like:
%.foo: %.bar
# commands to
# invoke compiler
%.foo: %.baz
# commands to
# invoke compiler
However, this could start getting a bit long and redundant as the number of input types and recipe commands increase. Is there any syntax available to compress this into a single rule?
%.foo: $(oneof %.bar %.baz)
# commands to
# invoke compiler
What you propose at the beginning is right: Makefiles should be clear and concise regarding building rules.
In the other hand you may take a look at Canned Recipes to try to avoid repeating the same recipes once and again:
define MAKE_FOO =
#You may use automatic variables such as $^ or $#.
mv $< $# #In this example just a file renaming.
endef
%.foo: %.bar
$(MAKE_FOO)
%.foo: %.baz
$(MAKE_FOO)
The canned recipe MAKE_FOO will expand to whatever recipes you write inside the define statement as if they were copied manually.
Here's an illustration for the concrete problem of making an .o file
from either a .c file or a .cpp file with a combined pattern rule.
An executable is also built to aid the illustration.
Makefile
.PHONY: all clean
all: test
%.o: %.c %.cpp
gcc -c $?
test: main.o hw.o
g++ -o $# $^
clean:
rm -f test *.o
where we have:
hw.c
#include <stdio.h>
void hw(void)
{
puts("Hello from C");
}
hw.cpp
#include <iostream>
extern "C" void hw()
{
std::cout << "Hello from C++" << std::endl;
}
and:
main.cpp
extern "C" void hw(void);
int main(void)
{
hw();
return 0;
}
Make from clean and run:
$ make clean && make && ./test
rm -f test *.o
g++ -c -o main.o main.cpp
gcc -c hw.c hw.cpp
g++ -o test main.o hw.o
Hello from C++
Both hw.c and hw.cpp were compiled per the pattern rule.
Each one of them was compiled to the same object file, hw.o, with the second, C++
compilation overwriting the C compilation. So the C++ object file was linked,
simply because it was the last to be built. Be clear about what you expect to
happen when the combined rule is triggered by multiple prerequisites.
Now let's update hw.c and repeat:
$ touch hw.c
$ make && ./test
gcc -c hw.c
g++ -o test main.o hw.o
Hello from C
This time, hw.o was compiled only from hw.c, and linked.
Update hw.cpp and repeat:
$ touch hw.cpp
make && ./test
gcc -c hw.cpp
g++ -o test main.o hw.o
Hello from C++
Once again, the hw.o from C++ was linked.
The key element of the combined pattern rule is $?, which
means all the prerequisites that are newer than the target

Given a target's name, get all of its prerequisites

If I have a makefile with e.g.:
foo.o: foo.c a.h b.h c.h
cc -c foo.c -o foo.o
Now, in some other part of the makefile, I want to get all the prerequsites of foo.o, like I'd do with $^ in the recipe. Something like:
$(info $(call GET_TARGET_PREREQS(foo.o))) # prints "foo.c a.h b.h c.h"
Basically, I have dependency files (generated by -M) for all my object files, and from there I want a list of all the header files that are included by a given object.
I'm hoping for a more or less pure make solution, and not a sed script that parses the *.d files and outputs makefile fragments.
If you want to print all prerequsites, you can always use $^
.PONY: all
all: a b c
#echo $^
a:
b:
c:

Problems with directories making a simple makefile

I want to make a simple makefile for a C project that have the following directories.
-Project
- src
- a.c
- b.c
- main.c
- headers
- a.h
- b.h
- build
- makefile
- project.exe
And here it's the makefile that I've done.
project: a.o b.o main.o
cc -o sesion0 a.o b.o main.o
a.o: ../src/a.c ../headers/a.h
b.o: ../src/b.c ../headers/b.h
main.o: ../src/main.c ../headers/a.h ../headers/b.h
But when I execute the make order, it tell's me that the file or directory a.o, b.o and main.o doesn't exist and also that there's not input files. In the end shows this error:
make: *** [project] Error 1
Does anyone know why this happen or where I have the error? I don't know very well how to manage the directories in the makefile.
Thanks.
Make has built-in rules for making x.o from x.c, but not from ../src/x.c. In other words, paths of input and output must be the same, only the file extension differs.
You can fix it by using VPATH for directory search:
VPATH = ../src:../headers
a.o: a.c a.h
b.o: b.c b.h
main.o: main.c a.h b.h

Resources