I'm trying to write an etags target in make based on the dependency information generated by gcc. Is there some way of getting at all the dependencies in one go? Or, failing that, how would I write a rule that is only executed when the "tags" target is made and that passes all the source and header files to etags? I only want to index the files actually compiled (including headers). I'm aware that I can sed around in the .d files myself but I'm trying to find a more elegant and less brittle solution.
I have the following (excerpt)
DEPFILES = $(OBJFILES:.o=.d)
%.o : %.c
#echo "Compiling $<"
$(NO_ECHO) $(CC) $(CFLAGS) -MMD -MF $(#:.o=.d) -o $# -c $<
%.o : %.S
#echo "Compiling $<"
$(NO_ECHO) $(CC) $(ASFLAGS) -MMD -MF $(#:.o=.d) -o $# -c $<
$(TARGET) : $(OBJFILES)
#echo "Linking $#"
$(NO_ECHO) $(LD) $(LDFLAGS) -o $# $(OBJFILES) $(LIBS:%=-l%)
.PHONY: clean
# Order-only dependency to make Dep/obj-directories if they are not
# present
$(OBJFILES) : | $(ALLPATHS)
$(ALLPATHS):
$(NO_ECHO) mkdir -p $(ALLPATHS)
# Depend on the makefiles
$(OBJFILES) : $(SRC_PATH)/Makefile $(MAKECFG) $(BUILDCFG)
# Include dependency files
sinclude $(DEPFILES)
Edit : The following seems to work but I'd really like to find a more elegant solution
(the double sort/uniq is just for performance).
tags : $(TARGET)
cat $(DEPFILES) | sort | uniq | sed 's/.*:.*$$//' | tr -d '\\' | tr "\n" " " | xargs -n 1 readlink -f | sort | uniq | xargs etags -a
I came here looking for an answer to the same thing as the original question, but went away as didn't think it was adequately answered and found a solution that doesn't require sed etc.
Below I assume a Makefile similar to the Makefile in the original question which generates dep files using the compiler and includes them.
For the compiling rule where the .d files are generated, I modified the compiler options to ask it to also make the tags target depend on the dependancies of the object file. I added -MQ $# -MQ tags to the options. These options explicitly tell the compiler the names of the target for the dependancies.
%.o : %.c
$(CC) $(CFLAGS) -MMD -MF $(#:.o=.d) -o $# -c $< -MQ $# -MQ tags
Now we don't need to explicitly give the tags target a list of dependencies, it will be generated when we compile and will be updated accordingly as the source files change. In my case I am using ctags and this is what the options are I used it with:
tags:
ctags $^ -o $#
The $^ variable is the list of dependancies. This will be a list of source and header files as each .d file will look something like this:
main.o tags: main.c a.h b.h
Hope that helps.
You need to create a mini tag file for each .d file and then use that to update your etags file. Note this appends stuff to the file, rather than removing and replacing, so you might need to remote the tags file occasionally (ctags has a --update option)
TAGFILES = $(OBJFILES:.o=.t)
# prune the .d files a bit to get a list of files for etags to scan
%.t: %.d
cat $< | sed stuff > $#
cat $# | etags -a $#
.PHONY: tags
tags: $(TAGFILES)
Related
Here this tutorial explains it quite beautifully and most of it works fine. The following is the final Makefile from the tutorial which assumes that you have a directory structure like the following:
root-----Makefile
|-----All source files here.
Results of compilation are to be in the root directory. The following is the Makefile:
OBJS := foo.o bar.o
# link
proggie: $(OBJS)
gcc $(OBJS) -o proggie
# pull in dependency info for *existing* .o files
-include $(OBJS:.o=.d) #NOTE THIS
%.o: %.c #NOTE THIS
gcc -c $(CFLAGS) $*.c -o $*.o
gcc -MM $(CFLAGS) $*.c > $*.d
#cp -f $*.d $*.d.tmp
#sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -1 | \
sed -e 's/^ *//' -e 's/$$/:/' >> $*.d
#rm -f $*.d.tmp
# remove compilation products
clean:
rm -f proggie *.o *.dOBJS := foo.o bar.o
I do not understand only one thing in tutorial. It says pull in dependency info for *existing* .o files and the corresponding .d files are made but how are these taken care of as no change has been made in the dependency list of the targets which still remain %.o: %.c.
In fact from what I have noticed it just does not work for me. Can anybody explain what is going on here. If this tutorial is wrong(which I highly doubt) then please mention how can we include dependency from .d files to the dependency list.
The dependency files created with gcc MM will contain rules like:
foo.o: stdio.h myinc.h # ...
and this line here includes dependency file for each object in the list:
-include $(OBJS:.o=.d)
just look at the foo.d for example.
According to this:
One file can be the target of several rules. All the prerequisites mentioned in all the rules are merged into one list of prerequisites for the target. If the target is older than any prerequisite from any rule, the recipe is executed.
So even if you have the rule %.o: %.c, the include statement imports rules that expand this rule with dependencies.
I've been trying for a while to use auto dependencies in my makefile, but i struggle a lot to make it work, as i use a more complex structure for my project than just putting every files in a root folder.
I've tried using this piece of code in my makefile :
OBJECTS_DIR = obj/
SRCS_DIR = src/
#source files list, i.e : src/foo.cpp src/bar/foo2.cpp
SOURCES = $(shell find $(SRCS_DIR) -name \*.cpp | xargs -d '\n')
#object files list, i.e : obj/foo.o obj/foo2.o
OBJECTS = $(addprefix $(OBJECTS_DIR), $(notdir $(SOURCES:.cpp=.o)))
DEPS = $(OBJECTS_DIR)make.dep
myproject : $(OBJECTS)
$(CC) $(CFLAGS) $(LDFLAGS) -o $# $^
$(OBJECTS) : $(SOURCES)
$(CC) $(CFLAGS) $(LDFLAGS) -o $# -c $(filter %/$(notdir $(#:.o=.cpp)), $(SOURCES))
$(DEPS): $(SOURCES)
$(CC) -MM $(SOURCES) > $(DEPS)
-include $(DEPS)
all : myproject
clean :
#-rm -f $(RELEASE_DIR)* $(OBJECTS_DIR)*
I would like to store all the dependencies in the obj/make.dep file. This gives me the following output (here foo includes foo2) :
foo2.o: src/bar/foo2.cpp src/bar/headers/foo2.h
foo.o: src/foo.cpp src/bar/headers/foo2.h
I would like to rename each of the object files in order to have the following output :
obj/foo2.o: src/bar/foo2.cpp src/bar/headers/foo2.h
obj/foo.o: src/foo.cpp src/bar/headers/foo2.h
I've tried with the -MT option with no result...
Furthermore, is the -include $(DEPS) enough to include the dependencies i created? Shouldn't I replace $(filter %/$(notdir $(#:.o=.cpp)), $(SOURCES)) in the OBJECT rule by the line written in the make.dep file?
Thanks in advance for your insight, please tell me if there is any other way to enhance my makefile !
What is your reasoning for storing all the dependencies in 1 file? Conventionally each object file will have it's own .d file that is created during the compile and that is what you -include. When that is the case it is much simpler to add -MT flags etc into the same command as your initial compile to get the object dir's and names correct.
I would do something more along the lines of the answer to this question:
make include directive and dependency generation with -MM
The good thing is that all the dependency generation will happen in the compile step so you're not having to make additional calls to gcc. Having all the dependencies in one file is probably OK for a small file but if you want your makefile to scale its probably best to do something like the above.
You should use a pattern rule to generate your .o files.
$(OBJECTS_DIR)/%.o : %.c
$(CC) $(CFLAGS) -c -o $# $^
You have to process the dependency file to add OBJECTS_DIR to the target path. sed makes this simple. You probably also want it to depend on the .h files.
$(DEPS): $(SOURCES) $(shell find . -name '*.h')
$(CC) -MM $(SOURCES) | sed 's#^\(.*:\)#$(OBJECTS_DIR)\1#' > $(DEPS)
I like to use the g++ -MM feature to auto-build my dependencies. The way I do this is as follows:
include $(ALLOBJ:%.o=%.d)
%.d: %.cxx
#echo making dependencies for $<
#g++ -MM $(CXXFLAGS) $< -o $#
#sed -i 's,$*\.o,& $# ,g' $#
Basically I can give this rule ALLOBJ, and it will:
convert every .o name to a .d name, and include it,
when it can't find a .d, it will create it from the .cxx file
the final line of the %.d: %.cxx rule will add the name of the .d file to the file itself, so that the dependency will be updated automatically.
The issue arises when I remove a header: the .d file still expects to find it, and make will get upset when it's not there. One solution is to replace include with -include, and to build the dependencies within the compile rule. Unfortunately this requires a dependency generation line for each compile rule, and will also ignore all other include errors (which seems risky). Is there some other simple way to auto-build dependencies which avoids this issue?
Reading the g++ manual a bit more, and thanks to #jackKelly and #Beta's responses above, I found the following solution:
include $(ALLOBJ:%.o=%.d)
%.d: %.cxx
#echo making dependencies for $<
#g++ -MM -MP -MT $*.d -MT $*.o $(CXXFLAGS) $< -o $#
To summarize the flags:
-MM: build dependencies (rather than compiling)
-MP: build 'dummy' targets for all headers. This prevents make from complaining when headers are deleted and thus can't be found.
-MT: specify the targets for the rule. This allows us to tell make the .d file depends on the headers without resorting to the ugly sed rule.
I don't believe my solution is any more correct than #Beta's solution. I tend to use multiple compile rules for C++ files in the same makefile, so having a single dependency rule for all of them is slightly cleaner (in my case) than generating the dependencies in each compile rule.
To restate my answer to the other question, I do it this way:
%.o: %.cpp
#echo making $# and dependencies for $< at the same time
#$(CC) -MD -c $(CXXFLAGS) -o $# $<
#cp $*.d $*.P
#sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < $*.P >> $*.d
#rm $*.P
-include $(ALLOBJ:%.o=%.d)
EDIT:
It... It produces the dependency file, but more cleanly and without the sed command:
%.o: %.cpp
#echo making $# and dependencies for $< at the same time
#$(CC) -c $(CXXFLAGS) -o $# $<
#$(CC) -MM -MP $(CXXFLAGS) $< -o $*.d
-include *.d
So now I have to modify the %.o rule in my own makefiles. From now on there'll be a little bit of #JackKelly in everything I compile, mocking me. Oh, this is a black day.
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 $#"
Here is a simple header file for six different programs. This Makefile used to work just fine, but then I changed the programs to include other implementation files. This Makefile needs to get changed so that if the implementation files change the files that include those implementation files get recompiled.
all: load list show add delete btree
%: %.cpp
g++ $< -g -o $#
You can use the -MM option of gcc to create dependency files, and then include those into your Makefile.
TARGETS = load list show add delete btree
all: $(TARGETS)
%: %.cpp
g++ $< -g -o $# -MM -MF $#.dd
sed "s/$#\.o:/$#:/" $#.dd > $#.d
-#rm $#.dd
DEPS=$(TARGETS:%=%.d)
-include $(DEPS)
The sed line is present to change the dependency file from load.o: load.c to load: load.c.