[gnu.utils.bug] Some questions about GNU Make 3.54

andrewt@watnext.waterloo.edu (Andrew Thomas) (07/26/89)

I find myself in a position where I have to keep an archive up to
date, where the files in the archive are the objecs produced by
compiling all of the C files in the directory.  The directory is very
large, so I would like to compile all of the C files which need
compiling first, and then perform the 'ar' command at the end, thereby
saving the (significant) time spent calling ar over and over again.
My incorrect solution goes something like this:

------------------------------------------------------

CC = gcc
CFLAGS = -g -fwritable-strings

TARGET = libutil.a

files := $(basename $(wildcard *.c))
objects := $(patsubst %,$(TARGET)(%),$(addsuffix .o,$(files)))

$(objects):	libutil.a(%.o):		%.c
		$(CC) $(CFLAGS) -c $?

libutil.a:	$(objects)
		$(AR) $(ARFLAGS) $@ $(patsubst $@(%),%,$?)
		ranlib $@
		$(RM) $(patsubst $@(%),%,$?)

-----------------------------------------------------

The problem here is that this makefile will recompile all of the
objects correctly, but when it does the pattern substitution on $?,
the value of $? is empty because there are in fact no members of the
archive which are more recent than the archive itself.  At this point,
I would like to see a variable which is the value of all dependencies
which were remade, as opposed to those whose dates are newer.

I eventually gave up on this, and used the implicit rules for making
archive members.  I like to pipe the output from make to a file so I
can look back at any errors, etc.  Trouble is, the 'ar' command gets
echoed out all the time, kind of cluttering up my log file.  Is there
any way to turn off the echoing in an implicit rule without turning
off echoing altogether?

One last question ... is the exact specification of the implicit rules
documented anywhere, and is it possible to replicate them exactly
using explicit directives in a makefile?  I would have been able to
solve my problem by rewriting the implicit rule for archive members,
but could not figure out how to do it.

Thanks for listening.  I fell better.

jay@banzai.UUCP (Jay Schuster) (07/29/89)

In article <10822@watcgl.waterloo.edu> andrewt@watnext.waterloo.edu (Andrew Thomas) writes:
>I find myself in a position where I have to keep an archive up to
>date, where the files in the archive are the objecs produced by
>compiling all of the C files in the directory.  The directory is very
>large, so I would like to compile all of the C files which need
>compiling first, and then perform the 'ar' command at the end, thereby
>saving the (significant) time spent calling ar over and over again.

I ran into this problem starting at make 3.05 and continuing up
to make 3.54.  In SystemV-land we are encouraged (by the manual) to
do archives like this (converted to GNUish):

(%.o): %.c ;

$(LIB): \
	$(LIB)(erasebox.o) \
	$(LIB)(command.o) \
	$(LIB)(errlog.o) \
	$(LIB)(fmt.o) \
	$(LIB)(printlib+.o)
	$(CC) -c $(CFLAGS) $(?:.o=.c)
	ar rv $(LIB) $?
	rm $?

.PRECIOUS: $(LIB)

The idea being that we should defeat the default rule as to how to
make $(LIB)(foo.o), collect up what was out of date, and then just
issue one `$(CC)' command, one `ar', and one `rm'.

GNU make doesn't let you do this because when it runs across a rule
with no command, it still pretends that the targets were updated,
even though they weren't.  Also, In SystemV, if $? would be a list
of archive members `lib(member)', then $? actually becomes the list
of `member's.

Here is what I do to fix it (this is an excerpt from a larger patch,
so your line numbers will be off) :

*** commands.c~	Thu Jul 27 12:17:47 1989
--- commands.c	Thu Jul 27 15:43:55 1989
***************
*** 102,108 ****
    DEFINE_VARIABLE ("%D", 2, DIRONLY (percent));
    DEFINE_VARIABLE ("%F", 2, FILEONLY (percent));
  
!   /* Compute the values for $^ and $? and their F and D versions.  */
  
    {
      register unsigned int caret_len, qmark_len;
--- 108,117 ----
    DEFINE_VARIABLE ("%D", 2, DIRONLY (percent));
    DEFINE_VARIABLE ("%F", 2, FILEONLY (percent));
  
!   /* Compute the values for $^ and $? and their F and D versions.
!      In SystemV, if $? is a list of archive members `lib(member)'',
!      then $? is the list of `member''s.  We do a similar thing with
!      $^ */
  
    {
      register unsigned int caret_len, qmark_len;
***************
*** 116,122 ****
      caret_len = qmark_len = 0;
      for (d = file->deps; d != 0; d = d->next)
        {
! 	register unsigned int i = strlen (dep_name (d)) + 1;
  	caret_len += i;
  	if (d->changed)
  	  qmark_len += i;
--- 125,137 ----
      caret_len = qmark_len = 0;
      for (d = file->deps; d != 0; d = d->next)
        {
! 	register unsigned int i;
! 
! 	if (ar_name (dep_name(d)))
! 	  i = strlen (1 + index (dep_name(d), '('));
! 	else
! 	  i = strlen (dep_name (d)) + 1;
! 
  	caret_len += i;
  	if (d->changed)
  	  qmark_len += i;
***************
*** 138,143 ****
--- 153,165 ----
  
  	c = dep_name (d);
  	len = strlen (c);
+ 
+ 	if (ar_name (c))
+ 	  {
+ 	    c = (1 + index (c, '('));
+ 	    len = strlen (c) - 1;
+ 	  }
+ 
  	bcopy (c, cp, len);
  	cp += len;
  	*cp++ = ' ';
*** remake.c~	Thu Jul 27 12:17:56 1989
--- remake.c	Thu Jul 27 15:35:49 1989
***************
*** 414,423 ****
       register struct file *file;
  {
    file->updated = 1;
!   if (just_print_flag)
!     file->last_mtime = time ((time_t *) 0);
!   else
!     file->last_mtime = name_mtime (file->name);
  
    if (file->also_make != 0)
      {
--- 414,440 ----
       register struct file *file;
  {
    file->updated = 1;
! 
!   /* If the command we just tried to execute was empty, pretend that
!      we did something anyway.  This way things that depended on this
!      file get updated as well.  This is a hack, and not correct, but it
!      will make the efficient library update method work. */
!   {
!     if (just_print_flag)
!       file->last_mtime = time ((time_t *) 0);
!     else {
!       if (file->cmds != 0) {
!         char *p;
!         for (p = file->cmds->commands; *p != '\0'; ++p)
!           if (*p != ' ' && *p != '\t' && *p != '\n')
!             break;
!         file->last_mtime = (*p == '\0')
!                          ? time ((time_t *) 0) : name_mtime (file->name);
!       }
!       else
!         file->last_mtime = name_mtime (file->name);
!     }
!   }
  
    if (file->also_make != 0)
      {
-- 
Jay Schuster			uunet!uvm-gen!banzai!jay, attmail!banzai!jay
The People's Computer Company	`Revolutionary Programming'