[comp.unix.questions] Make dependencies and nested include files

corbin@maxzilla.Encore.COM (10/12/89)

Does anyone have an idea of how to handle nexted include file
dependencies in make?  Given that test includes test.h, test.h
include test1.h and test1.h include test2.h.  When test2.h is
touched test.c will not get rebuilt given the follow make dependencies:

test.c:	    test.h
test.h:	    test1.h
test1.h:    test2.h

Can this be done in make or is

test.c:	    test.h test1.h test2.h

the only way it will work.

The problem with the second example is that I'm working on a product
with hundreds of files and maintaining dependencies manuallly is tedious
and error prone.  I would like to do automatic dependency generation.

I have a grep/sed/awk script that will generate the first example by
scanning all the sources (.s .c .h).  I'm just learning sed/awk and it
would probably take me weeks to figure out how to generate the second case.

Does anyone have such a tool/script that would take a list of files
and generate dependencies or any ideas on how to solve the problem?


Stephen Corbin
{bu-cs,decvax,necntc,talcott}!encore!corbin             corbin@encore.COM
Stephen Corbin
{bu-cs,decvax,necntc,talcott}!encore!corbin             corbin@encore.COM

erikb@cs.vu.nl (Erik Baalbergen) (10/12/89)

In article <10115@encore.Encore.COM> corbin@maxzilla.UUCP () writes:
>
>Can this be done in make or is
>
>test.c:	    test.h test1.h test2.h
>
>the only way it will work.
In fact, it's
test.o:	test.h test1.h test2.h

>
>The problem with the second example is that I'm working on a product
>with hundreds of files and maintaining dependencies manuallly is tedious
>and error prone.  I would like to do automatic dependency generation.
Lots of "mkdep"s, "makedep"s, and "makedependencies"s are floating around in
the world.  There may be one on your system.
Some cc's are equiped with a "-M" option.  "cc -M f.c" then produces
make dependency lines in the right format.  (-M causes cpp to print out
the names of the files which are included.)

The "make-dependencies" or "cc -M" can then be used from within the Makefile:

-- start Makefile --
depend:
	sed '/^#THE FOLLOWING LINES ARE GENERATED/,$$d' Makefile > Makefile.new
	echo '#THE FOLLOWING LINES ARE GENERATED' >> Makefile.new
	$(MKDEP) $(CSRC) >> Makefile.new
	mv Makefile.new Makefile
#THE FOLLOWING LINES ARE GENERATED
# don't put any hand-written stuff here
-- end Makefile --

The command "make depend" will create a new Makefile with the dependencies 
appearing after the "#THE FOLLOWING LINES ARE GENERATED" line.

Erik Baalbergen (erikb@cs.vu.nl)
-- 
Erik H. Baalbergen				    <erikb@cs.vu.nl>
Vrije Universiteit / Dept. of Maths. & Comp. Sc.
De Boelelaan 1081
1081 HV Amsterdam / The Netherlands		tel. +31 20 548 8080

cpcahil@virtech.UUCP (Conor P. Cahill) (10/12/89)

In article <10115@encore.Encore.COM>, corbin@maxzilla.Encore.COM writes:
> Does anyone have an idea of how to handle nexted include file
> dependencies in make?  Given that test includes test.h, test.h
> include test1.h and test1.h include test2.h.  When test2.h is
> touched test.c will not get rebuilt given the follow make dependencies:
> 
> test.c:	    test.h
> test.h:	    test1.h
> test1.h:    test2.h
> 
> Can this be done in make or is
> 
> test.c:	    test.h test1.h test2.h

There is a problem in both of these dependencies.  A "dependency" is an 
item that is required to build the target.  The *.h files are not dependencies
for the .c file.  The *.h files and the .c file are dependencies for the 
.o file (or executable, if there are no other .c files and you set up the 
makefile appropriatly).

The first example has one include file dependent upon the other, but that is 
not a true dependency since test1.h is not needed to build test.h (it is
necessary to interpret/process test.h, but that is in the build step for the
object file, not the include file)

> I have a grep/sed/awk script that will generate the first example by
> scanning all the sources (.s .c .h).  I'm just learning sed/awk and it
> would probably take me weeks to figure out how to generate the second case.

2 points.  

	1. are you just hard coding the nested include, or are you actually
	   parsing the files and only including the needed files.  For example
	   if your program runs int:

		#ifdef USE_SGTTY
		#include <sgtty.h>
		#else
		#include <termio.h>
		#endif

	   do you properly interpret this as a single include of the
	   appropriate file?

	2. The imake program source (on the X11 release tape) does include
	   a shell (I think) that uses cpp to parse the file and then scans
	   the output to properly generate the dependency list.  There probably
	   are other PD/Shareware/FSF software to do the same.
		


-- 
+-----------------------------------------------------------------------+
| Conor P. Cahill     uunet!virtech!cpcahil      	703-430-9247	!
| Virtual Technologies Inc.,    P. O. Box 876,   Sterling, VA 22170     |
+-----------------------------------------------------------------------+

chris@mimsy.UUCP (Chris Torek) (10/13/89)

In article <10115@encore.Encore.COM> corbin@maxzilla.Encore.COM writes:
>Does anyone have an idea of how to handle nexted include file
>dependencies in make?  Given that test includes test.h, test.h
>include test1.h and test1.h include test2.h.  When test2.h is
>touched test.c will not get rebuilt given the follow make dependencies:
>
>test.c:    test.h
>test.h:    test1.h
>test1.h:    test2.h

Of course not---because this list is wrong.  test.h does *not* depend
on test1.h, and test1.h does *not* depend on test2.h.  To see this,
think about what you must do if you change test2.h.  You do *not* need
to apply some command sequence to rebuild test1.h.  You do, however,
have to apply some command sequence to build things that include any of
test.h, test1.h, or test2.h.

Now, if test.c included `tconc.h', and tconc.h were to be built by
concatenating test.h and t12conc.h, and t12conc.h were to be built by
concatenating test1.h and test2.h, you could correctly write:

	test.c: tconc.h
	tconc.h: test.h t12conc.h
		cat test.h t12conc.h > $@
	t12conc.h: test1.h test2.h
		cat test1.h test2.h > $@

Now if you change test2.h, you really *do* have to apply some command
sequence to build t12conc.h, and once you have done that, you again
really do have to apply some command sequence to build tconc.h, and
then again apply some command sequence to build test.o.

>Can this be done in make or is
>
>test.c:	    test.h test1.h test2.h
>
>the only way it will work.
>
>The problem with the second example is that I'm working on a product
>with hundreds of files and maintaining dependencies manuallly is tedious
>and error prone.  I would like to do automatic dependency generation.

If you have `mkdep', this is easy.  If not, build mkdep.  There are
several ways to build it:

(best way) apply a C preprocessor to the source code; have it
	print a list of files included by each source file.  On
	reasonably current BSD releases, `cc -M' does this.  (More
	current releases have `mkdep'.)

(next best way) apply the C preprocessor to the source code.  Extract
	`#line' directives from the result, and build a list of files
	included by each source file.  See shell script below.

(not as good) use grep to extract all `#include' lines.  Transform
	file names to path names.  Recursively extract `#include' lines
	from these paths.  This goofs on `#ifdef/#include/#endif'
	sequences, e.g.

Here are the shell scripts.

cat >getdep.sh <<'END'
#! /bin/sh
# getdep - get dependency lists.

# find cpp
cpp=unknown
for where in /lib /usr/lib /bin /usr/bin; do
	if test -f $where/cpp; then cpp=$where/cpp; break; fi
done
if test $cpp = unknown; then
	echo "I cannot find cpp, sorry" 1>&2; exit 1
fi

# handle arguments
incl=
for i
do
	case "$i" in
	-I*)
		incl="$incl $i";;

	# you may have to add more cases below
	-O|-c|-M|-D*|-U*)
		;;

	*)
		# assume source file
		# put '$dep' in front of dependencies
		dep=`echo "$i" | sed -e 's,/,\\\\/,g' -e 's/\.c$/.o/'`
		# Find includes, remove leading numerics, remove ./,
		# remove double quotes, and remove trailing numerics.
		# Sort that, discarding duplicates, and add '$dep'.
		$cpp $incl "$i" | sed -e '
			/^#/!d
			s/# [0-9]* //
			s,"./,",
			s/"\(.*\)"/\1/
			s/ [ 0-9]*$//' |
		sort -u | sed -e "s/^/$dep: /";;
	esac
done
END

cat >mkdep.sh <<'ENDEND'
#! /bin/sh -
#
# Copyright (c) 1987 Regents of the University of California.
# All rights reserved.
#
# Redistribution and use in source and binary forms are permitted
# provided that the above copyright notice and this paragraph are
# duplicated in all such forms and that any documentation,
# advertising materials, and other materials related to such
# distribution and use acknowledge that the software was developed
# by the University of California, Berkeley.  The name of the
# University may not be used to endorse or promote products derived
# from this software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
# WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
#	@(#)mkdep.sh	5.13 (Berkeley) 8/19/88
#
#	modified at Univ of Maryland
#
PATH=/bin:/usr/bin:/usr/ucb
export PATH

D=.depend			# default dependency file is .depend

while :
	do case "$1" in
		# -f allows you to select a makefile name
		-f)
			D=$2
			shift; shift ;;

		# the -p flag produces "program: program.c" style dependencies
		# so .o's don't get produced
		-p)
			SED='s;\.o;;'
			shift ;;
		*)
			break ;;
	esac
done

if [ $# = 0 ] ; then
	echo 'usage: mkdep [-p] [-f depend_file] [cc_flags] file ...'
	exit 1
fi

TMP=/tmp/mkdep$$

trap 'rm -f $TMP ; exit 1' 1 2 3 13 15

rm -f $TMP
case $D in
.depend) style=new;;
*)	if grep -s "DO NOT DELETE THIS LINE" $D; then
		style=old
	elif [ -f $D -a ! -w $D ]; then
		echo "mkdep: no writeable file \"$D\""
		exit 1
	fi;;
esac

exec >$TMP
case $style in
old)
	cp $D $D.bak

	sed -e '/DO NOT DELETE THIS LINE/,$d' < $D
	cat << _EOF_
# DO NOT DELETE THIS LINE -- mkdep uses it.
# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY.
_EOF_
esac

# If your compiler has -M, use it here instead of getdep.sh.
# cc -M $* |
/usr/local/lib/getdep $* |
sed "
	s; \./; ;g
	$SED" |
awk '{
	if ($1 != prev) {
		if (rec != "")
			print rec;
		rec = $0;
		prev = $1;
	}
	else {
		if (length(rec $2) > 78) {
			print rec;
			rec = $0;
		}
		else
			rec = rec " " $2
	}
}
END {
	print rec
}'

case $style in
old)
	cat << _EOF_

# IF YOU PUT ANYTHING HERE IT WILL GO AWAY
_EOF_
esac

# copy to preserve permissions
cp $TMP $D
rm -f $TMP
exit 0
ENDEND
cat >make.sh <<'END'
#! /bin/sh

if [ -f .depend ]; then
	if [ -f makefile ]; then
		f="-f makefile -f .depend"
	else
		f="-f Makefile -f .depend"
	fi
	for i do
		case "$i" in -f*) f=;; esac
	done
else
	f=
fi

exec /bin/make $f ${1+"$@"} MACHINE=${MACHINE-`machine`}
END
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

gregg@cbnewsc.ATT.COM (gregg.g.wonderly) (10/13/89)

From article <10115@encore.Encore.COM>, by corbin@maxzilla.Encore.COM:
> Does anyone have such a tool/script that would take a list of files
> and generate dependencies or any ideas on how to solve the problem?

An easy way to do this is to make use of "cc -E" to get the listing with
the filenames on the "# line" lines.  Through the use of the following
pipe line of commands, you can get the names of all of the files that a
file includes without having to do recursive greps.

	defs="all -Dthis and -Dthat required"
	ifiles="all -Idir1 -Idir2 required"
	file="input .c file"

	cc -E $defs $ifiles $file | \
		grep '^#' | \
		sed '/^#ident/d' | 
		sed "1,\$s/^[^\"]*\"//;s/\"\$//;/`basename $file`/d;/y.tab.c/d" | \
		sort -u

The output will be the absolute path names and relative path names of
ALL included files relative to the current directory and/or the -I
directory names (relative or absolute).  "cc -E" works on most UN*Xs,
if it doesn't on yours, you will need to find an alternative way.  This
has worked for me for quite some time...

-- 
-----
gregg.g.wonderly@att.com   (AT&T bell laboratories)

chris@mimsy.UUCP (Chris Torek) (10/13/89)

In article <20139@mimsy.UUCP> I wrote:
>Now, if test.c included `tconc.h', and tconc.h were to be built by
>concatenating test.h and t12conc.h, and t12conc.h were to be built by
>concatenating test1.h and test2.h, you could correctly write:
>
>	test.c: tconc.h
>	tconc.h: test.h t12conc.h
>		cat test.h t12conc.h > $@
>	t12conc.h: test1.h test2.h
>		cat test1.h test2.h > $@

Oops, I completely missed the fact that it is not test.c, but rather
test.o, which depends on (is built from the changed version of) tconc.h,
so the first line should read

	test.o: tconc.h

Implicit rules add implicit dependencies, so that

	test.o: test.c

is not actually required, unless there is an explicit action below,
such as

		cc -DFOO ${CFLAGS} -c $@.c
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris