views:

32

answers:

1

This is a bit complex so I have uploaded an example here.

How to test it:

  • Download the tarball and unpack it
  • cd make_problem/make
  • make aclean (forgot to remove the archive from the archive ;))
  • make alib (will re-create the simple and silly archive you just removed)
  • ./weird.sh

What weird.sh does is simply to touch a source file, re-make, touch another source file, re-make, a few times.

This is what the output looks like on my Linux system using GNU Make 3.81:

$ ./weird.sh
####  1  ####
gcc -c -g -Wall -Wextra  -o ../swu/1/src/foo1.o ../swu/1/src/foo1.c

ar cr ../make/libmine.a ../swu/1/src/foo1.o
====  1  ====
gcc -c -g -Wall -Wextra  -o ../swu/1/src/bar1.o ../swu/1/src/bar1.c

ar cr ../make/libmine.a ../swu/1/src/bar1.o
####  2  ####
gcc -c -g -Wall -Wextra  -o ../swu/1/src/foo2.o ../swu/1/src/foo2.c

====  2  ====
gcc -c -g -Wall -Wextra  -o ../swu/1/src/bar2.o ../swu/1/src/bar2.c

####  3  ####
gcc -c -g -Wall -Wextra  -o ../swu/1/src/foo3.o ../swu/1/src/foo3.c

====  3  ====
gcc -c -g -Wall -Wextra  -o ../swu/1/src/bar3.o ../swu/1/src/bar3.c

####  4  ####
gcc -c -g -Wall -Wextra  -o ../swu/1/src/foo4.o ../swu/1/src/foo4.c

====  4  ====
gcc -c -g -Wall -Wextra  -o ../swu/1/src/bar4.o ../swu/1/src/bar4.c

####  5  ####
gcc -c -g -Wall -Wextra  -o ../swu/1/src/foo5.o ../swu/1/src/foo5.c

====  5  ====
gcc -c -g -Wall -Wextra  -o ../swu/1/src/bar5.o ../swu/1/src/bar5.c

####  6  ####
gcc -c -g -Wall -Wextra  -o ../swu/1/src/foo6.o ../swu/1/src/foo6.c

====  6  ====
gcc -c -g -Wall -Wextra  -o ../swu/1/src/bar6.o ../swu/1/src/bar6.c

####  7  ####
gcc -c -g -Wall -Wextra  -o ../swu/1/src/foo7.o ../swu/1/src/foo7.c

====  7  ====
gcc -c -g -Wall -Wextra  -o ../swu/1/src/bar7.o ../swu/1/src/bar7.c

ar cr ../make/libmine.a ../swu/1/src/bar7.o
####  8  ####
gcc -c -g -Wall -Wextra  -o ../swu/1/src/foo8.o ../swu/1/src/foo8.c

====  8  ====
gcc -c -g -Wall -Wextra  -o ../swu/1/src/bar8.o ../swu/1/src/bar8.c

$

Now I expected the archive be re-built every time a source was touched, but that evidently did not happen.

Can anyone explain this? And explain how to make sure it always works as expected?

+2  A: 

The problem here is the relatively low resolution of file modification timestamps, which causes make to get confused when you run it twice in a row really quickly, like your weird.sh does.

Specifically, weird.sh will:

  1. touch foo1.c
  2. run make, which
    1. rebuilds foo1.o and
    2. rebuilds libmine.a
  3. touch bar1.c
  4. run make, which
    1. rebuilds bar1.o
    2. maybe(see below) rebuilds libmine.a

If the time between step 2.2 and step 4.2 is less than the timestamp resolution of your filesystem, then make sees libmine.a as already having the same timestamp as bar1.o, and so doesn't rebuild it.

The timestamp resolution for the Linux ext3 file system is 1 second. This explanation from the maintainer of make describes the problem in more detail, and also mentions that Solaris has better timestamp resolution, which probably explains why your example works as expected there.

If this is a problem for you in a real application, you might try the ext4 filesystem, which has nanosecond resolution timestamps. Otherwise, just put a sleep 1 after each make command in weird.sh, and the problem goes away :-)

slowdog
Thanks, this makes me kinda curious about how the hi-res timestamps are read, as they can't be in a normal stat struct. But that's a side track.I would have given a short example in the question itself if I could, but as there was pretty much text, I figured this would be nicer. Sorry if I figured wrong.
Christer
Extra fields were added to the stat struct to hold the nanosecond part of each timestamp.
slowdog