bugmake - Bugs: bug #40610, gmake 4.0 loops rebuilding...

 
 

bug #40610: gmake 4.0 loops rebuilding included makefiles

Submitter:  None
Submitted:  Sun 17 Nov 2013 04:56:26 PM UTC
   
 
Severity:  3 - Normal Item Group:  Bug
Status:  Not A Bug Privacy:  Public
Assigned to:  None Open/Closed:  Closed
Component Version:  4.0 Operating System:  POSIX-Based
Fixed Release:  None Triage Status:  None
* Mandatory Fields

Add a New Comment Rich Markup
   

Mon 18 Nov 2013 03:20:14 PM UTC, comment #5: 

It re-execs after building all makefiles that it knows how to build.  However it's quite possible (and indeed I've worked on build systems that rely on this) that the generated makefiles themselves use auto-re-generation to build more makefiles.

So you can't say that one iteration of building and one re-exec will be enough; in any event it's a backward-compatibility break to make this requirement.

It can be argued that even a change such as I discussed below, where each makefile is rebuilt no more than one time is a backward-compatibility break; it could be possible to have an included makefile rebuilt N times then stop, due to some outside influence.  But we can worry about that if we ever get the capability.

Another option which has been suggested is just to impose a maximum number of re-execs, then fail.  This is probably a good idea, just to avoid infinite recursion, but it wouldn't help with the issue you describe here.  This can actually be implemented today inside your makefile but it would be better if make did it itself.

Since this appears to be an issue with tmpfs on NetBSD I'll close this bug.  Cheers!

Paul D. Smith <psmith>
Group administrator
Mon 18 Nov 2013 02:10:59 AM UTC, comment #4: 

Yes, it's a tmpfs bug; this has been confirmed. See the downstream url. It would be better if gmake could avoid looping, but if you don't see a way, then I guess it isn't reasonably possible.

Does it re-exec itself after rebuilding all included makefiles, or after each one? If it's after all, it could conceivably add a single option to inhibit regenerating included makefiles; then if one's out of date on the second run it can fail with a cogent error message. Based on the debug output, it looks like this might be the case. But if it restarts itself after every one there's not much hope.

In any case, it's not my makefile that triggered it. I know better than to have rules depend on directories. :-)  And before I figured out what was really going on I'd already found a workaround that was adequate for that particular package, which is the only one out of 13,000+ that stepped on this problem: I added "false" to the end of its make depend recipe. This causes the loop to terminate but does not fail the overall build. (This does seem like a bug in its own right, even though it's currently convenient.)

Given what's going on the smallest and most reliable way to fix it (given that the actual makefile involved is fairly complicated and fairly fragile) is to change the depend rule to do $(MAKE) objdir instead of depending on objdir. This is a localized change, and while it causes a mostly-useless extra make invocation, that doesn't matter much.

Anonymous
Sun 17 Nov 2013 11:08:37 PM UTC, comment #3: 

Yes, thanks, I meant both put the mkdir inside the depend.mk rule and remove the prerequisite (as in my example).  Sorry for not being clear.

I don't believe this is a bug in GNU make; this is a sub-optimal makefile, combined with what seems to be a bug, or at least non-standard behavior, in the tmpfs handling of directory timestamps.

It's almost never correct to list a directory as a prerequisite of a target.  This is because the time-last-modified operation of a directory is completely different than that of a regular file: the TLM of a directory is changed whenever a new file is added, removed, or renamed.  So if a target depends on a directory the target will be considered out of date whenever ANY file in that directory is added, removed, or renamed.  Very unlikely to be what you want.

The first time you run make it creates objdir at time T, then runs the depend.mk rule which removes and recreates depend.mk (which updates the objdir timestamp to T+X), then writes to depend.mk and closes it which sets the depend.mk timestamp to T+X+Y.  Then we re-exec, depend.mk's time of T+X+Y is greater than objdir's time of T+X, and all is well.

If you run it again where depend.mk doesn't exist or is older than objdir (maybe someone added a file to objdir), then depend.mk will be removed (due to the rm) then created (modifying objdir's timestamp), written, and closed again, which will update the timestamp of depend.mk.  Again here, depend.mk should have a newer timestamp than objdir.

In this case somehow the timestamp of objdir is always newer than the timestamp of depend.mk.  That says to me that the handling of directory timestamps in this implementation of tmpfs is non-standard in some way, so that for some reason it's updated again after the depend.mk file is closed.

I'm not sure why it wasn't triggered with GNU make 3.82; perhaps, as you say, for some reason that version of make was not using sub-second timestamps and this version is (I don't know why that would be however).

Some things you can try are first, don't use the "rm"; this will keep the objdir timestamp from being updated, because opening an existing file (with truncation), writing to it, and closing it should not change the directory timestamp:


objdir/depend.mk: objdir
        echo 'foo: bar' > objdir/depend.mk


This is just an experiment, really.  I don't suggest this is a good solution as it's too obscure.

The second (more correct) thing to try is order-only prerequisites instead of normal prerequisites:


objdir/depend.mk : | objdir
        rm -f objdir/depend.mk
        echo 'foo: bar' > objdir/depend.mk


This ensures that the objdir rule is run first, but it also does not rebuild depend.mk if objdir has a newer timestamp.

Please let me know how these two modifications work.

As for the infinite looping, ideally we could avoid that.  However, the behavior of make when it detects that a makefile is remade is to re-exec itself (with exec(2)).  This means that a new instance of make completely replaces the existing instance: there is no state that's transferred from the original to the re-exec'd copy.  So the re-exec'd copy has no way to know that it already built that file.  If the file is out of date still, make will build it again.

At one point I had experimented with adding -o <file> arguments to the re-exec so that the new make would not rebuild something we just rebuilt.  The major problem with this is that sometimes there were so many arguments added this way that we exceeded the environment limit and the re-exec operation failed.

At some point I'd like to implement a "read options from a file" capability for GNU make, as other tools are starting to do, and when that happens I may be able to revisit this decision since we'll have a virtually unlimited number of options available.  Until then, this is just an issue we have to live with and be aware of when writing makefiles that recreate themselves: we need to be careful to avoid infinite recursion.

Paul D. Smith <psmith>
Group administrator
Sun 17 Nov 2013 09:11:09 PM UTC, comment #2: 

It loops running the recipe for objdir/depend.mk. It does not rerun the mkdir. Moving the mkdir into the recipe for depend.mk does not change the behavior; however, removing objdir from the depends of depend.mk does.

If depend.mk exists and is newer than objdir, e.g. by creating both by hand and then touching objdir/depend.mk, it doesn't attempt to rebuild depend.mk at all and doesn't get into trouble.  However, it seems that once it runs the depend.mk recipe it gets stuck.

It looks like the proximate cause is that after creating objdir/depend.mk, objdir is newer than depend.mk, typically by about 1 ms. It looks like the difference vs. 3.82 is that 3.82 doesn't notice this. (Possibly it just isn't reading the fractional timestamps.)

What happens, based on the -d output and printing the full timestamps of objdir and objdir/depend.mk at the end of the objdir/depend.mk recipe, is that it creates depend.mk, which leaves depend.mk older than objdir, does

   Re-executing[1]: gmake -d

decides it needs to build depend.mk because it's older than objdir, and thereby goes into an infinite loop.

This is looking like the underlying cause is a tmpfs bug, but it would still probably be a good idea for make to not go into an infinite loop.

Anonymous
Sun 17 Nov 2013 08:18:13 PM UTC, comment #1: 

Odd.  I'm not aware of anything off the top of my head that changed between 3.82 and 4.0 that might cause this.  Also, I can't reproduce it on the Linux tmpfs implementation I have here.

I'll need some help debugging.  When it loops forever, what commands does it run?  Does it re-run the rebuild of objdir/depend.mk every time?  I assume so.  Does it re-run the mkdir -p objdir every time?

Also if you rewrite the makefile so that the mkdir is in the same rule as the echo, does it work properly?  I'm not saying this is a solution, I'm just trying to work out what's going on.


objdir/depend.mk:
        rm -f objdir/depend.mk
        mkdir -p objdir
        echo 'foo: bar' > objdir/depend.mk


Does it reproduce if you enable debugging mode (-d)?  If it's really timing-sensitive that might be enough to avoid the problem.  If so, try using "--debug=m" to restrict the debugging output.  If it still won't reproduce we'll maybe need to add some targeted debugging statements.

Paul D. Smith <psmith>
Group administrator
Sun 17 Nov 2013 04:56:26 PM UTC, original submission:  

When working on a sufficiently fast volume (apparently) the attached makefile loops forever rerunning the recipe for objdir/depend.mk.

I have seen this using tmpfs on NetBSD; I don't know if it happens on other ramdisk-type fses.

It does not occur with gmake 3.82 so I doubt it's a bug in the fs, though.

See http://gnats.netbsd.org/48385 for the downstream bug report.

Anonymous

 

(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach Files:
   
   
Comment:
   

Attached Files
file #29642:  Makefile added by None (161B - application/octet-stream)

 

Depends on the following items: None found

Items that depend on this one: None found

 

Carbon-Copy List
  • -email is unavailable- added by psmith (Posted a comment)
  •  

    There are 0 votes so far. Votes easily highlight which items people would like to see resolved in priority, independently of the priority of the item set by tracker managers.

    Only logged-in users can vote.

     

    Follow 3 latest changes.

    Date Changed by Updated Field Previous Value => Replaced by
    2013-11-18 psmith StatusNone Not A Bug
        Open/ClosedOpen Closed
    2013-11-17 None Attached File- Added Makefile, #29642

    Back to the top

    Powered by Savane 3.13-02a9.
    Corresponding source code