I have a tree where I have something like this :
libFoo/foo.c
libFoo/bar.c
resulting in libFoo.a being placed in an .out directory at the top of the directory tree. I then have an application like so:
StandAlone/Foo/foo.c
resulting in an executable Foo in the .out directory
What I want/need is for the Makefile in StandAlone/Foo to recognize that .out/Foo is dependent on .out/libFoo.a which is dependent on libFoo/foo.c such that if libFoo/foo.c is modified, only libFoo.a and the binary Foo are compiled. I realize the tree should be flat, and this would be easy, but for historical purposes that's just not the way it is. Is there a place in the GNU Make manual that I'm missing that describes a recipe for doing this?
I've tried -MD and -MMD and all they generate is deps on include/<whatever.h>. This is useful, but definitely not what I want. What I want is described in the [how do I do this] section. I can post snippets of Makefiles that may help, but the StandAlone/Foo directory has a very simple Makefile that looks like this:
check_libraries looks like this (which is ugly, but I didn't write it). It seems as if a modification here would get me what I want, but I really have dependencies across three different directories and I'm not sure how to make that work.
PROGS=Foo
include ../../Makefile.macros
include ../../Makefile.libs
include ../Makefile.inc
check:
$(call check_libraries)
DEPENDS = .makedepend
SRC := *.c *.ec
$(DEPENDS):
@makedepend -- $(CFLAGS) -- $(SRC) $(INC_FLAGS) -f- 2>/dev/null >$(DEPENDS)
-include $(DEPENDS)
check_libraries looks like this:
define check_libraries
@for a in .obj ; do \
build_flag=0 ; \
lib="$${a}/`basename $$a`.a" ; \
if [ ! -f ".out/$$lib" ] ; then \
build_flag=1 ; \
else \
cd $$a; \
for obj in `ar -t $$lib` ; do \
fullpath_object="$${a}/$${obj}"; \
if [ ! -f "$$fullpath_object" ] ; then \
build_flag=1 ; \
fi \
done \
fi; \
if [ "$$build_flag" -eq 1 ] ; then \
echo "Building library: $$lib"; \
cd $$a; \
make; \
fi; \
done
endef
It seems that check_libraries could/should be modified to allow me to do that I want, I just can't figure out how to modify it (actually, the way I'd prefer is to have gcc -MMD be "smarter"
1 Answer 1
What I want/need is for the Makefile in StandAlone/Foo to recognize that .out/Foo is dependent on .out/libFoo.a which is dependent on libFoo/foo.c such that if libFoo/foo.c is modified, only libFoo.a and the binary Foo are compiled.
Taken in isolation, that is easy.
Taken in the context of your current build system, that probably means rewriting the current build system. Which just might be the best thing to do.
I realize the tree should be flat, and this would be easy, but for historical purposes that's just not the way it is.
The tree being flat is irrelevant. The issue appears rather to be the use of recursive make. One of the consequences (usually) is that no make process sees a complete dependency graph. Refer to the classic paper Recursive Make Considered Harmful for more information.
The layout of your source is orthogonal to the use of recursive make -- you can avoid recursive make even with a complex source tree, and you can use recursive make even with a flat tree.
Is there a place in the GNU Make manual that I'm missing that describes a recipe for doing this?
As far as make's dependency analysis goes, dependencies are a matter of rules, not recipes. In fact, they are a matter strictly of the parts of rules other than their recipes. If you want make to know that .out/Foo depends on .out/libFoo.a, then the latter should be a prerequisite of the former. If you want make to know that .out/libFoo.a depends on path/to/foo.c and path/to/bar.c then the latter should be prerequisites of the former.
If the rules for building .out/Foo and .out/libFoo.a are in separate makefiles then you have a problem, to wit:
if the makefile for
.out/Foodoes not convey its indirect dependencies onpath/to/foo.candpath/to/bar.c, then.out/Foowill not be rebuilt on account of being out of date with respect to one of those.it does not help for the makefile for
.out/Footo convey its indirect dependencies onpath/to/foo.candpath/to/bar.cwithout doing so through a rule for building.out/libFoo.a(which we supposed it doesn't), because the required mitigation in that case involves rebuilding.out/libFoo.a.If there is some other reason that causes
.out/libFoo.ato be rebuilt under the circumstances then we gain no benefit from dependencies onpath/to/foo.candpath/to/bar.c. If there is not such a reason thenmakewill run the recipe for.out/Foowithout first (re)building.out/libFoo.a, which, if it succeeds at all, gives you a result that is timestamped later than those indirect dependencies, but which is not actually up to date with respect to them.
The way that problem is typically addressed in projects using recursive make is that the sequence of recursive make calls is structured carefully, so that .out/libFoo.a is always brought up to date with respect to its dependencies before any attempt is made to bring .out/Foo up to date. Also, either way, each target has only direct dependencies for prerequisites, not indirect ones. All this typically means that it is not safe to manually run make against subdirectory makefiles, and probably not against any but a few specific targets in the top-level makefile.
1 Comment
Explore related questions
See similar questions with these tags.
-MMDetc. or makedepend? Those are used to generate prerequisite information for source files (e.g.,.c) showing the header files (e.g.,.h) that they depend on. That has nothing to do with what libraries etc. that an executable depends on (how could it, since it's a preprocessor option not a linker option?)check_librariesvariable can help with your problem, because that's a recipe that's used to build a target. What you want is to explain to make itself which targets to run. But really this entire thing is so non-make-like that it's impossible to have it work well using make. It basically re-implements make's work using a shell script instead.check_librariesabove is doing that same thing, except in a fatally limited way: it looks for files in various directories and if they don't exist it runs make. That's not well-designed because that's what make is supposed to do.