Justin Banks
Justin Banks

Reputation: 1

GNU Makefile deps across three directories

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"

Upvotes: 0

Views: 51

Answers (1)

John Bollinger
John Bollinger

Reputation: 181824

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/Foo does not convey its indirect dependencies on path/to/foo.c and path/to/bar.c, then .out/Foo will 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/Foo to convey its indirect dependencies on path/to/foo.c and path/to/bar.c without 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.a to be rebuilt under the circumstances then we gain no benefit from dependencies on path/to/foo.c and path/to/bar.c. If there is not such a reason then make will run the recipe for .out/Foo without 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.

Upvotes: 1

Related Questions