ar deterministic mode breaks make

While modernising an old C version of Adventure, I discovered that the Makefile for the BTree library was not working correctly. Running make always rebuilt the entire library and all test programs even if nothing had changed. This happened on both Linux (Debian Buster) and FreeBSD 12.1.

The Btree library (libbt.a) is built using GNU make default rules for archives, triggered by a dependency line of the form:

  libtbt.a: libbt.a(${OBJS})

The rule fired by the above dependency is something like:

      $(CC) -c $(CFLAGS) $<
      ar -rv $@ $*.o
      rm -f $*.o

It seems that at some point, ar defaulted to a deterministic build, as described in the man page:

  D   Operate in deterministic mode.  When adding files and the archive
      index use zero for UIDs, GIDs, timestamps, and use consistent file
      modes for all files.  When this option is used, if ar is used with
      identical options and identical input files, multiple runs will
      create identical output files regardless of the input files'
      owners, groups, file modes, or modification times.

      If binutils was configured with --enable-deterministic-archives,
      then this mode is on by default.  It can be disabled with the U
      modifier, below.

This is the reason that the library is being rebuilt; there are no timestamps in the archive file. To return to the previous behaviour, the ar flag U must be specified via the ARFLAGS macro:


By the time I figured this out, I had already re-written the rules that rebuilt the BTree library:

  libbt.a: $(SRC)
        ${CC} -c ${CFLAGS} $?
        ${AR} ${ARFLAGS} $@ *.o
        rm -f *.o
  $(SRC): $(HEADERS)
        touch $@

This is arguable more efficient, as the C compiler and ar are invoked only once, irrespective of how many source files have changed. I could also simplify the Makefile by removing the rule that created the dependencies between objects, sources and headers. The downside is that any header changes will cause all the source files to be recompiled, although some are only dependent on a subset of the header files.

Perhaps the default for make's ARFLAGS should be changed to include the U flag?

However, the GNU make team view the issue as a defect in binutils (which contains ar). See this mail in bug-make. Only three years late to the party.