[gnu.utils.bug] Bugs in Make 3.27

jay@UUNET.UU.NET (Jay Schuster) (01/20/89)

What follows are some problems and some proposed fixes for Make 3.27.

=========== -W Problem ===================

This is a fun one.

If you have a Makefile that looks like the following:

    all: a

    dependencies: a.c b.c c.c
	    makedep -o dependencies $^
    
    a: a.o b.o c.o

    include dependencies
    .PHONY: all

Then make is smart enough to realize that the Makefile itself depends
upon the file `dependencies'.  So, if it detects that `dependencies' is
out of date with respect to a.c, b.c, or c.c, then it goes and makes
dependencies.  Afterwards it goes and re-exec's itself to go and make
the target (a).

Unfortunately, if you use the -W option, this gets passed (reasonable) to
the re-exec'd make.  So if you do a `make -W a.c', make goes and makes
dependencies because it realizes that dependencies depends on a.c.  It
then goes and re-exec's itself with the -W option, so that it can go on to
make all.  Unfortunately, the re-exec'd make realizes that because of the
-W option, a.c is newer than dependencies and goes and makes dependencies
again.

A fun infinite loop.

Somehow, make has to pass an actual timestamp for the -W option(s) to
the re-exec'd make.

=========== Open files ===================

File descriptors are left open in three places:

dir.c often leaves directories open.  When execvp is called, those
files stay open across the exec.  I do a (probably non-portable)
fcntl() after a successful opendir() to set the close-on-exec flag
for the file.

I ran into this problem because we use make to maintain a software
project spread over many directories, and use included makefiles
to make maintenance easier -- and in a recursive make three deep,
the compiler said it couldn't open a .c file.

*** dir.c~	Fri Jan 20 01:53:38 1989
--- dir.c	Fri Jan 20 01:55:25 1989
***************
*** 19,28 ****
--- 19,30 ----
  #include <dirent.h>
  #define direct dirent
  #define	D_NAMLEN(d) strlen((d)->d_name)
+ #include <fcntl.h>
  #else	/* not USGr3	*/
  #define D_NAMLEN(d) ((d)->d_namlen)
  #	ifdef	USG
  #include "ndir.h"   /* Get ndir.h from the Emacs distribution.  */
+ #include <fcntl.h>
  #	else	/* not USG	*/
  #include <sys/dir.h>
  #	endif	/* USG		*/
***************
*** 98,103 ****
--- 100,106 ----
  	dir->files = 0;
        else
  	{
+ 	  fcntl(dir->dirstream->dd_fd, F_SETFD, 1);
  	  /* Allocate an array of hash buckets for files and zero it.  */
  	  dir->files = (struct dirfile **)
  	    xmalloc (sizeof (struct dirfile) * DIRFILE_BUCKETS);

In commands.c, bad_stdin is left open for the child process even
if the child gets a good stdin.

*** commands.c~	Thu Dec 15 22:37:15 1988
--- commands.c	Fri Jan 20 02:56:09 1989
***************
*** 455,460 ****
--- 468,475 ----
        if (!child->good_stdin)
  	/* We get the `bad' standard input.  */
  	(void) dup2 (bad_stdin, 0);
+       else
+ 	close(bad_stdin);
  
        /* Run the command.  */
        (void) execvp (argv[0], argv);

In variable.c, a similar problem occurs with the pipe to a child shell
process.

*** variable.c~	Thu Dec 15 22:37:21 1988
--- variable.c	Fri Jan 20 02:51:25 1989
***************
*** 667,672 ****
--- 668,675 ----
  #else
  	    while (cc > 0);
  #endif
+ 	    /* Close the read side of the pipe now that we are done with it.  */
+ 	    (void) close (pipedes[0]);
  
  	    /* Loop until child_handler sets shell_function_pid
  	       to the status of our child shell.  */

=========== savestring(s, n) Problem ===================

There was one bug that I reported in Make 3.05 that never made it
into 3.27.  When converting the .X.a rules, savestring was being
called with an incorrect argument.

*** rule.c~	Thu Dec 15 22:37:19 1988
--- rule.c	Wed Jan 18 21:43:38 1989
***************
*** 948,954 ****
  	  if (s2len == 2 && rulename[slen] == '.' && rulename[slen + 1] == 'a')
  	    /* The suffix rule `.X.a:' is converted
  	       to the pattern rule `(%.o): %.X'.  */
! 	    name = savestring ("(%.o)", 4);
  	  else
  	    {
  	      /* The suffix rule `.X.Y:' is converted
--- 957,963 ----
  	  if (s2len == 2 && rulename[slen] == '.' && rulename[slen + 1] == 'a')
  	    /* The suffix rule `.X.a:' is converted
  	       to the pattern rule `(%.o): %.X'.  */
! 	    name = savestring ("(%.o)", 5);
  	  else
  	    {
  	      /* The suffix rule `.X.Y:' is converted


=========== Duplicate Elimination Problem ===================

In read.c, there is code to eliminate duplicates out of a singly
linked list.  It is incorrect.  It would lose everything before
a duplicate pair in the list.  Here is the diff:

*** read.c~	Fri Jan 20 04:05:12 1989
--- read.c	Fri Jan 20 04:08:15 1989
***************
*** 967,982 ****
  	  for (d = f->deps; d != 0; d = d->next)
  	    {
  	      struct dep *last, *next;
! 	      last = 0;
  	      next = d->next;
  	      while (next != 0)
  		if (streq (dep_name (d), dep_name (next)))
  		  {
  		    struct dep *n = next->next;
! 		    if (last == 0)
! 		      f->deps = n;
! 		    else
! 		      last->next = n;
  		    if (next->name != 0)
  		      free (next->name);
  		    free ((char *) next);
--- 967,979 ----
  	  for (d = f->deps; d != 0; d = d->next)
  	    {
  	      struct dep *last, *next;
! 	      last = d;
  	      next = d->next;
  	      while (next != 0)
  		if (streq (dep_name (d), dep_name (next)))
  		  {
  		    struct dep *n = next->next;
! 		    last->next = n;
  		    if (next->name != 0)
  		      free (next->name);
  		    free ((char *) next);


=========== How Complicatedly We Use Make ===================

I have Makefile's that nest this way (Partner and Pcc are two
software systems -- Partner needs Pcc to work).

Makefile for top Partner directory which include's:
    MakeHeader for Partner which include's:
	MakeHeader for Pcc which include's
	    Machine which specifies some machine specific information
	    MakeGeneric which defines all the generic variables and rules,
			some depending upon variables set by the including file.
	MakeGeneric (again) after resetting some variables.
    dependencies which defines C-code dependencies.

Basically, MakeGeneric creates a bunch of variables based on a few predefined
variables.  In order to stack the -I flags to cc and the LOADLIBES, we
grab the ones that Pcc needs to have, and then add the extra ones that
Partner needs to it.  All the including minimizes the changes we have to
make when we start a new software hierarchy or port to a new machine.


=========== My Sincerest Compliments ===================

BTW, I like Make 3.27 a lot.  I am running it under SysV.2 with mods
that have been posted before:

    Changing the field name of index to something else.
    Fuller SysV wait compatibility.
    Changing the SIGCLD handler.
    Removing trailing slashes from archive component names
    Something about the job slots problem -- I am not sure how
	this is supposed to work for recursive submakes.  I think
	that make should pass a -j N to submakes equalling the
	number of job-slots the super has available.  This way, if
	you have a makefile that does a bunch of make -C's, and
	nothing else, the sub-makes can get a -j 4 passed to them,
	because the super isn't taking advantage of the slots it
	has available.

and my own mods that I have sent before:
    $? and $^ becoming the member .o files of an
	archive instead of archive.a(member.o)
    allowing the one-pass library update scheme to work:
	.c.a: ;

	$(LIB): $(LIB)(foo.o) $(LIB)(bar.o)
		$(CC) -c $(CFLAGS) $(?:.o=.c)
		ar rv $(LIB) $?
		rm $?
	
	This has the effect of only invoking the c compiler once, and only
	invoking ar once, a big win.

	My implementation, however, tends to break if you have
	double-colon rules for the library (like one section for
	the .c files, and another for the .y files).

	I wish I had a clean way of doing this, or time to think
	about a clean way to do it.

Keep up the good work!

--
Jay Schuster			uunet!uvm-gen!banzai!jay, attmail!banzai!jay
The People's Computer Company	`Revolutionary Programming'