Subject: bin/6426: make sometimes fails to build directory targets in top layer of union mount
To: None <gnats-bugs@gnats.netbsd.org>
From: None <jbernard@ox.mines.edu>
List: netbsd-bugs
Date: 11/10/1998 20:39:52
>Number:         6426
>Category:       bin
>Synopsis:       make sometimes fails to build directory targets in top layer of union mount
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    bin-bug-people (Utility Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Nov 10 19:50:01 1998
>Last-Modified:
>Originator:     Jim Bernard
>Organization:
	Speaking for myself
>Release:        October 26, 1998
>Environment:
System: NetBSD zoo 1.3H NetBSD 1.3H (ZOO) #0: Sun Oct 18 08:48:41 MDT 1998 local@zoo:/home/local/netbsd-current/usr/src/sys/arch/i386/compile/ZOO i386


>Description:
	This was first observed while building the qt package, and reported
	as pkg/5949 (by me), and pkg/6260 (by Chris Jones).  However, since
	the problem appears to be the fault of make (and/or unionfs), those
	PR's have been closed, and this one supercedes them.

	In summary, if a Makefile has a target that is the name of a
	directory ("src:" in the qt top-level Makefile), and the sources
	reside in the lower layer of a union mount, while the build is
	done in the upper layer, NetBSD's make frequently fails to build
	the target.  GNU make always succeeds, and NetBSD make always
	succeeds when no union mount is involved.

	The best analysis I have of what happens is quoted below from
	one of my addenda to PR 5949 (the behavior still occurs for the
	latest version, qt-1.41).  One important new piece of information
	is that using gnu make for the build, via "make USE_GMAKE=yes"
	(thanks to Matthias Scheler for the suggestion) was successful
	in 2 out of 2 tries.  This suggests rather strongly that make is
	to blame, hence this PR.

	I've developed a much smaller, faster test case that also
	exhibits the behavior (see "How-To-Repeat" below), and verified
	that it occurs on three different NetBSD systems, two i386 and
	one sparc, with a variety of recent and not-so-recent versions
	of -current.

============ Beginning of quoted material from pkg/5949  ============

  I've done some additional sleuthing on this, and now have it pinned down
to the behavior of make when working on a union-mounted filesystem, using
the top-level Makefile in the qt source (i.e., x11/qt/work/qt-1.40/Makefile).
The build of qt usually (but not always!) does not complete the necessary
work, because make thinks the src directory is up to date already before
descending into that directory to build there.  I gathered some statistics:
8 of 10 attempts to build qt failed to build in src when the package files
(Makefile, files/md5, etc.) were in the lower layer of a union mount, even
though _all_ of the files in the work directory, where the error occurs, are
in the top layer.  By contrast, 10 of 10 tries completed successfully when
no union mount was involved.  (BTW: libtool, LOCALBASE, and nfs are now
clearly shown to be unrelated to the problem.)

  To get a picture of what's happening, let's start with selections from the
qt Makefile:

all: moc src tutorial examples
	@echo
...

moc: variables FORCE
	cd src/moc; $(MAKE)
	cp src/moc/moc bin/moc

src: moc variables FORCE
	cd $@; $(MAKE)

...
variables: Makefile
	@echo
	@echo These targets are available:
	@echo
	@ls configs
	@echo
	@echo Make any of them to configure Qt for building.  The make process
	@echo will now abort with an error.
	@echo
	@exit 1

...

Now, src, tutorial, and examples are directories, but moc is non-existent both
before and after building; variables is an empty file created by the configuration
process, and already exists before the build begins.  Here's a portion of what
"make -d m" shows for a successful build:

...
===>  Building for qt-1.40
Examining pre-build...non-existent...non-existent and no sources...out-of-date.
update time:  9:51:32 Aug 21, 1998
Examining Makefile...modified 13:25:06 Jul 09, 1998...up-to-date.
Examining variables...modified  9:51:31 Aug 21, 1998...up-to-date.
Examining FORCE...non-existent...non-existent and no sources...out-of-date.
update time:  9:51:32 Aug 21, 1998
Examining moc...non-existent...modified before source...out-of-date.
cd src/moc; make
...
update time:  9:51:56 Aug 21, 1998
Examining all...non-existent...modified before source...out-of-date.
update time:  9:51:56 Aug 21, 1998
cp src/moc/moc bin/moc
update time:  9:51:32 Aug 21, 1998
Examining src...modified  9:51:28 Aug 21, 1998...modified before source...out-of-date.
cd src; make
...


And, here's the analogous output from an unsuccessful build when unionfs is
involved:

...
===>  Building for qt-1.40
Examining pre-build...non-existent...non-existent and no sources...out-of-date.
update time: 15:21:41 Aug 20, 1998
Examining Makefile...modified 13:25:06 Jul 09, 1998...up-to-date.
Examining variables...modified 15:21:40 Aug 20, 1998...up-to-date.
Examining FORCE...non-existent...non-existent and no sources...out-of-date.
update time: 15:21:42 Aug 20, 1998
Examining moc...non-existent...modified before source...out-of-date.
cd src/moc; make
...
update time: 15:22:07 Aug 20, 1998
Examining all...non-existent...modified before source...out-of-date.
update time: 15:22:07 Aug 20, 1998
cp src/moc/moc bin/moc
update time: 15:21:42 Aug 20, 1998
Examining src...modified 15:21:42 Aug 20, 1998...up-to-date.
`src' is up to date.
...

As you can see, in the latter case, the timestamp on the src directory
matches the update time, whereas in the successful case above, src is
4 seconds older, so deemed out of date.

  Now, it's not clear to me at this point whether this should be viewed as
a bug in make, unionfs, or the qt Makefile.  Clearly the latter can be
modified to work around the problem by marking src as PHONY, and I'll submit
a patch below as a suggested addition to the qt/patches directory (as patch-ac)
that does that.  However, I wonder if Christos (and/or any of the rest of you)
could render an opinion as to which bit of software is most to blame here.
It seems to me that make _ought_ to handle this situation: it should always
build src, since some of its sources never actually exist, and it should
always be viewed as out of date with respect to such a source, so I'm
inclined to point the finger there.

============ End of quoted material ============


>How-To-Repeat:
	Try to build qt in the top layer of a union mount.

	Alternatively, here's a much simpler example that also shows
	the behavior.  Curiously, this example seems to fail exactly
	half the time (specifically every other time), whereas the qt
	case fails more often.

	* Unpack the shell archive attached below into some directory--I'll
	  assume it's /var/tmp.

	* mount -t union -o -b /var/tmp/tst /var/tmp/top
	  (I assume the problem would also occur if /var/tmp/top is
	  mounted above /var/tmp/tst, but I haven't tried that.)

	* cd /var/tmp/top

	* make && rm -f src/b
	  - You should find that sometimes the make will succeed, and
	    sometimes it will simply report that "src is up to date".
	  - If instead you use gmake, it will always succeed.
	  - If instead you execute the test in /var/tmp/tst, it will
	    always succeed.
	  - If instead you don't "rm -f src/b", it seems to always
	    succeed after the first successful execution.
	  - If you mark the "src:" target as .PHONY, it will always
	    succeed (this is one way to work around the problem in qt).

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	top
#	tst
#	tst/src
#	tst/src/a
#	tst/Makefile
#
echo c - top
mkdir -p top > /dev/null 2>&1
echo c - tst
mkdir -p tst > /dev/null 2>&1
echo c - tst/src
mkdir -p tst/src > /dev/null 2>&1
echo x - tst/src/a
sed 's/^X//' >tst/src/a << 'END-of-tst/src/a'
END-of-tst/src/a
echo x - tst/Makefile
sed 's/^X//' >tst/Makefile << 'END-of-tst/Makefile'
X
Xsrc: FORCE
X	cd $@; touch b; echo "done in src"
X
XFORCE:
END-of-tst/Makefile
exit


>Fix:
	Unknown.
>Audit-Trail:
>Unformatted: