[lug] OT: Makefile oddity

D. Stimits stimits at idcomm.com
Sat Apr 20 01:31:34 MDT 2002


Tom Tromey wrote:
> 
> >>>>> ">" == D Stimits <stimits at idcomm.com> writes:
> 
> >> I keep trying to say, there is NO error message, and it DOES
> >> compile what I expect.
> 
> Ok.  I guess I misunderstood.  Remaking the Makefile is all a red
> herring.
> 
> >> Variables and everything is technically working. The problem is
> >> some oddness where a fresh build does everything right, but if any
> >> dependency is out, it tries to double rebuild all things; those
> >> that are not out of date get "target is up to date" twice (no job
> >> server running, this is all a single -j1).
> 
> Unfortunately, as you've discovered, debugging a complex make setup is
> not easy.  GNU make's debug output is much too voluminous to be really
> useful.
> 
> Can you reproduce the problem with a simpler setup?  Say with just two
> or three files?  That sort of thing makes it a lot easier to see if
> the problem is in the Makefile or in make.
> 
> Failing that try submitting a GNU make bug report.  I've found the
> maintainers to be pretty responsive.
> 
> Tom

I can eventually figure out more, but here is a basic setup. The SOURCE
type variables are in an include file though, so pretend they included
indirectly. Also, I have attempted to turn off all unused suffix rules
or default suffixes. Here is the gist of it all.

.SUFFIXES	:
.SUFFIXES	: .cpp .h .o .cxx

CXX		= g++
LINK		= g++
MAKE		= make
CXXFLAGS	= -Wall -g -D__cplusplus -D__linux__
INC		= -I/usr/include -I../..
LIBS		= -lm -lcrypt -ldl -lX11
LDFLAGS		= -L/usr/X11R6/lib
BINDIR		= ../../bin

### This block is really via include.
# NOTE: "SERVER" is one of several projects.
#       "GLOBAL" are common to all projects.
# It is desirable to ship a common package, a
# server package, a client package, and a utilities
# package, all separate.
SRCDIR_SERVER	= ../../server
SRC_SERVER	=	$(SRCDIR_SERVER)/a.cpp	\
			$(SRCDIR_SERVER)/b.cpp	\
			$(SRCDIR_SERVER)/c.cpp


SRCDIR_GLOBAL_SERVER	= ../../global
SRC_GLOBAL_SERVER	=	$(SRCDIR_GLOBAL_SERVER)/x.cpp	\
				$(SRCDIR_GLOBAL_SERVER)/y.cpp	\
				$(SRCDIR_GLOBAL_SERVER)/z.cpp

OBJDIR_SERVER	= ../../obj/server
OBJ_SERVER	=	$(OBJDIR_SERVER)/a.o	\
			$(OBJDIR_SERVER)/b.o	\
			$(OBJDIR_SERVER)/c.o

OBJDIR_GLOBAL		= ../../obj/global
OBJ_GLOBAL_SERVER	=	$(OBJDIR_GLOBAL)/x.o	\
				$(OBJDIR_GLOBAL)/y.o	\
				$(OBJDIR_GLOBAL)/z.o
### End of "include" file variables.
all: server

server	:	${BINDIR}/TheServer

${BINDIR}/TheServer:	${OBJ_GLOBAL_SERVER} ${OBJ_SERVER}
	${LINK} ${LIBS} ${LDFLAGS} -o ${BINDIR}/TheServer ${OBJ_GLOBAL_SERVER}
${OBJ_SERVER}

${OBJ_GLOBAL_SERVER}: ${SRC_GLOBAL_SERVER}
	@cd ../global ; ${MAKE} ${@}

${OBJ_SERVER}: ${SRC_SERVER}
	@cd ../server ; ${MAKE} ${@}


distclean	:	clean
	+rm -f ${BINDIR}/server

clean:
	+cd ../global ; ${MAKE} clean
	+cd ../server ; ${MAKE} clean


I've tried all kinds of variations, and it does do the right thing as
far as final output. And if it is a clean build after a distclean, then
it is even without any unexpected quirks. But if I cd to the server or
global directory, and touch one of the files, it's behavior is odd. It
still works, it still ends up with the right thing, but it tries to do
all targets twice, even the ones that were not touched. When it cd's to
the right subdirectory and tries a dual build of a target, it correctly
knows that it is out of date there, but the parent Makefile is still
trying to build *all* targets if any one of them is out of date. Now I
figured I could be making a mistake with ${@}, but I have also
explicitly set it to different values, and the cd and touch routine
always results in all targets being attempted twice. For example, it
links twice. And I am not using -j it is a single thread (using -j
doesn't actually change anything as far as dual attempts at the same
thing).

While looking at this I think and figuring out how to word it, I do see
the likely problem (writing it down tends to do that). If an object file
in OBJ_SERVER were to be tested for whether it is up to date, it would
have to continue down the chain of dependencies, find target for
${OBJ_SERVER}, and test if it is up to date by comparison to the .cpp
files of ${SRC_SERVER}. But ${OBJ_SERVER} is not one file, it is three,
and it might be considering all of ${OBJ_SERVER} out of date if any one
of the files in ${SRC_SERVER} is out of date (then it cd's to the
subproject directory and tries to build it from that Makefile which
knows the truth...it isn't really out of date except for the one that
changed). I'm thinking I will not be able to name a set of object files
as a target on the left of the '=', at least not in the same way as I
could name multiple dependencies to the right of the '='. I will wait
till tomorrow to test this, but I think if I manually change this
dependency to multiple individual rules, it might work right:
${OBJ_SERVER}: ${SRC_SERVER}
	@cd ../server ; ${MAKE} ${@}

...i.e., not using a list of object files, but 3 rules for 3 files.
Unfortunately, that would be a ridiculously long set of rules for this
project. Now I know about the OBJ = ${SRC:.cpp=.o} style macros, but
having this split into multiple directories with possibly different flag
settings for different subprojects, I am at a lost how to do that.
Possibly I can rearrange with some ${@F} and ${@D} style macros and get
away with it embedded inside of another macro (guess I'll be finding out
how make feels about multipass preprocessing).

D. Stimits, stimits at idcomm.com



More information about the LUG mailing list