NetBSD Problem Report #34934

From dholland@eecs.harvard.edu  Sat Oct 28 22:26:18 2006
Return-Path: <dholland@eecs.harvard.edu>
Received: from mail.netbsd.org (mail.netbsd.org [204.152.190.11])
	by narn.NetBSD.org (Postfix) with ESMTP id A5D6963B8CA
	for <gnats-bugs@gnats.NetBSD.org>; Sat, 28 Oct 2006 22:26:18 +0000 (UTC)
Message-Id: <20061028222519.038085426@weatherwax.eecs.harvard.edu>
Date: Sat, 28 Oct 2006 18:25:18 -0400 (EDT)
From: dholland@eecs.harvard.edu
Reply-To: dholland@eecs.harvard.edu
To: gnats-bugs@NetBSD.org
Subject: make sometimes fails to handle multiple targets on the left correctly
X-Send-Pr-Version: 3.95

>Number:         34934
>Category:       bin
>Synopsis:       make sometimes fails to handle multiple targets on the left correctly
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    rillig
>State:          closed
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sat Oct 28 22:30:00 +0000 2006
>Closed-Date:    Sun May 15 09:03:56 +0000 2022
>Last-Modified:  Sun May 15 09:03:56 +0000 2022
>Originator:     David A. Holland
>Release:        NetBSD 4.99.3 (20061018)
>Organization:
   Moderately organized.
>Environment:
System: NetBSD weatherwax 4.99.3 NetBSD 4.99.3 (WEATHERWAX) #2: Wed Oct 18 18:55:11 EDT 2006 dholland@weatherwax:/usr/src/sys/arch/i386/compile/WEATHERWAX i386
Architecture: i386
Machine: i386

/usr/bin/make:
     $NetBSD: crt0.c,v 1.16 2006/05/17 17:08:54 christos Exp $
     $NetBSD: arch.c,v 1.52 2006/10/15 08:38:21 dsl Exp $
     $NetBSD: buf.c,v 1.19 2005/08/08 16:42:54 christos Exp $
     $NetBSD: compat.c,v 1.63 2006/10/15 08:38:21 dsl Exp $
     $NetBSD: cond.c,v 1.34 2006/10/15 08:38:21 dsl Exp $
     $NetBSD: dir.c,v 1.50 2006/10/15 08:38:21 dsl Exp $
     $NetBSD: for.c,v 1.23 2006/10/15 08:38:21 dsl Exp $
     $NetBSD: hash.c,v 1.16 2005/08/04 00:20:12 christos Exp $
     $NetBSD: job.c,v 1.123 2006/10/15 08:38:21 dsl Exp $
     $NetBSD: main.c,v 1.133 2006/10/15 08:38:21 dsl Exp $
     $NetBSD: make.c,v 1.66 2006/10/15 08:38:22 dsl Exp $
     $NetBSD: parse.c,v 1.118 2006/10/15 21:17:27 dsl Exp $
     $NetBSD: str.c,v 1.25 2006/08/11 19:11:00 christos Exp $
     $NetBSD: suff.c,v 1.55 2006/10/15 08:38:22 dsl Exp $
     $NetBSD: targ.c,v 1.43 2006/10/15 08:38:22 dsl Exp $
     $NetBSD: trace.c,v 1.7 2006/01/04 21:35:44 dsl Exp $
     $NetBSD: var.c,v 1.114 2006/10/15 08:38:22 dsl Exp $
     $NetBSD: util.c,v 1.39 2005/08/08 16:42:54 christos Exp $
     $NetBSD: lstAppend.c,v 1.10 2004/05/07 00:04:41 ross Exp $
     $NetBSD: lstAtEnd.c,v 1.11 2005/02/16 15:11:53 christos Exp $
     $NetBSD: lstAtFront.c,v 1.11 2005/02/16 15:11:53 christos Exp $
     $NetBSD: lstClose.c,v 1.10 2004/05/07 00:04:41 ross Exp $
     $NetBSD: lstConcat.c,v 1.14 2005/08/08 16:42:54 christos Exp $
     $NetBSD: lstDatum.c,v 1.10 2004/05/07 00:04:41 ross Exp $
     $NetBSD: lstDeQueue.c,v 1.11 2005/02/16 15:11:53 christos Exp $
     $NetBSD: lstDestroy.c,v 1.14 2005/08/09 21:36:42 christos Exp $
     $NetBSD: lstDupl.c,v 1.13 2005/08/09 21:36:42 christos Exp $
     $NetBSD: lstEnQueue.c,v 1.11 2005/02/16 15:11:53 christos Exp $
     $NetBSD: lstFind.c,v 1.12 2005/02/16 15:11:53 christos Exp $
     $NetBSD: lstFindFrom.c,v 1.11 2004/05/07 00:04:41 ross Exp $
     $NetBSD: lstFirst.c,v 1.10 2004/05/07 00:04:41 ross Exp $
     $NetBSD: lstForEach.c,v 1.11 2004/05/07 00:04:41 ross Exp $
     $NetBSD: lstForEachFrom.c,v 1.11 2004/05/07 00:04:41 ross Exp $
     $NetBSD: lstInit.c,v 1.10 2004/05/07 00:04:41 ross Exp $
     $NetBSD: lstInsert.c,v 1.10 2004/05/07 00:04:41 ross Exp $
     $NetBSD: lstIsAtEnd.c,v 1.10 2004/05/07 00:04:41 ross Exp $
     $NetBSD: lstIsEmpty.c,v 1.10 2004/05/07 00:04:41 ross Exp $
     $NetBSD: lstLast.c,v 1.10 2004/05/07 00:04:41 ross Exp $
     $NetBSD: lstMember.c,v 1.10 2004/05/07 00:04:41 ross Exp $
     $NetBSD: lstNext.c,v 1.10 2004/05/07 00:04:41 ross Exp $
     $NetBSD: lstOpen.c,v 1.10 2004/05/07 00:04:41 ross Exp $
     $NetBSD: lstRemove.c,v 1.12 2005/08/08 16:42:54 christos Exp $
     $NetBSD: lstReplace.c,v 1.10 2004/05/07 00:04:41 ross Exp $
     $NetBSD: lstSucc.c,v 1.11 2004/05/07 00:04:41 ross Exp $


>Description:

If you have two (or presumably more) targets on the left hand side of
a rule, and they're files that aren't in the current directory, make
will sometimes fail to detect that they got built together and run the
recipe twice, once "for" each target.

If the recipe is in some way generative, this can result in a broken
build; e.g. if you generate matching .c and .h files and this happens
twice, some parts of a build can end up including one version of the
header file and some the other, which in obscure cases could
conceivably be lethal.

>How-To-Repeat:

Use the included simple example, which has a simple code generator
that generates a source and header file into a subdirectory.

Unpack the example:

	tar -xvzf example.tgz
	cd example

Compile it:

	make

This does
		gdir/gen.sh gdir/gen
		cc -Igdir -c src.c
		cc -Igdir -c gdir/gen.c
		cc src.o gen.o -o prog

Note that it runs gen.sh once.

Now, having built it, do this:

	touch gdir/gen.sh
	make

This causes
		gdir/gen.sh gdir/gen
		cc -Igdir -c src.c
		gdir/gen.sh gdir/gen
		cc -Igdir -c gdir/gen.c
		cc src.o gen.o -o prog

Note that gen.sh gets run twice and the two source files in the
program are compiled with different copies of gen.h.

I haven't contrived this example so the resulting program breaks
(maybe I should have) but it's clearly not supposed to happen this
way.

Example follows:

begin 644 example.tgz
M'XL(`!K50T4``^U8ST\3012>`@J,)B(G$P\^`9,6PS*[;1=3@0@5B`8TL?'F
M9=D=V(W;W69W40Q!#YR\^0=YTXO_@U<3#QST;H(S^Z,=VB*:T*)QOJ:=>6_>
MSGLSK]^;:>FN46^X%/42A)3('"&LG=/ULL;:&%D;]U52ULA<62O.E9E>U9B,
MRCV-*L5.&!D!`+)LWW4-SSK)[J5-J=N/@/H+FN1_=MMR@E[Y^+/\EWC^BZ2H
MROSW`6+^9[>II_AG[X.HA.@GYU\MM?*OZEK,_Q+//SG[4#KQG^?_S<KZ:BZ7
M:\HY-(ARPOC[M"W%GWF$T3!Z^O;+P>'(P>%EF^N^_C@Z.CKX./;I@TU=U\<(
MK56K%<BO/7I:@*+"7I!_1*/EVGWP-HN@\6K`<EU`2`E?U2-CD[51D+1VU@NH
MJT1T-T**940&4C;#D"G]1##]>IUZT6^O\4:\+H0N"FNY)HP/I>UU]AX5]-\&
M$!IC[4CZ_%#:OY7*@ZG=>IN_;+[;J?UI=B2=;T"P&Q;LLGS<$?H<-<0STFEW
M5?#)L<?>2UWL1)^Q7R:\0\D>#*?QC:6R.-]G9C?>9;YVB/HA]/U('!ML>VKP
MF`<N#[7)%]KDB\T]XKD<9_-=$L9YW(C7,Y-_HD;@>-$64V$>%YOJ"FL'1DX(
M_#]"1_T/[3/WP>O_K\[_HJJUSG]=C^N_QG2R_O<>DS=G-QUO-K3Q)"3IAQD(
M'?Z=@(9KF)1MBT4#V/(#,,#T+<K-:&!$?H"Q:42P"%-[ZKYBP_S\RN-5_,)W
M+&Z2YYW"7<QUHIV9VDTZGNGNL.GFP\AR?,5>;*DF>"3V1-M<L(<!(&%R?B(^
M;)YY$\S%?NSDO'?RWT3&_S`P6:GL#4ZY__'BT.0_*1;C^[]*)/_[@0[.848O
MJ!N.ET\)Q_G'2,9Z`8UV`@\(9]QYQRUQ-LCXOV$\IUM.;_X(.HW_Y9+>.O\U
M+>:_KA')_SZ@]J1:6XB+/S2O@"9^O/PPT?KQI<#'N%I=,$U<75U?6JLMS#S@
MMA@W`G^[`E-Y;E[`HU/Y:K60B3#CP]0]C%NSMAS8%1#NFWA4$)H#[$GNN2*&
MU?20Q,%\'(L:QQ%7(%Y.5^-D!&<U3[%H@WH6QDE;Z?+,Q@:3^"85V`6F:6^Z
MU/"8>5"'F2W@VP!=UPG3;`>G7__%U3+C?[JTGO@X]?S7RRW^EY/__]@/`,G_
H/D"DC/#%[22?.'C>04M(2$A(2$A(2$A(2$A(2/P1?@+?4`\4`"@``-@/
`
end

>Fix:

I don't have time to look into it right now, but I'll put it on my list...

(As a workaround to the broken build problem, one can always run make
repeatedly.)

>Release-Note:

>Audit-Trail:
From: David Laight <david@l8s.co.uk>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: bin/34934: make sometimes fails to handle multiple targets on the left correctly
Date: Sun, 29 Oct 2006 07:18:35 +0000

 On Sat, Oct 28, 2006 at 10:30:00PM +0000, dholland@eecs.harvard.edu wrote:
 > >Number:         34934
 > >Category:       bin
 > >Synopsis:       make sometimes fails to handle multiple targets on the left correctly
 ...
 > >Description:
 > 
 > If you have two (or presumably more) targets on the left hand side of
 > a rule, and they're files that aren't in the current directory, make
 > will sometimes fail to detect that they got built together and run the
 > recipe twice, once "for" each target.
 ....

 If you have:

 a1 a1: b
 	echo $@

 The make runs the rules twice, once for a1 and once for a2.
 This is the correct and required behaviour.
 (Otherwise the above could't output 'a1' and 'a2'.)

 If you have something that generates two files then you have to
 do something else (eg have one of the files depend on the other one).

 	David

 -- 
 David Laight: david@l8s.co.uk

From: dholland@eecs.harvard.edu (David Holland)
To: gnats-bugs@NetBSD.org
Cc: gnats-admin@netbsd.org, netbsd-bugs@netbsd.org,
	dholland@eecs.harvard.edu
Subject: Re: bin/34934: make sometimes fails to handle multiple targets on the left correctly
Date: Sun, 29 Oct 2006 13:29:39 -0500 (EST)

  >  If you have:
  >  
  >  a1 a1: b
         ^2
  >  	echo $@
  >  
  >  The make runs the rules twice, once for a1 and once for a2.
  >  This is the correct and required behaviour.
  >  (Otherwise the above could't output 'a1' and 'a2'.)

 Yes.

 It's not a matter of what the rules mean; it's a matter of when
 timestamps can and cannot be cached. If you have

 a1 a2: b
 	echo foo > a1; echo bar > a2

 then after running the recipe make is required to check and ascertain
 that both a1 and a2 have been updated. Then it won't run the recipe a
 second time. Whereas if you don't list both a1 and a2 on the left make
 is allowed to assume that the one not listed isn't modified, and this
 can cause things to go wrong later. (Lying to make rarely goes
 unpunished.)

 You'll note that make does actually do this; the problem is that
 something breaks some of the time if a1 and a2 are in another
 directory.

 -- 
    - David A. Holland / dholland@eecs.harvard.edu

From: Roland Illig <roland.illig@gmx.de>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: toolchain/34934
Date: Sat, 12 Feb 2022 01:18:57 +0100

 David A. Holland wrote on 29 Oct 2006:
  > It's not a matter of what the rules mean

 I disagree.  It's exactly a matter of what the rules mean.  From your
 description, you seem to think that "target1 target2: source" means that
 this is a single rule that builds 2 targets.

 Unfortunately,
 https://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html#tag_2=
 0_76_13_04
 talks a lot about the syntax of the dependency declarations but misses
 to mention explicitly that each target is handled separately, there are
 no groups of targets.  The manual page for make(1) is as silent as POSIX
 in this regard.

 NetBSD make and GNU make agree on how to interpret the dependency
 "target1 target2: source" though.  They both treat the targets as
 independent.  Therefore I don't see any need to change the code.  It
 would be good to mention this independence in the documentation and ask
 POSIX to add it to the specification for their next issue.

 David A. Holland wrote on 29 Oct 2006:
  > after running the recipe make is required to check and
  > ascertain that both a1 and a2 have been updated

 If we assume that a1 and a2 are independent (as GNU make and NetBSD make
 treat them), why would make be required to check that both files have
 been updated?

Responsible-Changed-From-To: bin-bug-people->rillig
Responsible-Changed-By: rillig@NetBSD.org
Responsible-Changed-When: Sat, 12 Feb 2022 00:21:09 +0000
Responsible-Changed-Why:
I'll handle this one.


From: David Holland <dholland-bugs@netbsd.org>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: toolchain/34934
Date: Mon, 14 Feb 2022 21:32:50 +0000

 On Sat, Feb 12, 2022 at 12:20:02AM +0000, Roland Illig wrote:
  >  David A. Holland wrote on 29 Oct 2006:
  >   > It's not a matter of what the rules mean
  >  
  >  I disagree.  It's exactly a matter of what the rules mean.  From your
  >  description, you seem to think that "target1 target2: source" means that
  >  this is a single rule that builds 2 targets.

 Unfortunately, you are wrong.

 I "seem to think" that because it's true: rules of the form "target1
 target2: source" have always been used for this purpose, as well as
 *also* for rules that build several targets independently.

 Traditionally, this works because a non-parallel make sees the rule,
 says "oh, target1 is out of date", runs the recipe, and then
   - if target2 is now up to date, it doesn't need to run it again (and
     doesn't)
   - if target2 does not exist, it runs it again for target2

 This does not work correctly for parallel make and that's been a
 long-standing problem. Several solutions have been proposed but none
 have been adopted. I think there's another PR for it, but whether or
 not that's the case it's a relatively well-known issue.

 *This* PR is about the sequential form breaking sometimes for targets
 in other directories. As I pointed out in 2006, by default it works
 correctly. (If by chance you have broken this recently, please fix
 it.)

 viz.:

    valkyrie% cat Makefile 
    all: foo bar
    foo bar: baz
 	   touch foo bar
    valkyrie% touch baz
    valkyrie% make
    touch foo bar
    valkyrie% 

 Note that gmake behaves the same way:

    valkyrie% touch baz
    valkyrie% gmake
    touch foo bar
    valkyrie% 

  >  Unfortunately,
  >  [irrelevant]

  >  David A. Holland wrote on 29 Oct 2006:
  >   > after running the recipe make is required to check and
  >   > ascertain that both a1 and a2 have been updated
  >  
  >  If we assume that a1 and a2 are independent (as GNU make and NetBSD make
  >  treat them), why would make be required to check that both files have
  >  been updated?

 Because that's how it's supposed to work.

 -- 
 David A. Holland
 dholland@netbsd.org

From: Roland Illig <roland.illig@gmx.de>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: toolchain/34934
Date: Sat, 7 May 2022 12:40:24 +0200

 Am 14.02.2022 um 22:35 schrieb David Holland:
 >   *This* PR is about the sequential form breaking sometimes for targets
 >   in other directories. As I pointed out in 2006, by default it works
 >   correctly. (If by chance you have broken this recently, please fix
 >   it.)

 Thanks for the detailed explanation.

 Do you see any chance of making this "sometimes" or "in some cases" more
 specific?

 When you say "sometimes", does this mean that when running the same
 scenario 20 times in a row, 3 of them might fail and the other 17 might
 succeed?

 When you say "in some cases", does this mean that the behavior is then
 either "always fail" or "always succeed", but no mixture?

 Could it have something to do with .PATH, or is it enough to have
 subdir/file?

 I ran the following loop for several minutes in the example directory
 that you provided; it always generated consistent output.

 make clean
 make > good.out
 while :; do
    make -s clean
    make > new.out
    cmp -s new.out good.out || break
    printf .
 done

 Any other ideas to reproduce the wrong behavior?

 Roland

From: David Holland <dholland-bugs@netbsd.org>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: toolchain/34934
Date: Sun, 15 May 2022 08:55:23 +0000

 On Sat, May 07, 2022 at 10:45:02AM +0000, Roland Illig wrote:
  >  Am 14.02.2022 um 22:35 schrieb David Holland:
  >  >   *This* PR is about the sequential form breaking sometimes for targets
  >  >   in other directories. As I pointed out in 2006, by default it works
  >  >   correctly. (If by chance you have broken this recently, please fix
  >  >   it.)
  >  
  >  Thanks for the detailed explanation.
  >  
  >  Do you see any chance of making this "sometimes" or "in some cases" more
  >  specific?

 Not without doing a bunch of experiments. I have long forgotten what I
 was doing in 2006 that triggered the initial report :-)

 Reading between the lines of what I originally wrote, which does (or
 did then) exhibit both behaviors, and looking at the example, the
 difference is triggered by touching the generator script; that is, in
 the case where it worked, the dep from the output files to the
 generator was satisfied, and where it didn't, it was out of date
 before the recipe was run the first time (but not before it was run
 the second time).

 So,

  >  When you say "sometimes", does this mean that when running the same
  >  scenario 20 times in a row, 3 of them might fail and the other 17 might
  >  succeed?

 I think it meant "depending on the mtime state of the files involved"
 as opposed to "nondeterministically". Very little in non-parallel make
 is nondeterministic :-)

 I have figured that the problem was somewhere in the (then, at least)
 rudimentary directory caching layer, something maybe like not flushing
 cached mtimes of files in other directories.


 However...

 ...it no longer behaves according to the description, that is, it
 works in both cases.

 I also vaguely recall something in this context involving ".."
 directories, but I just tried it with ../gdir/ insted of gdir/ and it
 still works.

 Therefore, I'm inclined to think it's fixed.


  >  Could it have something to do with .PATH, or is it enough to have
  >  subdir/file?

 Very unlikely; I don't ordinarily write makefiles with .PATH (since
 long before 2006) so I wouldn't have hit any related problems.

 -- 
 David A. Holland
 dholland@netbsd.org

State-Changed-From-To: open->closed
State-Changed-By: dholland@NetBSD.org
State-Changed-When: Sun, 15 May 2022 09:03:56 +0000
State-Changed-Why:
No longer reproducible.


>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.46 2020/01/03 16:35:01 leot Exp $
$NetBSD: gnats_config.sh,v 1.9 2014/08/02 14:16:04 spz Exp $
Copyright © 1994-2020 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.