richardg@elecvax.OZ (Richard Grevis) (10/07/84)
I noticed the net news regarding the construction of dependency lists, so I thought the following program would be of interest. Makemake is a (complicated) shell file which will take a list of files, and construct a Makefile to compile the files into a binary or a library. Unfortunately it only handles single targets, but the source may lie in many directories. The feature of interest in the program is 'make depend', which constructs the dependency list. What might be a tricky activity is simplified by using the C preprocessor itself; IT handles all that mucking about with nested includes in multiple directories. You also get include dependencies right back to /usr/include, which I suppose is good. Richard Grevis ...decvax!mulga!cadvax!richardg Joint Microelectronics Research Centre, University of N.S.W (Australia). ######-cut-########-the stuff below is a shell archive-######-cut-######### # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # # This archive contains: # makemake makemake.1 echo x - makemake cat > "makemake" << '//E*O*F makemake//' # # Make a template makefile for C and Pascal programs # Richard Grevis (...decvax!mulga!cadvax!richardg) # # If a single argument is given to makemake, a # Makefile template is constructed, with fields # such as OBJECTS etc left blank. The argument supplied # is the name of the program or library the makefile # is a template for; e.g. "makemake cc". If more than # one argument is given, the extra arguments will be the # files that form the source. These include C files, headers, # YACC and LEx files, but also documentation, shell files, # and READMEs etc. MAKEFILE=${MAKEFILE-Makefile} # My manually made ones are 'Makefile', won't clobber. if [ $# = 0 ] ; then echo "Usage: $0 [-l] name [files]..." echo "-l: make a library makefile." echo "name is the name of the resulting binary or library" echo "files is the complete set of source files; give all of them" exit 1 fi if [ -r $MAKEFILE ] ; then echo Old Makefile present echo Moving $MAKEFILE to $MAKEFILE.old mv ${MAKEFILE} ${MAKEFILE}.old 2>/dev/null fi # awk program to make CFILES etc strings nice for the makefile (not too long) AWKPROG='BEGIN{buf=""}{if(length(buf $1)<66)buf=buf" "$1;else{print buf,"\\";buf=" "$1}}END{print buf}' if [ $1 = '-l' ] ; then libflag=true shift else libflag=false fi name=$1 shift for i in $@ ; do case $i in *.[1-9]) MANFILES="$MANFILES $i" ;; *.mm|*.ms|*.me|*.doc) DOCFILES="$DOCFILES $i" ;; *.c) CFILES="$CFILES $i" OBJECTS="$OBJECTS `echo $i | sed -e 's/.c$/.o/'`" ;; *.f) FFILES="$FFILES $i" OBJECTS="$OBJECTS `echo $i | sed -e 's/.f$/.o/'`" ;; *.y) YACCFILES="$YACCFILES $i" OBJECTS="$OBJECTS `echo $i | sed -e 's/.y$/.o/'`" ;; *.l) LEXFILES="$LEXFILES $i" OBJECTS="$OBJECTS `echo $i | sed -e 's/.l$/.o/'`" ;; *.s) ASFILES="$ASFILES $i" OBJECTS="$OBJECTS `echo $i | sed -e 's/.s$/.o/'`" ;; *.h) HEADERS="$HEADERS $i" ;; *.i) INCLUDES="$INCLUDES $i" ;; *.sh) SHFILES="$SHFILES $i" ;; *) OTHERS="$OTHERS $i" ;; esac done # # Add to libraries as appropriate # if [ "$LEXFILES" != "" ] ; then LIBS="$LIBS -ll" fi if [ "$YACCFILES" != "" ] ; then LIBS="$LIBS -ly" fi MCC='$(CC)' if [ "$FFILES" != "" ] ; then MCC='f77' FORTRAN='.SUFFIXES: .f .f.o: f77 -c $(FFLAGS) $*.f .f: f77 $(FFLAGS) $*.f -o $@ $(LIBS) ' fi LIBS="$LIBS -lnm" # We are very lucky up to 10 blocks of arguments can be held MANFILES=`echo "$MANFILES" | awk "$AWKPROG"` DOCFILES=`echo "$DOCFILES" | awk "$AWKPROG"` HEADERS=`echo "$HEADERS" | awk "$AWKPROG"` INCLUDES=`echo "$INCLUDES" | awk "$AWKPROG"` CFILES=`echo "$CFILES" | awk "$AWKPROG"` FFILES=`echo "$FFILES" | awk "$AWKPROG"` ASFILES=`echo "$ASFILES" | awk "$AWKPROG"` LEXFILES=`echo "$LEXFILES" | awk "$AWKPROG"` YACCFILES=`echo "$YACCFILES" | awk "$AWKPROG"` OBJECTS=`echo "$OBJECTS" | awk "$AWKPROG"` SHFILES=`echo "$SHFILES" | awk "$AWKPROG"` OTHERS=`echo "$OTHERS" | awk "$AWKPROG"` # # Construct either a normal makefile or a library makefile # as appropriate # if [ $libflag = true ] ; then name="\$(CPREFIX)$name" MAIN="\$(TARGET): \$(OBJECTS) -rm \$(TARGET) \$(AR) rcv \$(TARGET) \`\$(LORDER) \$(OBJECTS) | tsort\` -\$(RANLIB) \$(TARGET)" INSTALL="cp \$(TARGET) \$(INSDIR)/\$(TARGET) chmod 644 \$(INSDIR)/\$(TARGET) -\$(RANLIB) \$(INSDIR)/\$(TARGET)" INSDIR='$(ROOTDIR)/lib' else MAIN="\$(TARGET): \$(OBJECTS) ${MCC} \$(LDFLAGS) \$(OBJECTS) -o \$(TARGET) \$(LIBS) size \$(TARGET)" INSTALL="cp \$(TARGET) \$(INSDIR)/\$(TARGET) chmod 711 \$(INSDIR)/\$(TARGET)" INSDIR='$(ROOTDIR)/bin' fi # # Start making the makefile template. This # is VERY tricky; most of the multiple sloshing # etc was done by a careful mechanical procedure # One should do the reverse to work out what it all # means, although I warn you, this is a shell file # producing a makefile which executes shell commands # that produce the dependancy lists at the end of # the makefile (itself). This is BLACK MAGIC !!! # cat > $MAKEFILE <<! MAKEFILE= $MAKEFILE CLOCALFLAGS = ROOTDIR = \$\$HOME $FORTRAN CPREFIX=$CPREFIX TARGET= $name CC = \$(CPREFIX)cc INCLUDE = /usr/local/include CFLAGS = -I\$(INCLUDE) $(CLOCALFLAGS) YFLAGS = -d FFLAGS = -onetrip -w66 -O LDFLAGS = -n LIBS = $LIBS # Directory where program is installed INSDIR = $INSDIR LINT = \$(CPREFIX)lint $(CLOCALFLAGS) -habx AR = \$(CPREFIX)ar LORDER = \$(CPREFIX)lorder RANLIB = \$(CPREFIX)ranlib HEADERS =$HEADERS INCLUDES=$INCLUDES CFILES =$CFILES FFILES =$FFILES ASFILES =$ASFILES YACCFILES=$YACCFILES LEXFILES=$LEXFILES SHFILES =$SHFILES MANFILES=$MANFILES DOCFILES=$DOCFILES OTHERS =$OTHERS OBJECTS =$OBJECTS SOURCE = \$(MAKEFILE) \$(MANFILES) \$(DOCFILES) \$(SHFILES) \$(OTHERS) \ \$(HEADERS) \$(INCLUDES) \$(YACCFILES) \$(LEXFILES) \$(CFILES) \ \$(FFILES) \$(ASFILES) LISTFILES= \$(MAKEFILE) \$(SHFILES) \$(HEADERS) \$(INCLUDES) \\ \$(YACCFILES) \$(LEXFILES) \$(CFILES) \$(FFILES) \$(ASFILES) $MAIN install: \$(INSDIR)/\$(TARGET) \$(INSDIR)/\$(TARGET): \$(TARGET) $INSTALL tar: tar rfcb /dev/rht0 20 \$(SOURCE) cpio: ls \$(SOURCE) | cpio -oB netsend: netsend \$(dest) \$(SOURCE) lint: \$(LINT) \$(CLOCALFLAGS) -I\$(INCLUDE) \$(CFILES) clean: -rm -f \$(OBJECTS) clobber: clean -rm -f \$(TARGET) touch: touch \$(TARGET) print: list60 list: list66 list66: @pr -n -w132 -l66 \$(LISTFILES) list60: @pr -n -w132 -l60 \$(LISTFILES) list51: @pr -n -w132 -l51 \$(LISTFILES) vgrind: @vgrind \$(LISTFILES) depend: sed -n -e '1,/^### DO NOT DELETE THIS LINE./p' < \$(MAKEFILE) > \$(MAKEFILE).new ! cat >> $MAKEFILE <<'!' -for i in $(YACCFILES) $(LEXFILES) $(CFILES) $(FFILES) ; do\ base=`expr $$i ':' '\(.*\).[cylf]$$'`;\ suffix=`expr $$i ':' '.*\.\([cylf]\)$$'`;\ if /bin/test $$suffix = l ; then\ lex $$i;\ mv lex.yy.c $$base.c;\ suffix=c;\ echo "$$base.c: $$base.l" >> $(MAKEFILE).new;\ elif /bin/test $$suffix = y ; then\ yacc $(YFLAGS) $$i;\ mv y.tab.c $$base.c;\ suffix=c;\ echo "$$base.c: $$base.y" >> $(MAKEFILE).new;\ echo "y.tab.h: $$base.y" >> $(MAKEFILE).new;\ fi;\ $(CC) $(CLOCALFLAGS) -I$(INCLUDE) -E $$base.$$suffix |\ grep '^# [0-9][0-9]* ".*"$$' > /tmp/grep$$$$;\ sed -e 's/.*"\(.*\)"$$/\1/' -e 's/^.\///' < /tmp/grep$$$$ |\ sort -u |\ awk\ "BEGIN { line=\"$$base.o: \"}\ {\ if(length(line \$$0)>63)\ {\ print line,\"\\\\\";\ line=\" \"\$$0\ }\ else\ line=line\" \"\$$0\ }\ END { print line}"\ >> $(MAKEFILE).new;\ done;\ rm /tmp/grep$$$$ ! cat >> $MAKEFILE <<! mv \$(MAKEFILE).new \$(MAKEFILE) ### The following dependancies are/can be generated automatically ### by 'make depend'. Listen to this warning ### ### Do NOT put any of your own dependancies below this line, ### they will be removed ### DO NOT DELETE THIS LINE. USED FOR MAKE DEPEND ! echo "Template makefile is called '$MAKEFILE'" echo "Remember to use 'make depend' to finish the job" //E*O*F makemake// echo x - makemake.1 cat > "makemake.1" << '//E*O*F makemake.1//' .TH MAKEMAKE 1 .SH NAME makemake \- construct a makefile from C, Pascal, LEX, or Yacc files. .SH SYNOPSIS .B makemake [-l] targetname files... .SH DESCRIPTION .I Makemake will construct a correct makefile for a single target binary or library. The -l flag selects that a library should be constructed, rather than a binary, and the targetname is either the library name or binary name as appropriate. .B All files should be supplied to .IR makemake , including all header files, yacc files, lex files, and any other files that are appropriate to the program or library. Normal file naming conventions apply, with .p and .i files being Pascal source and include files repectively. .P The constructed makefile will have text in it which allows the printing, archiving to tape, and netsending of the source. Most importantly, "make depend" will construct a dependency list automatically for the given source, although only C, Yacc, LEX, and Pascal files will be searched for dependencies. The dependencies looked for are only the ones that arise from the syntactic inclusion of one file within another using the "#include" facility, although "make depend" will correctly construct dependencies for nested inclusions. .P This program is terribly useful for a changing single target software system like a compiler, graphics package, and similar beasts. After significant changes to source, making the whole system properly again becomes as simple as: .sp .nf makemake griffin *.h *.p *.c README testfile make depend make install .fi .sp .SH FILES Makefile .SH BUGS Really only works for a single target binary or library. Cannot cope (I don't think) with the concept of files scattered over many directories. //E*O*F makemake.1// echo Possible errors detected by \'wc\' [hopefully none]: temp=/tmp/shar$$ trap "rm -f $temp; exit" 0 1 2 3 15 cat > $temp <<\!!! 283 949 6896 makemake 47 275 1705 makemake.1 330 1224 8601 total !!! wc makemake makemake.1 | sed 's=[^ ]*/==' | diff -b $temp - exit 0