[net.sources] 'Makemake': a program to construct makefiles

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