help-make
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Recursive Make Considered Harmful (was: Re: executing a rule prior t


From: Philip Guenther
Subject: Re: Recursive Make Considered Harmful (was: Re: executing a rule prior to any targets?)
Date: Sun, 20 Oct 2002 15:01:38 -0700

address@hidden (gk) writes:
>At 01:57 AM 10/20/2002 -0700, Philip Guenther wrote:
>>I don't know what Paul was referring to in the manual, but I will
>>suggest that you read, if you haven't already, the "Recursive Make
>>Considered Harmful" paper by Peter Miller:
>>
>>http://owlsoft.ne.client2.attbi.com/build/rmch/recu-make-cons-harm.html
>
>
>Thank you very much - excellent discussion. I am thoroughly convinced this 
>is the way to go:
>make should only ever be run from the top level directory, using a single 
>makefile (which includes all others, in subdirectories).
>
>Fortunately, my current system is not far from the goal Miller describes:
>I already have all makefiles in subdirectories being auto-generated from 
>top level rules and included by the top level makefile.
>
>The key change I need to make is that all generated dependencies use 
>filename paths relative from the TOP level directory (or absolute paths).

Right.  The makefiles I worked up refer to all files relative to
a variable called TOP.  When building from the top of the project,
TOP is just ".", while when building from a subdirectory, the
GNUmakefile in the directory just sets TOP and includes the real
top-level makefile.  I then ended up with the following chunk of
Makefile variables to handle the dependency generation part of this:

#
# The DEPEND flag should tell the compiler to put the dependencies
# for the given file into $(basename $@).${DSUFFIX}
#       These are overriden when using a compiler other than gcc
DEPEND  = -Wp,-MMD,$(basename $@).d
DSUFFIX = d

#
# We use a suffix of 'P' for holding the dependency info post-munging
# (that's the file included by make).  Some paranoia
ifeq (${DSUFFIX},P)
$(error DSUFFIX == 'P'.  Rewrite the depend stuff to avoid the conflict!)
endif


#
# We want all files names in .P files to be explicitly relative to
# ${TOP} so that the .P files make sense no matter what the current
# directory is.  We're going to do the substitution via sed, so we
# need a version of ${TOP} that's safe to use in a sed s!!! command.
# That's TOPregexp: we put a backslash in front of every period,
# bang, and backslash in ${TOP}.  replaceTOP contains the desired
# arguments for sed to the the substitution, both ${TOP} appears
# at the beginning of a line and when it appears after a space or tab.
# If ${TOP} == "." then leading "./" may have been omitted, so add
# two more substitutions to handle that case.
#
TOPregexp       := $(subst .,\.,$(subst !,\!,$(subst \,\\,${TOP}/)))
replaceTOP      := -e 's!^${TOPregexp}!$${TOP}/!' \
                   -e 's![      ]${TOPregexp}! $${TOP}/!g'
ifeq (${TOP},.)
replaceTOP      += -e 's!^\([^$$\\:/    ]\)!$${TOP}/\1!' \
                   -e 's![      ]\([^$$\\:/     ]\)! $${TOP}/\1!g'
endif

#
# Various command strings
#       DEPEND_MUNGE generates a .P file from a .d file
#               (this is a helper command for DEPENDc and COMPILEc)
#       DEPENDc generates a .P file from a .c files
#       COMPILEc generates both a .o and a .P file from a .c file
DEPEND_MUNGE    = dfile=$(basename $@).${DSUFFIX} Pfile=$(basename $@).P ; \
                  rm -f $$Pfile ; \
                  sed -e '1s:^:$(dir $@):' \
                        ${replaceTOP} < $$dfile >$$Pfile ; \
                  sed -e 's/\#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
                        -e '/^$$/d' -e 's/$$/ :/' \
                        ${replaceTOP} < $$dfile >>$$Pfile ; \
                  rm -f $$dfile

# Suppress the dependency related bits from the output seen by the user
#       The $(firstword) call was necessary because there were a couple
#       .c files that #included other .c files, so that you ended up
#       with more than .c file in the dependency list.
define COMPILEc
        @ echo ${CC} -c -o $@ ${ALL_CPPFLAGS} ${ALL_CFLAGS} \
                $(firstword $(filter %.c,$^))
        @ ${CC} -c -o $@ ${ALL_CPPFLAGS} ${ALL_CFLAGS} ${DEPEND} \
                $(firstword $(filter %.c,$^))
        @ ${DEPEND_MUNGE}
endef


This results in .P files that look like:

${TOP}/obj.platform/util/foo/bar.o: \
 ${TOP}/util/foo/bar.c \
 ${TOP}/util/foo/bar.h \
 ${TOP}/util/baz/another.h
 ${TOP}/util/foo/bar.c :
 ${TOP}/util/foo/bar.h :
 ${TOP}/util/baz/another.h :

(If you don't understand why those last three lines are added, check out
Paul's page on advanced auto-dependency generation at
        http://make.paulandlesley.org/autodep.html
)


>As a convenience, my system will automatically cd to top level project 
>directory if 'make' command is issued from any subdirectory.

As mentioned above, instead of cd'ing and reinvoking make, I just
set a variable and include the top-level makefile.  For example,
here's what the GNUmakefile in the 'sys_utils' directory looks
like:

        THIS := sys_utils
        TOP := $(patsubst %/${THIS},%,${CURDIR})
        include ${TOP}/GNUmakefile

The top line is the only thing that differs among the subdirectory
GNUmakefiles.  (The 'THIS' variable is used to change the default
target to only include the files 'below' where the make was invoked.)


Philip Guenther




reply via email to

[Prev in Thread] Current Thread [Next in Thread]