Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Follow up fixes to #1753 #1933

Closed

Conversation

jdrouhard
Copy link
Contributor

@jdrouhard jdrouhard commented Mar 22, 2021

These are followup fixes for #1753 and fixes #1932.

  • Check the output file's mtime against the most recent input time before checking the build log
  • For outputs created by generator rules, record the restat'd output's mtime in the build log
  • Always include depfile mtime in build log mtime if special deps binding isn't specified
  • Fix performance regression (change Stat() to StatIfNecessary() in StartEdge())

@jdrouhard
Copy link
Contributor Author

cc: @bradking @jhasse

@jdrouhard jdrouhard changed the title Check the output file's mtime against the most recent input time before checking the build log (Fixes #1932) Check the output file's mtime against the most recent input time before checking build log Mar 22, 2021
@bradking
Copy link
Contributor

I've confirmed this fixes #1932, but some of CMake's test suite still fails. I'll track down the next case.

…re checking the build log

This is a followup fix for ninja-build#1753. Fixes ninja-build#1932

`build_log()` will always be valid (even for new builds). We should be
checking for the output being older than the input before we check the
build log for additional possible conditions that would make the output
dirty.
@bradking
Copy link
Contributor

See additional case posted in #1932 (comment).

@jdrouhard jdrouhard force-pushed the fix_dirty_status_computation branch from 46202d4 to 8884066 Compare March 22, 2021 18:01
@jdrouhard jdrouhard changed the title Check the output file's mtime against the most recent input time before checking build log Fix dirty status computation for generator rules and first runs with implicit deps Mar 22, 2021
@bradking
Copy link
Contributor

@jdrouhard as of commit 9fb5867362 all but one of CMake's tests pass. I'm still tracking down the remaining failure.

@bradking
Copy link
Contributor

See additional case posted in #1932 (comment).

…mtime in the build log

Generator rules may create inputs before creating the outputs. To avoid
possible infinite loops, record the actual mtime of the output file in
build log instead of the most recent input when the edge started
building, since the edge's rule could create newer inputs.
…ng isn't specified

Since the edge command might produce the depfile (implicit input) along
with the output(s), it should be taken into account when determining the
mtime to record in the build log for the outputs. If it's not, then the
next run could see a stale mtime since the previous run would have
recorded the most recent mtime excluding the depfile itself.

For special depfiles (msvcc and gcc as of this commit), the depfiles are
themselves parsed when the edge command is finished and the most recent
input time of the parsed implicit deps is directly used instead.
@jdrouhard jdrouhard force-pushed the fix_dirty_status_computation branch from 5fa40fe to f220571 Compare March 23, 2021 00:36
@jdrouhard jdrouhard changed the title Fix dirty status computation for generator rules and first runs with implicit deps Follow up fixes to #1753 Mar 23, 2021
src/build.cc Outdated
@@ -692,7 +692,7 @@ bool Builder::StartEdge(Edge* edge, string* err) {
Node* most_recent_input = NULL;
for (vector<Node*>::iterator i = edge->inputs_.begin();
i != edge->inputs_.end() - edge->order_only_deps_; ++i) {
if (!(*i)->Stat(disk_interface_, err))
if (!(*i)->StatIfNecessary(disk_interface_, err))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was discussed in #1753 but I think the performance tradeoff should lean toward speed over correctness. If we err on the side of speed, we just might have unnecessary rebuilds on future runs (as opposed to the alternative which is thinking outputs are up to date when they are actually dirty).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A build system should not re-run commands when not needed. We don't know how many downstream dependencies might need to rebuild unnecessarily.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed this on the original PR, I think you actually wondered why this wasn't StatIfNecessary() to begin with.

This shouldn't cause ninja to rerun commands if the inputs don't change, which is identical behavior to what was here before #1753.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is an example that change affects:

rm -f .ninja_deps .ninja_log out1 out2 in1 in2imp

>in1
echo >build.ninja '
rule touch_out_discovered_during_build
  command = touch $out $discovered

rule touch_dep_discovered_during_build
  command = touch $out; echo "$out: $discovered" > $depfile

build out1: touch_out_discovered_during_build in1
  discovered = in2imp

build out2: touch_dep_discovered_during_build in1 || out1
  discovered = in2imp
  depfile = out2.d
  deps = gcc

default out2'

ninja >/dev/null 2>&1

sleep 1
>in1
ninja
ninja -d explain

With Stat, or prior to #1753, the output is:

[1/2] touch out1 in2imp
[2/2] touch out2; echo "out2: in2imp" > out2.d
ninja: no work to do.

With StatIfNecessary after #1753, the output is:

[1/2] touch out1 in2imp
[2/2] touch out2; echo "out2: in2imp" > out2.d
ninja explain: recorded mtime of out2 older than most recent input in2imp (... vs ...)
[1/1] touch out2; echo "out2: in2imp" > out2.d

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this test is unfortunate. Using StatIfNecessary() could lead to infinite rebuild loops (target would never be clean) with this specific relationship of rules and implicit dependencies being updated by the rules. out1 has no specific knowledge that it updated in2imp so there's nothing we can do when finishing the command for out1 to update the cached mtime for in2imp.

If we want to keep the better build correctness that #1753 provides, it looks like there's no way to avoid Stat() when edges are started which does have a performance penalty.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC #1753 provides robustness against edits made to the source tree during the build by processes outside the build system. We need to decide if supporting that is worth making everyone pay for the extra Stat on every build just in case someone does that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This investigation led me to find that CMake Issue 20025 has the same root cause, even with Ninja 1.10 (which does not have #1753). Cases like this might be resolvable using a dyndep binding to declare the dynamically-discovered implicit output so that Ninja knows about it. I still need to try that out to be sure.

However, CMake's cases are just examples of existing cases that may exist in the wild. Many projects would have had no choice but to express things as in my above example because dyndep support is recent.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest dropping commit 53f96fed for now so we can get everything else fixed. Then open an issue to discuss this optimization.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that sounds good. I'm going to go ahead and add regression tests for this case anyhow.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be resolvable using a dyndep binding... I still need to try that out to be sure.

Unfortunately that won't work without additional changes to Ninja. See the explanation I posted in the CMake issue.

@bradking
Copy link
Contributor

As of commit f2205713 all of CMake's tests pass. However, commit 53f96fed breaks one again.

@jdrouhard
Copy link
Contributor Author

As of commit f2205713 all of CMake's tests pass. However, commit 53f96fed breaks one again.

What test? We really should port more of CMake's ninja tests to native ninja tests.

@bradking
Copy link
Contributor

commit 53f96fe breaks one again.
What test?

It's the Qt5Autogen.RerunMocBasic test. I won't have time until later to try to narrow it down to a ninja-only example.

These expose some quirky behavior of implicit deps that are not directly
known to the ninja build system.
@jdrouhard jdrouhard force-pushed the fix_dirty_status_computation branch from 53f96fe to 7b04cd7 Compare March 23, 2021 19:38
@jhasse
Copy link
Collaborator

jhasse commented Mar 23, 2021

Hi! Thanks for your work. I haven't followed everything yet.

I think a good idea would be to actually revert the merge commit of #1753, then add the test cases (except those for #1753) and then draft a new version of #1753 with the fixups. What do you think?

We could also merge this PR, but then the actual change (and the fixes for it) would be scattered.

@jdrouhard
Copy link
Contributor Author

I can do that. I'll open another PR with the revert + new tests, and a second one that re-lands the change along with the fixes.

@jdrouhard jdrouhard closed this Mar 23, 2021
@jdrouhard jdrouhard deleted the fix_dirty_status_computation branch March 23, 2021 20:24
jdrouhard added a commit to jdrouhard/ninja that referenced this pull request Mar 24, 2021
This reverts commit 67fbbee.

There were a few missing test cases exposed by CMake's test suite that
need slight adjustments. Better to revert the original attempt, add the
test cases, and then re-land the change with the fixes.

Fixes ninja-build#1933
jdrouhard added a commit to jdrouhard/ninja that referenced this pull request Mar 24, 2021
These expose some behavior related to implicit deps unknown to ninja and
timestamps with generating them as part of a rule.

See discussions in ninja-build#1932 and ninja-build#1933.
jdrouhard added a commit to jdrouhard/ninja that referenced this pull request Mar 24, 2021
These expose some behavior related to implicit deps unknown to ninja and
timestamps with generating them as part of a rule.

See discussions in ninja-build#1932 and ninja-build#1933.
rascani pushed a commit to rascani/ninja that referenced this pull request Apr 29, 2021
These expose some behavior related to implicit deps unknown to ninja and
timestamps with generating them as part of a rule.

See discussions in ninja-build#1932 and ninja-build#1933.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

build.ninja generator not re-running when it should
3 participants