[comp.lang.c] Makefile Maker

djones@megatest.UUCP (Dave Jones) (12/23/87)

The problem with make -- well ONE of the problems with make -- is that it
requires the user to specify the dependencies.  What you really want
is something that keeps up with the dependencies automatically as they
change.  You want to use a data-base, but the Makefile IS its own data-base.
HEY! THAT'S IT!  Let's cause the Makefile to update itself each time a
dependency might have changed.

We will need a program which removes dependency spec's from a Makefile,
and one which creates new dependency spec's.  In Sun-3 land, cc -M
will make the new dependencies.  It's quite easy to write a little
program to do it, if your cc doesn't have the feature.  If you have the 
source to cpp, just hack that.  I've done so, and it's easy.

Let's call the one which removes dependencies "remove_depends".  (One follows
at the end of this letter, in C++.  Absolutely free.)  Again easy.  If 
you speak sed or awk, (I don't), you can probably write it in a line or 
two.

The here is a paradigm for your Makefile:



# Is this self-modifying code?  If so, sue me.
##################################################

FILES = foo.o bar.o foobar.o foozle.o

libFoo.a: $(FILES)
	ar cru libFoo.a $(FILES)
	ranlib libFoo.a

.c.o:
	remove_depends $*.o < Makefile > NewMakefile
	cc -M $(CFLAGS) $< >> NewMakefile
	mv NewMakefile Makefile
	$(CC) $(CFLAGS) -.o $<

# dependency specs will magically appear below



/* This can be used as "remove_depends" */

#include <stream.h>
#include <string.h>

/* This program filters out all lines prefixed by argv[1].
** 
*/

void maybe_quit()
{
  if(cin.rdstate() != _good)
    exit(cin.rdstate() == _eof ? 0 : -1);
}

main(int argc, char** argv)
{
  if(argc == 1)
    { cerr << "Need a parameter\n";
      exit(1);
    }

  const int name_len = strlen(argv[1]) + 1;
  char* name = new char[name_len];
  strcpy(name,argv[1]);
  char*  prefix = new char[name_len];

  while(1)
    { 
      cin.get(prefix, name_len);
      maybe_quit();

      const int compares = ( strcmp(name, prefix) == 0 );

      /* flush prefix */
      if(!compares) cout << prefix;

      /* flush remainder of line */
      char ch;
      do
	{ cin.get(ch);
	  maybe_quit();
	  if( !compares  ) cout.put(ch);
	}
      while( ch != '\n');
  
    }
}

djones@megatest.UUCP (Dave Jones) (12/23/87)

in article <176@goofy.megatest.UUCP>, djones@megatest.UUCP (Dave Jones) says:
> 
> 
> 
> The problem with make -- well ONE of the problems with make -- is that it
> requires the user to specify the dependencies.  What you really want
 
[...]

Oops.  In translating from a C++ makefile into a C one, I dropped the ball.
The flag -.o  in the $(CC) line should be -c.


Sorry.

		Dave Jones
		Megatest Corp.
		880 Fox Lane
		San Jose, CA.
		95131

		(408) 437-9700 Ext 3227
		UUCP: ucbvax!sun!megatest!djones
		ARPA: megatest!djones@riacs.ARPA

chris@mimsy.UUCP (Chris Torek) (12/24/87)

In article <176@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
>... What you really want is something that keeps up with the
>dependencies automatically as they change. ... Let's cause the
>Makefile to update itself each time a dependency might have changed.

We already have something like this in 4.3BSD.  A number of the
makefiles in /usr/src support the command `make depend'.  Admittedly
this requires manual intervention---one has to decide that it is
time to update the dependency lists---but it works and is relatively
simple.

The makefiles distributed with 4.3BSD do this in an ugly way.  Each
makefile has something like this:

depend:
	for i in ${SUBDIR}; do (cd $$i; make ${MFLAGS} depend); done
	for i in ${STD} ${NSTD} ${SETUID}; do \
	    cc -M $$i.c | sed 's/\.o//' | \
	    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 } '; done >makedep
	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
	echo '$$r makedep' >>eddep
	echo 'w' >>eddep
	cp Makefile Makefile.bak
	ed - Makefile < eddep
	rm eddep makedep
	echo '# DEPENDENCIES MUST END AT END OF FILE' >>Makefile
	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >>Makefile
	echo '# see make depend above' >>Makefile

# DO NOT DELETE THIS LINE -- make depend uses it

foo: foo.c /usr/include/stdio.h /usr/include/signal.h foo.h

# DEPENDENCIES MUST END AT END OF FILE
[etc]

As it happens, all the ugliness can be, should be, and as of a
number of months ago, has been, encapsulated in a program.  This
program is called `mkdep'; it is really just a shell script that
runs cc -M.  It has two options, `-f' (specify the name of the
makefile) and `-p' (producing programs: do the sed command shown
above, so that x depends on x.c and incl.h; otherwise x.o depends
on x.c and incl.h).

I am not sure I should post the script itself, but it is trivial,
since everything you need do is shown above.  Instead of the for
loop, mkdep assumes you are naming .c files, so the Makefile
entry now looks like this:

	depend: ldepend
		for i in ${SUBDIR}; do (cd $$i; make ${MFLAGS} depend); done

	ldepend:
		mkdep -p ${CFLAGS} ${CSRCS}

	# DO NOT DELETE THIS LINE -- mkdep uses it

	# DEPENDENCIES MUST END ... [etc]

(Here `depend' has been split to provide a `change local dependencies
only' target.  `make depend' in an upper-level source directory
such as /usr/src/ucb is time consuming, and deadly boring; `make
ldepend' confines dependency generation to the current directory.)

Disliking having to list both source files *and* programs (it seems
inelegant and error prone), I created two small scripts called
`addsuf' and `chgsuf', so that the `ldepend' target becomes

	ldepend:
		mkdep -p ${CFLAGS} `addsuf .c ${STD} ${NSTD}`

or

	ldepend:
		mkdep ${CFLAGS} `chgsuf .o .c ${OBJS}`

Since I wrote them, I know I can post them.  Here are addsuf and
chgsuf in their entirety:

#! /bin/sh
#
# addsuf - add suffix to each argument
# assumes no embedded spaces in arguments

case $# in 0) echo "usage: addsuf suffix files" 1>&2; exit 1;; esac

suf="$1"
shift
echo ${1+"$@"} | sed -e "s/ /$suf /g" -e "s/$/$suf/"

#! /bin/sh
#
# chgsuf - change suffix in each argument
# assumes no embedded spaces in arguments
# oldsuf is a string, not an R.E.

case $# in 0|1) echo "usage: chgsuf oldsuf newsuf files" 1>&2; exit 1;; esac

sed="sed -e s/[/.^[\*]/\\\\&/g"
oldsuf="`echo \"$1\" | $sed`"
newsuf="`echo \"$2\" | $sed`"
shift; shift
echo ${1+"$@"} | sed -e "s/$oldsuf /$newsuf /g" -e "s/$oldsuf$/$newsuf/"


Of course, there is still a problem for those for whom `cc -M'
produces nothing useful.  Fortunately, some time ago I wrote a
shell script for use on our Suns, which then did not support a -M
option for cc.  Not long ago I moved it to our Pyramid, which still
does not support cc -M (we run OSx release 2.5, as we have some
trouble getting current sources).  Here is that version:

#! /bin/sh
#
# getdep - get dependency lists.

# find cpp
cpp=/lib/cpp

# handle arguments
incl=
for i
do
	case "$i" in
	-I*)
		incl="$incl $i";;
	-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" | grep "^#" |
		sed -e 's/# [0-9]* //' -e 's,"./,",' -e 's/"\(.*\)"/\1/' \
		    -e 's/ [ 0-9]*$//' |
		sort -u | sed -e "s/^/$dep: /";;
	esac
done
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

ljz@fxgrp.UUCP (Lloyd Zusman, Master Byte Software) (12/24/87)

In article <176@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:

>The problem with make -- well ONE of the problems with make -- is that it
>requires the user to specify the dependencies.  What you really want
>is something that keeps up with the dependencies automatically as they
>change.
> ...
>We will need a program which removes dependency spec's from a Makefile,
>and one which creates new dependency spec's.
> ...

There already is a program that lets you do this.  It is called "mkmf"
and I have mentioned it in an earlier posting.  I have determined that
it indeed is in the public domain.  After mentioning it here I got
such an overwhelming number of requests for it that yesterday I mailed
it to the moderators of comp.sources.misc and comp.sources.unix ...
I'm hoping it will be posted in one or the other of these newsgroups
in the near future.

This program takes a makefile as input and produces a new one as
output.  It automatically recalculates all the dependencies every time
it is run, removes the old dependencies from the makefile, and writes
the new ones therein.  We use it that way here at our installation and
we are quite pleased with it.

Look for it soon in comp.sources.unix or comp.sources.misc.


-------------------------------------------------------------------------
 Lloyd Zusman
 Master Byte Software
 Los Gatos, California	    	    	Internet:   fxgrp!ljz@ames.arpa
 "We take things well in hand."	    	UUCP:	    ...!ames!fxgrp!ljz

djones@megatest.UUCP (Dave Jones) (12/25/87)

in article <9942@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) says:
> 
> In article <176@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
>>... What you really want is something that keeps up with the
>>dependencies automatically as they change. ... Let's cause the
>>Makefile to update itself each time a dependency might have changed.
> 
> We already have something like this in 4.3BSD.  A number of the
> makefiles in /usr/src support the command `make depend'.  Admittedly
> this requires manual intervention---one has to decide that it is
> time to update the dependency lists---but it works and is relatively
> simple.
> 

I guess I forgot to say, "I know about 'depend', but it requires 
manual intervention."

By the way, the "make" of my dreams would keep in its data-base not only
the a record of what the source files were the last time the object was made,
but also the modification dates, and the METHOD which was used to make
the object.  It would then remake the object if any file had been modified,
or if the method to make the object had changed.  For example, maybe
you have added something to CFLAGS.

About three years ago, I wrote just such a program for our customers.
Called it "compile_files".  Now it's used exclusively for Pascal programs
and extensively for C programs.  It is not general, however.  The methods
for making Pascal and C programs are "hard-coded" into it, rather
than coded in the makefile. "Knows too much for its own good", you might say.
So now that we have C++, it needs to be fiddled with.

djones@megatest.UUCP (Dave Jones) (12/26/87)

in article <190@fxgrp.UUCP>, ljz@fxgrp.UUCP (Lloyd Zusman, Master Byte Software) says:
> 
> In article <176@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
> 
>>The problem with make -- well ONE of the problems with make -- is that it
>>requires the user to specify the dependencies.  What you really want
>>is something that keeps up with the dependencies automatically as they
>>change.
>> ...
>>We will need a program which removes dependency spec's from a Makefile,
>>and one which creates new dependency spec's.
>> ...
> 
> There already is a program that lets you do this.  It is called "mkmf"
> and I have mentioned it in an earlier posting.  I have determined that
> it indeed is in the public domain.

  [...]


> This program takes a makefile as input and produces a new one as
> output.  It automatically recalculates all the dependencies every time
> it is run ...


Com'on guys!! Read the posting!  The little program and Makefile paradigm
I posted automatically recalculates the dependancies on a file-by-file
basis, ONLY when a file is remade.  "mkmf", as you described it, goes
through ALL files every time ONE changes.  That was the point.
I don't make it out to be profound, but when you are working on one
file in a large archive, it is fast and foolproof.  The "depend" which somebody
posted earlier also reads through all files, and requires manual 
intervention.

That's enough "Bah-Humbug".  Merry Christmas.


X
X
X
X
X
X
X
X
X
X
X
X