I am starting to learn make and I came across making the compiler regenerate the object files prerequisites. I got it working in a dummy project but I don't quite understand when does make expand the include statements as in my case they contain the rules for making the object files.
1 CC=cc
2 CFLAGS=-Wall -MM
3
4 CFILES=add.c mul.c
5
6 all:libmath.a
7
8 libmath.a:add.o mul.o
9 ar -cvq libmath.a $^
10
11 include $(CFILES:.c=.d)
12
13 %.d:%.c
14 set -e; rm -f $#; \
15 $(CC) $(CFLAGS) $(CPPFLAGS) $< > $#.$$$$; \
16 sed 's,\($*\)\.o[ :]*,\1.o $# : ,g' < $#.$$$$ > $#; \
17 rm -f $#.$$$$
so my question is simply when does make reads/expands these include statements ? Is there a "preprocessor" for example ? or make reads it when it reaches this statement ?
Also, if there is no "preprocessor" that expands these statements before the all target starts, what happens when make starts looking for the rules of the .o files and can't find them ?
Related
I have a simple C program with two source files, and the Makefile generates dependencies automatically, as documented in section 4.14 of the GNU Make Manual:
all: main
%.d: %.c
#set -e; rm -f $#; \
$(CC) -MM $(CPPFLAGS) $< > $#.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $# : ,g' < $#.$$$$ > $#; \
rm -f $#.$$$$
include main.d
include hello.d
main: main.o hello.o
This creates files like main.d that look like so:
main.o main.d : main.c hello.h
The problem comes if I make a change to the source code (and Makefile) to remove hello.c and hello.h. Upon the next incremental rebuild, make fails:
make: *** No rule to make target `hello.h', needed by `main.d'. Stop.
The main.d file is out of date, but make cannot rebuild it since (according to the stale main.d) it still depends on the no-longer-existent hello.h.
In this situation, a clean build will succeed. How can I get the incremental build to succeed as well?
I modified the %.d recipe so that the dependencies are only considered if the file still exists. The new Makefile rule:
%.d: %.c
#set -e; rm -f $#; \
$(CC) -MM $(CPPFLAGS) $< > $#.$$$$; \
sed --in-place 's,\($*\)\.o[ :]*,\1.o $# : ,g' $#.$$$$ ; \
sed 's^: \(.*\)^: $$(foreach t,\1,$$(if $$(wildcard $$t),$$t,))^g' < $#.$$$$ > $#; \
rm -f $#.$$$$
The second sed modifies the main.d to look like:
main.o main.d : $(foreach t,main.c hello.h,$(if $(wildcard $t),$t,))
Thus if any of the dependent files should disappear, make will not complain.
One drawback to this approach is that an incremental build may succeed in situations where a clean build would fail. For example, if hello.h was deleted without properly removing the #include from main.c, then the incremental build would succeed (since it won't try to rebuild main.o) while a full build would fail.
Are there any other drawbacks to this approach? For instance, are there any cases where an incremental build would be incomplete?
This question already has answers here:
Makefile removes object files for no reason
(2 answers)
Closed 6 years ago.
I created a Makefile, but when I use it, make seems to be adding rm commands at the end for some reason.
Here's the Makefile, stripped of only the full contents of FILENAMES and TESTS: https://gist.github.com/riking/9a1dff3f1c1b36e6dbfce53e52a325ff
Edit: Here's the rules that ended up mattering.
TESTS += char_is
TESTTARGETS = $(addprefix test-, $(TESTS))
TESTBINS = $(addprefix build/, $(TESTTARGETS))
build/%.o: %.c libft.h | build
$(CC) $(CFLAGS) -c $< -o $#
test: $(TESTBINS)
for bin in $(TESTBINS); do \
echo $$bin ; \
$$bin ; \
echo ; \
done
build/test-%: build/test_%.o libft.a | build
$(CC) $(LDFLAGS) -o $# $^
When I run make re test, the output ends with this:
.....
build/test-memmove
rm build/test_ft_memcpy.o ... build/test_char_is.o
(one object file for every element of $(TESTS))
Why the heck is it deleting the object files?
The object files for the test binaries are intermediate products, because the test binaries are created using implicit rules, as opposed to the libft.a archive, which is created with an explicit rule.
Because they're intermediate products of a chain of pattern rules, they're deleted at the end of the build.
The Make manual page that talks about this is Chains of Implicit Rules.
I am working on an old makefile that contains the following snippet to generate a shared library:
lib$(LIBNAME).so.$(SOLIBREV): $(OBJS)
$(RM) $#~
#SONAME=`echo $# | sed 's/\.[^\.]*$$//'`; set -x; \
$(CC) -o ./$#~ -shared -Wl,-soname,$$SONAME $(OBJS) $(SOEXTRALIBS) -lc;
$(MV) $#~ $#
$(MV) $# lib$(LIBNAME).so
Now I need to modify that. I know that $# specifies the target but what meaning does the tilde in "$#~" have?
By the way SOLIBREV stands for so-library-revision.
It doesn't mean anything special. It's just $# followed by a literal ~. A ~ suffix on filenames is often used for temporary files, so this recipe is using a temporary file named after the target but with the extra ~ suffix.
I want to do something like this, where I want to run a svn commit if a file has changed. The file has a timestamp which always changes. So if anything more than timestamp changes , then I want to commit the file.
The makefile will have something similar to this. But the If condition is not working properly. It is getting executed even when if is not satisfied. Can someone help me out what is the problem in If here.
UPDATE_STATE_FILE :
$(eval NO_LINES_CHANGES_IN_STATE = $(shell svn di STATE/build.state --diff-cmd=diff -x --normal | grep "^[<>]" | wc -l))
#echo $(NO_LINES_CHANGES_IN_STATE)
ifneq ($(strip $(NO_LINES_CHANGES_IN_STATE)), 2)
ifneq ($(strip $(NO_LINES_CHANGES_IN_STATE)), 0)
#echo $(NO_LINES_CHANGES_IN_STATE)
$(SVN) commit;
$(SVN) update;
endif
endif
You cannot mix make conditionals inside command rules. Make conditionals are like preprocessor statements in C or C++; they are handled as the file is read in, before any processing (like running rules) is performed.
If you want conditionals inside the rules you have to write the rule using shell conditionals, not make conditionals:
UPDATE_STATE_FILE :
#NO_LINES_CHANGES_IN_STATE=`svn di STATE/build.state --diff-cmd=diff -x --normal | grep "^[<>]" | wc -l`; \
echo $$NO_LINES_CHANGES_IN_STATE; \
if [ $$NO_LINES_CHANGES_IN_STATE -ne 2 ] && [ $$NO_LINES_CHANGES_IN_STATE -ne 0 ]; then \
echo $$NO_LINES_CHANGES_IN_STATE; \
$(SVN) commit; \
$(SVN) update; \
fi
After reading the answer given by #MadScientist, I came up with a different approach.
Not knowing if shell conditionals will work in any environment (windows vs. linux), I wrapped the rules inside the conditional instead of having a conditional inside the rule. i.e.
ifdef MY_FLAG
%.o: %.c
$(CC) -o $# -c $^ $(CFLAGS)
else
%.o: %.c
#$(CC) -o $# -c $^ $(CFLAGS)
endif
Good luck to anyone visiting here.
EDIT
As pointed out by James Moore in the comments, this different approach requires care and notice as to when and how variables are defined in relation to the placement of the if statement in the control flow.
I'm reading Managing Projects with GNU Make, and found this example in Chapter 2.7 - Automatic Dependency Generation. The Author says their from the GNU manual:
%.d: %c
$(CC) -M $(CPPFLAGS $< > $#.$$$$; \
sed s',\($*\)\.o[ :]*,\1.o $# : ,g' < $#.$$$$ > $#; \
rm -f $#.$$$$
However, I was able to do the same thing with this (note the sed):
-include $(subst .c,.d,$(SOURCES))
%.d: %.c
#$(CC) -M $(CPPFLAGS) $< | sed 's|:| $*.d : |' > $#;
All these lines do is generate the dependencies, then add in the *.d name. They had to change the first line from:
foo.o: bar.h foo.h fubar.h
to
foo.o foo.d : bar.h foo.h fubar.h
Mine is simpler and seems to work quite well, but I assume that the GNU folks had a reason for their sed command. Also:
Why do a redirect of the file into sed? Why not simply take it as a commond line parameter
Why not skip the intermediary file completely?
I know the guys at GNU could have thought of these too, but for some reason, went with the more complex setup. I just want to understand their reasoning, so I can do these on the fly.
Actually even the rule itself is not necessary. There is a great overview of different approaches of generating Make-style dependencies in Advanced Auto-Dependency Generation article written by Paul D. Smith.
After all, the following rule should be enough (in case of using GCC):
%.o: %.c
$(CC) $(CPPFLAGS) $(CFLAGS) -MMD -MP -o $# -c $<
-include $(SOURCES:.c=.d)
UPD.
I have also answered a similar question a bit earlier. It contains an explanation (quotation of GCC manual) of -MMD -MP options.
Addressing the question: Why do a redirect of the file into sed? If you do:
#$(CC) -M $(CPPFLAGS) $< | sed 's|:| $*.d : |' > $#;
and the compilation fails (errors out and generates no output), you will create an empty target file. When make is run again, it will see the newly created empty file and not regenerate it, leading to build errors. Using intermediate files is a common defensive strategy to avoid accidentally created an empty target.
An even simpler solution is to get rid of the sed call completely, as gcc can do everything you need directly:
%.d: %.c
$(CC) -M $(CPPFLAGS) -MF $# $< -MT "$*.o $#"