[gnu.utils.bug] Question on GNU Make 3.57

rfg@paris.ics.uci.edu (Ronald Guilmette) (02/24/90)

A question on GNU Make (3.57):

In the following GNU Makefile, I am trying to solve a very old problem
which affects C programmers but which is much worse for C++ programmers.

Basically, when I write C++ classes, I generally try to put only one class
definition into each .H file.  (I use the .H suffix for C++ header files
and I use the .C suffix for C++ "base" files.)

Anyway, for each class declaration which appears in a .H file, I like to
put the corresponding class member function definitions into the
corresponding .C file.

The problem is that this makes editing harder than it has to be.  It would
be nicer if the class declarations and the corresponding member function
definitions could be kept in the same single file.  That way, the class
declaration is easily visible while you are working on editing the
class member functions.

I figured out a tricky way to make this possible (using GNU Make) and it
mostly works except that there is one little problem.  Each time I run
the following Makefile, the final link step gets done again (whether it
needs to be done again or not).

I can't figure out why this is happening.  My guess is that the general
rule for updating .H files makes GNU make think that the .H files have
actually changed (even though they may not have).  I believe that this
in turn makes GNU make think that the .o files that depend on those .H
file have been updated (even though they do not get updated) and then that
in turn makes GNU make think that it is necessary to do the final link step
again.

Anyway, this is all just guesswork on my part.  I would appreciate it if
someone could look at this and tell me what I am doing wrong.

The fundamental symptom is that if you do a complete make (with the following
Makefile) and then immediately do a make again, the link step gets repeated
(unnecessarily).

A few comments on the procedure involved here.  First, I am generating .H
files from .C files by having `sed' slice out just those lines in the .C
files which come before my special delimiter string `####'.  Second, in
order to avoid unnecessary recompilations, I create a time-stamp file
(with a .t suffix) each time the contents of a .H file is checked against
(and possibly updated from) the corresponding .C file.  This checking
and/or updating of .H files is setup to only occur when a corresponding
.C file has changed since the last check/update.

This technique is similar to the method used in the GCC Makefile (where
stamp-* files are used and where there is a script called move-if-changed.)

The (short) example files I have been working with are also provided below.

P.S.  This Makefile is using the "rule chaining" feature of GNU Make.
This is a nice feature.  It is impossible for me to play these games with
System V make.

// rfg

Makefile:
------------------------------------------------------------------------------
SHELL = /bin/sh

#CPLUSPLUS= g++
CPLUSPLUS= CC
CPLUSPLUS_FLAGS = -g # -O

SOURCES= \
	one.C \
	two.C \
	three.C

OBJECTS= \
	one.o \
	two.o \
	three.o

.PRECIOUS:	%.t

all:	$(OBJECTS)
	$(CPLUSPLUS) $(LDFLAGS) -o ao $(OBJECTS)

%.o:	%.C
	$(CPLUSPLUS) $(CPLUSPLUS_FLAGS) -c $<

%.t:	%.C
	sed '/####/,$$d' $< > $*.H+
	if cmp -s $*.H+ $*.H; then \
		rm -f $*.H+; \
	else \
		rm -f $*.H; \
		mv $*.H+ $*.H; \
	fi
	touch $@@

%.H:	%.t
	@true

three.o:	two.H one.H
two.o:		one.H
------------------------------------------------------------------------------
one.C:
------------------------------------------------------------------------------
class one {
public:
  one ();
};

//####

one::one ()
{
}
------------------------------------------------------------------------------
two.C:
------------------------------------------------------------------------------
#include "one.H"

class two : public one {
public:
  two ();
};

//####

two::two ()
{
}
------------------------------------------------------------------------------
three.C:
------------------------------------------------------------------------------
#include "two.H"

class three : public two {
public:
  three ();
};

//####

three::three ()
{
}

main () { return 0; }
------------------------------------------------------------------------------