C + Make

Chris Torek chris at mimsy.umd.edu
Mon Sep 17 01:30:30 AEST 1990


I wrote:
>>I have two recommendations ... 1. Put the dependency-making in a
>>separate program.

In article <0949 at sheol.UUCP> throopw at sheol.UUCP (Wayne Throop) writes:
>Much as I am aware of the dangers of disagreeing with somebody as
>usually-correct as Chris, I disagree slightly here.  My experience
>says that packing all the dependency-making into a single step 
>seperated from the construction step is very bad for information
>hiding, and makes for monolithic, hard-to-maintain, hard-to-enhance
>dependency "expert systems".

Hmm, well, would you prefer `in separate programs'?  I.e., `mkdep-C'
extracts information on C source files, `mkdep-EP' extracts Extended
Pascal information (cannot do ANSI Standard Pascal since it has no
inclusion mechanism!), `mkdep-KCL' extracts Kyoto Common Lisp information,
and so forth.  In the case of the Berkeley source tree, the C-only
mkdep handles at least 95% of the job, i.e., it is `good enough'.

>In fact, Chris' second rule:
>> Do not make `mkdep' edit the makefile.  [.. instead ..]
>>       make -f Makefile -f .depend <original args>
>... obliquely illustrates part of the problem with the first rule.
>The dependency occurs in a single massive step before the "real"
>work of construction begins.  Thus, any source that is generated
>"on the fly" (like yacc or lex, but more complicated... for example
>computing a perfect hash literal array, or whatever) must be
>treated as a stylized special case in the dependency check.  The
>natural way of computing the dependencies of the output files
>using the method of an existing subcase won't work because the
>files don't exist yet.

True.  In the case of lex and yacc files, one cheats: since the output
from both these programs is a C file (which has not expanded any
`#include's---that is, if foo.y yaccs to foo.c and foo.y `#include's
foo.h, then foo.o depends on foo.c and foo.h, but foo.c does not depend
on foo.h) one adds the C files to the list of `things for mkdep' and
makes the `depend' rule depend on the .c files themselves.  I.e.:

	# Makefile for foo, built from foo.y->foo.c->foo.o and aux.c->aux.o
	CSRCS=	foo.c aux.c
	OBJS=	foo.o aux.o
	all: foo
	foo: ${OBJS}
		${CC} ${LDFLAGS} -o $@ ${OBJS}
	clean:
		rm -f ${OBJS} foo a.out core
	depend: ${CSRCS}
		mkdep ${CSRCS}

Note that foo.y need not be mentioned at all: `make' keys off its
existence, plus make's implicit rule for `.y' -> `.c'.

>Not that this problem is insoluable.  I've seen it solved.  It's just
>that the solution, while effective, seems not to have an audience. 
>
>( The basic notion of the solution is that the construction engine must
>  construct the dependency graph on the fly...  quite doable, trust me.  )

Indeed.  Unfortunately, this requires the construction engine (`make')
to have access to the actual dependency information, which means either
pushing all those rules from mkdep-* into `make' itself, or else an
incestuous relationship between the compiler(s) and `make'.  The latter
is certainly more maintainable (what else but the program that creates
an output file knows for certain what inputs were used?), and can be
quite efficient---the dependency information for any one source is correct
if and only if the corresponding output file exists, and if not that
output needs rebuilding anyway---but, unfortunately, this is MUCH more
work to add to a system that does not already have it.

(Too, it has the drawback of requiring that there be room in every
output file for the information needed by `make', or else that output
files come in pairs: foo.o and .depend.foo.o, or some such.  Which is
`better' is to some extent a matter of taste.  One can argue that every
`object' file should in fact be a directory: foo.o/text, foo.o/data,
foo.o/symbols, foo.o/debug, foo.o/depend....  Let the file system work
for you.)

In any case, it is important that a makefile---even one as minimal as a
source list (`bill of materials', as it were) for a system like the one
above---not be altered by the dependency-update step, because such a
file *is* a source file, just as much as any C program.  If you are
using a revision control system, you will not want revisions made
merely to reflect automatically-derived changes.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 405 2750)
Domain:	chris at cs.umd.edu	Path:	uunet!mimsy!chris



More information about the Comp.lang.c mailing list