Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.1 6/24/83; site CS-Arthur Path: utzoo!linus!decvax!harpo!floyd!clyde!ihnp4!inuxc!pur-ee!CS-Mordred!CS-Arthur!svb From: svb@CS-Arthur (Stephan v. Bechtolsheim) Newsgroups: net.sources Subject: make: construction of "#include dependencies" Message-ID: <625@CS-Arthur> Date: Tue, 29-Nov-83 08:00:12 EST Article-I.D.: CS-Arthu.625 Posted: Tue Nov 29 08:00:12 1983 Date-Received: Thu, 1-Dec-83 09:03:42 EST Organization: Department of Computer Science, Purdue University Lines: 336 What follows is 1. An excerpt from the manual page provided containing the motivation 2. The manual page (use -man macros). 3. The shell script itself. I would appreciate feedback. Please drop me a short note, if you are using the program. Also let me know about improvements. Thank you. Stephan Bechtolsheim svb @ purdue ============================================================================ Maketd computes dependencies for makefiles from sources introduced through include files. It generates lines like xx.o: e.h struct.h ../h/const.h ... It makes xx.o not only dependent on all files it includes, but also recursively on all files other files include. This is achieved by running the whole source through the C preprocessor which generates information about which file is included into which file at what line. This information is extracted to generate the dependency lines. The motivation to write this program stems from the fact that make does not recognize transitive dependencies. Example: assume the following makefile: xx.o: e.h e.h: struct.h ../h/const.h A change of struct.h will not trigger a recompilation of xx.o. ============================================================================== .BE .if n \{.de Q "\\$1"\\$2 .\} .if t \{.de Q ``\\$1''\\$2 .\} .. .TH MAKETD 1 .UC 4 .SH NAME maketd \- make transitive dependencies .SH SYNOPSIS .B maketd [ option ... ] [ .I file ... ] .SH DESCRIPTION .I Maketd computes dependencies for makefiles from sources introduced through include files. It generates lines like .Q "xx.o: e.h struct.h ../h/const.h ..." . It makes xx.o not only dependent on all files it includes, but also recursively on all files other files include. .PP This is achieved by running the whole source through the C preprocessor which generates information about which file is included into which file at what line. This information is extracted to generate the dependency lines. .PP The motivation to write this program stems from the fact that make does not recognize transitive dependencies. Example: assume the following makefile: .nf xx.o: e.h e.h: struct.h ../h/const.h .fi A change of .I struct.h will .B not trigger a recompilation of xx.o. .PP The directories used in the search for include files are identical to the ones used by the C-compiler, because the C preprocessor is used. This also means that `#define's, `#ifdef's, etc. are evaluated. It may therefore be necessary to recompute the dependencies if any source has been changed, and especially if .I CFLAGS in the associated makefile has been changed. .PP A typical application in a makefile goes as follows: .nf SOURCE = a.c b.c c.c INCLUDES = \-I../include \-I../h CFLAGS = \-DPURDUE \-DBSD4.2 $(INCLUDES) maketd: maketd $(CFLAGS) $(SOURCE) # DO NOT DELETE THIS LINE \- make maketd DEPENDS ON IT .fi The generated dependencies will be inserted after the `# DO NOT DELETE...' line. Everything after this line will go away through the editing process of the makefile. The default filename for the makefile is `Makefile', which can be changed with the -m option. Before it is edited, the makefile will be saved in `Makefile.bak'. .PP Several options apply: .TP .BI \-a Normally, dependencies on files in `/usr/include' are not included \- this option also includes dependencies on those files. .TP .BI \-m file Instead of editing `Makefile', the file named .I file is edited. .TP .BI \-o directory Normally dependencies are of the form .Q "a.o: ....." . This option generates dependencies of the form .Q "\fIdirectory\fP/a.o:...." , which is useful for makefiles which store the objects in a separate subdirectory. The name of the directory must not be empty. .TP .B \-I... \-D.... \-U.... These options are identical to the same options of .IR cc (1). .TP .BI \-d Instead of editing the makefile, dependencies are written to standard output. .SH "SEE ALSO" make(1), cc(1) .SH "AUTHOR" Stephan v. Bechtolsheim (svb) ============================================================================== #! /bin/sh PATH=/usr/local/bin:/bin:/usr/bin:/usr/ucb # Author: Stephan v. Bechtolsheim # svb @ purdue # # This is a shell script which runs the C preprocessor on every # source and this way finds the transitive dependencies # of the source on all include files. # The program respects #defines and #ifdefs etc. given in the # source or on the command line. # # Options: # (a) options specific to this shell script # -a: /usr/include..... # -m: Instead of $MAKEFILE whataver follows '-m' # is used as Makefile name to be # edited. # -d: Generate dependencies only and output on # standard output. Don't edit makefile. # (b) the following options are identical to the options used # by the C preprocessor: # -I.... # -D... # -U... # (c) other options # are ignored # progname=$0 # Name of the Makefile which will be edited to add the dependencies MAKEFILE=Makefile if [ $# = 0 ] ; then cat << EOF usage: $progname [-ad] [-m] [-o] [-I...] [-D...] [-U...] ... -a: include dependencies on files in directory /usr/include -m: to specify another file to be edited as Makefile (instead of $MAKEFILE) -d: dependencies to standard output, no editing of $MAKEFILE -o: Dependencies will be of the form /a.o: .... (instead of simply a.o:.....) -I, -D, -U: as in the C preprocessor EOF exit fi TMPFILE=/tmp/mtd1$$.tmp touch $TMPFILE TMPFILE2=/tmp/mtd2$$.tmp DEPFILE=/tmp/mtd3$$.tmp touch $DEPFILE # In EDD we generate a editor script to edit Makefile EDD=/tmp/mtd4$$.tmp trap 'rm -f $TMPFILE $TMPFILE2 $DEPFILE $EDD ; exit ' 1 2 3 15 # The crucial line - everything after this line will # disappear in the Makefile and will contain the mechanically # generated dependencies. CLINE='# DO NOT DELETE THIS LINE - make maketd DEPENDS ON IT' # For the -a, -d options of this program. AOPTION=0 DOPTION=0 OBJDIR= # Collect in OPTIONS all options you want to pass on to the C preprocessor. OPTIONS= for i in $*; do case $i in # **************** # OPTION business # **************** -a) AOPTION=1 ;; -d) DOPTION=1 ;; -m*) MAKEFILE=`expr $i : '-m\(.*\)'` ;; -o*) if test $i = '-o' ; then echo "$progname: -o option requires directory name" exit 1 fi OBJDIR=`expr $i : '-o\(.*\)'` if test ! -d $OBJDIR ; then echo "$progname: -o option: \"$OBJDIR\" is no directory" exit 1 fi OBJDIR="$OBJDIR/" ;; -[I,U,D]*) OPTIONS=" $OPTIONS $i" ;; -*) echo "$progname: option \"$i\" unknown, ignored" ;; # **************** # source file name business # **************** *) SOURCE=$i if test ! -r $SOURCE ; then echo "$progname: file \"$SOURCE\" does not exist, skipped" continue fi # Run everything through the preprocessor, collect only lines # which identify that an insert step has been executed by the C preproc. # Remove from those lines all crep we don't need. # Then sort this output so we can remove duplictate lines. SOURCE_=`echo $SOURCE | sed -e 's,/,\\\/,g'` # echo "SOURCE is \"$SOURCE\", SOURCE_ is \"$SOURCE_\"" /lib/cpp $OPTIONS $SOURCE | grep '^#' | \ sed -e 's,^#[ ]*[0-9]*[ ]*,,' -e 's,",,g' \ -e "/$SOURCE_/d" | \ sort | \ awk '{ if ($0 != prev) print $0 } { prev = $0 }' \ > $TMPFILE # If there is no '-a' option, we don't need dependencies on files # on files in '/usr/include'. Remove these. if [ $AOPTION = 0 ] ; then sed -e '/\/usr\/include/d' $TMPFILE > $TMPFILE2 mv $TMPFILE2 $TMPFILE fi # Generate a nice output now...... # OBJECT: The Source filename with extension '.o' now. OBJECT=$OBJDIR`expr $SOURCE : '\(.*\)\..*'`.o # echo "Object is \"$OBJECT\"" # Now the dependencies, and it checks whether the next dependency # still fits on the same line. If not a new line is started. # Instead of './struct.h' write 'struct.h' only. awk "BEGIN {printf \"$OBJECT:\t\" ; le=length(\"$OBJECT\")+8} \ {if ((le+length(\$0))>=80) {printf \"\\ \n\t\"; le=8}} \ {printf \" %s \", \$0 } \ {le += length(\$0)+3} \ END {print} \ " $TMPFILE | \ sed -e 's,\\ ,\\,g' -e 's,//,/,g' -e 's, \./,,g' >> $DEPFILE ;; esac done # At this point the dependencies are stored in DEPFILE. if test $DOPTION = 1 ; then cat $DEPFILE rm -f $DEPFILE $TMPFILE $TMPFILE2 exit fi # ****************************** # Now start editing the Makefile # ****************************** if test ! -w $MAKEFILE ; then echo "$progname: can't edit $MAKEFILE" exit 1; fi # We now append $CLINE to the Makefile - this way we make sure # that editing the Makefile does not fail below. If the Makefile # already had such a line it it, it does not matter - the line # will simply go away later. echo '' >> $MAKEFILE echo "$CLINE" >> $MAKEFILE # Build the editor script to edit the Makefile later. echo "/$CLINE/,\$d" >> $EDD echo "\$a" >> $EDD echo $CLINE >> $EDD echo '# Dependencies generated at: '`date` >> $EDD echo '' >> $EDD cat $DEPFILE >> $EDD echo '' >> $EDD echo '# DO NOT ADD ANYTHING HERE - WILL GO AWAY' >> $EDD echo '.' >> $EDD echo 'w' >> $EDD echo 'q' >> $EDD # Editor scipt done, edit makefile now. cp $MAKEFILE $MAKEFILE.bak ed - $MAKEFILE < $EDD rm -f $TMPFILE $TMPFILE2 $DEPFILE $EDD ==============================================================================