Recursive #includes

Leo de Wit leo at philmds.UUCP
Thu Mar 2 21:40:30 AEST 1989


In article <9736 at smoke.BRL.MIL> gwyn at brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
|In article <964 at philmds.UUCP> leo at philmds.UUCP (Leo de Wit) writes:
|>In article <9727 at smoke.BRL.MIL> gwyn at brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
|>|It's easy to get the Makefile correct; just declare that a header
|>|depends on the others that it #includes.
|>This is not correct, as it is never correct to declare make dependencies
|>between source files (this includes header files as well).
|
|Sorry, but it IS technically correct.  Dependence is a transitive
|property.  The only practical problem you could run into here occurs
|when your version of "make" barfs on cyclic dependencies caused by
|mutually-dependent headers.  If you don't have recursion among the
|headers, though, you should be safe.

Not so. You always have a problem. If I may quote from S.I.Feldman's
'Make - A Program for Maintaining Computer Programs':

(section Description Files and Substitutions)

"  A dependency line may have either a single or a double colon. A target
"  name may appear on more than one dependency line, but all of those
"  lines must be of the same (single or double colon) type.
"  
"  1.    For the usual single-colon case, at most one of these dependency
"        lines may have a command sequence associated with it. If the
"        target is out of date with any of the dependents on any of the
"        lines, and a command sequence is specified (even a null one
"        following a semicolon or a tab), it is executed; otherwise a
"        default creation rule may be invoked.
"  
"  2.    In the double-colon case, a command sequence may be associated
"        with each dependency line; if the target is out of date with any
"        of the files on a particular line, the associated commands are
"        executed. A builtin-rule may also be executed. This detailed form
"        is of particular value in updating archive-type files.

Note especially the last sentence of 1.: "otherwise a default creation
rule may be invoked.". Although it might not be clear, the implicit
rules are meant here (suffix dependencies). NOT a .DEFAULT rule, which
is only invoked if a target must be made and no explicit rule has been
specified and no implicit rules can be applied (an implicit rule can be
applied when both suffixes appear in the .SUFFIXES list, the 'source
suffix' file exists, and an implicit rule has been given for this pair
of suffixes). I checked Ultrix's native make (/bin/make), its S5 make
(/bin/s5make), our local make (a System III port, /usr/local/make) and
Sun's make (from /usr/sunpro, the one that sorts out header
dependencies and has lots of fancy stuff). They all behave this way,
that is: invoking an implicit rule if a dependency 'to be updated' has
no associated command sequence.

A little demo to make things clear; because not all makes support SCCS,
I don't use .h~.h rules; instead .h files are made dependent from .x
files, and the .x file copied to the .h if needed (of course in real
a check to prevent clobbering an existing file would be added).

(demo)  Script started on Wed Mar  1 21:31:49 1989
(demo)  philmds> cat makefile
(demo)  
(demo)  a.out : main.c inc1.h
(demo)  	$(CC) main.c
(demo)  
(demo)  inc1.h : inc2.h
(demo)  
(demo)  .SUFFIXES : .h .x
(demo)  
(demo)  .x.h :
(demo)  	cp $< $*.h
(demo)  
(demo)  philmds> cat main.c
(demo)  #include "inc1.h"
(demo)  
(demo)  main(){}
(demo)  philmds> cat inc1.h
(demo)  #include "inc2.h"
(demo)  philmds> cat inc2.h
(demo)  /* Nothing in here */
(demo)  philmds> cp inc1.h inc1.x
(demo)  philmds> /bin/make
(demo)  cp inc1.x inc1.h
(demo)  /bin/cc main.c
(demo)  philmds> touch inc2.h
(demo)  philmds> /bin/make
(demo)  cp inc1.x inc1.h
(demo)  /bin/cc main.c
(demo)  philmds> touch inc2.h
(demo)  philmds> /usr/local/make
(demo)  	cp inc1.x inc1.h
(demo)  	cc main.c
(demo)  philmds> touch inc2.h
(demo)  philmds> /bin/s5make
(demo)  	cp inc1.x inc1.h
(demo)  	cc main.c
(demo)  philmds> 
(demo)  script done on Wed Mar  1 22:02:21 1989

I suspect the reason that your approach works for instance on Suns, is
that those makes have implicit knowledge of SCCS; note it did not get
it right with the implicit rule I added myself (.x.h).

|The make/SCCS behavior you describe is clearly erroneous; a later-
|modified checked-out file should never be clobbered by retrieval
|from the archive.

True. In fact, the problem (in /usr/local/make) is in the implicit rules:

.h~.h:
	$(GET) $(GFLAGS) -p $< > $*.h

You can draw your own conclusions ... Of course it can only occur with
dependencies between sources (which I still consider incorrect), but
the clobbering is unforgivable.

|                   I think this behavior occurs because of the
|redundancy introduced in the default rules in an attempt to
|compensate for "make" not getting the logical transitivity right.

Maybe you expect too much of Make; you want a target name both to
represent a logical and a physical item. To use the demo example, the
preprocessed inc1.h is dependent of inc1.h and inc2.h, inc1.h itself is
not (inc1.h does not change if you modify inc2.h). The use of dummy
targets (I think that was what you meant by FRC?) I do not consider a
kludge.

	Leo.



More information about the Comp.lang.c mailing list