Skip to content

Commit

Permalink
add support for -d explain to help debug why rules are running
Browse files Browse the repository at this point in the history
  • Loading branch information
sgraham committed Apr 13, 2012
1 parent 16cab01 commit 1aae1bc
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 10 deletions.
1 change: 1 addition & 0 deletions configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ def binary(name):
'disk_interface',
'edit_distance',
'eval_env',
'explain',
'graph',
'graphviz',
'lexer',
Expand Down
2 changes: 1 addition & 1 deletion src/build.cc
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ void Plan::CleanNode(BuildLog* build_log, Node* node) {
if (!(*ni)->dirty())
continue;

if ((*ei)->RecomputeOutputDirty(build_log, most_recent_input, command,
if ((*ei)->RecomputeOutputDirty(build_log, most_recent_input, NULL, command,
*ni)) {
(*ni)->MarkDirty();
all_outputs_clean = false;
Expand Down
15 changes: 15 additions & 0 deletions src/explain.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

bool g_explaining = false;
22 changes: 22 additions & 0 deletions src/explain.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <stdio.h>

#define EXPLAIN(fmt, ...) { \
if (g_explaining) \
fprintf(stderr, "ninja explain: " fmt "\n", __VA_ARGS__); \
}

extern bool g_explaining;
31 changes: 25 additions & 6 deletions src/graph.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "build_log.h"
#include "depfile_parser.h"
#include "disk_interface.h"
#include "explain.h"
#include "metrics.h"
#include "parsers.h"
#include "state.h"
Expand All @@ -43,13 +44,16 @@ bool Edge::RecomputeDirty(State* state, DiskInterface* disk_interface,

// Visit all inputs; we're dirty if any of the inputs are dirty.
TimeStamp most_recent_input = 1;
Node* most_recent_node = NULL;
for (vector<Node*>::iterator i = inputs_.begin(); i != inputs_.end(); ++i) {
if ((*i)->StatIfNecessary(disk_interface)) {
if (Edge* edge = (*i)->in_edge()) {
if (!edge->RecomputeDirty(state, disk_interface, err))
return false;
} else {
// This input has no in-edge; it is dirty if it is missing.
if (!(*i)->exists())
EXPLAIN("%s has no in-edge and is missing", (*i)->path().c_str());
(*i)->set_dirty(!(*i)->exists());
}
}
Expand All @@ -64,10 +68,13 @@ bool Edge::RecomputeDirty(State* state, DiskInterface* disk_interface,
// If a regular input is dirty (or missing), we're dirty.
// Otherwise consider mtime.
if ((*i)->dirty()) {
EXPLAIN("%s is dirty", (*i)->path().c_str());
dirty = true;
} else {
if ((*i)->mtime() > most_recent_input)
if ((*i)->mtime() > most_recent_input) {
most_recent_input = (*i)->mtime();
most_recent_node = *i;
}
}
}
}
Expand All @@ -81,7 +88,7 @@ bool Edge::RecomputeDirty(State* state, DiskInterface* disk_interface,
for (vector<Node*>::iterator i = outputs_.begin();
i != outputs_.end(); ++i) {
(*i)->StatIfNecessary(disk_interface);
if (RecomputeOutputDirty(build_log, most_recent_input, command, *i)) {
if (RecomputeOutputDirty(build_log, most_recent_input, most_recent_node, command, *i)) {
dirty = true;
break;
}
Expand All @@ -107,7 +114,9 @@ bool Edge::RecomputeDirty(State* state, DiskInterface* disk_interface,

bool Edge::RecomputeOutputDirty(BuildLog* build_log,
TimeStamp most_recent_input,
const string& command, Node* output) {
Node* most_recent_node,
const string& command,
Node* output) {
if (is_phony()) {
// Phony edges don't write any output. Outputs are only dirty if
// there are no inputs and we're missing the output.
Expand All @@ -117,8 +126,10 @@ bool Edge::RecomputeOutputDirty(BuildLog* build_log,
BuildLog::LogEntry* entry = 0;

// Dirty if we're missing the output.
if (!output->exists())
if (!output->exists()) {
EXPLAIN("output %s doesn't exist", output->path().c_str());
return true;
}

// Dirty if the output is older than the input.
if (output->mtime() < most_recent_input) {
Expand All @@ -128,9 +139,15 @@ bool Edge::RecomputeOutputDirty(BuildLog* build_log,
// considered dirty if an input was modified since the previous run.
if (rule_->restat() && build_log &&
(entry = build_log->LookupByOutput(output->path()))) {
if (entry->restat_mtime < most_recent_input)
if (entry->restat_mtime < most_recent_input) {
EXPLAIN("restat of output %s older than inputs", output->path().c_str());
return true;
}
} else {
EXPLAIN("output %s older than most recent input %s (%d vs %d)",
output->path().c_str(),
most_recent_node ? most_recent_node->path().c_str() : "",
output->mtime(), most_recent_input);
return true;
}
}
Expand All @@ -140,8 +157,10 @@ bool Edge::RecomputeOutputDirty(BuildLog* build_log,
// dirty.
if (!rule_->generator() && build_log &&
(entry || (entry = build_log->LookupByOutput(output->path())))) {
if (command != entry->command)
if (command != entry->command) {
EXPLAIN("command line changed for %s", output->path().c_str());
return true;
}
}

return false;
Expand Down
3 changes: 2 additions & 1 deletion src/graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ struct Edge {
/// Recompute whether a given single output should be marked dirty.
/// Returns true if so.
bool RecomputeOutputDirty(BuildLog* build_log, TimeStamp most_recent_input,
const string& command, Node* output);
Node* most_recent_node, const string& command,
Node* output);

/// Return true if all inputs' in-edges are ready.
bool AllInputsReady() const;
Expand Down
9 changes: 7 additions & 2 deletions src/ninja.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "build_log.h"
#include "clean.h"
#include "edit_distance.h"
#include "explain.h"
#include "graph.h"
#include "graphviz.h"
#include "metrics.h"
Expand Down Expand Up @@ -567,12 +568,16 @@ int RunTool(const string& tool, Globals* globals, int argc, char** argv) {
bool DebugEnable(const string& name, Globals* globals) {
if (name == "list") {
printf("debugging modes:\n"
" stats print operation counts/timing info\n");
//"multiple modes can be enabled via -d FOO -d BAR\n");
" stats print operation counts/timing info\n"
" explain explain what caused a command to execute\n"
"multiple modes can be enabled via -d FOO -d BAR\n");
return false;
} else if (name == "stats") {
g_metrics = new Metrics;
return true;
} else if (name == "explain") {
g_explaining = true;
return true;
} else {
printf("ninja: unknown debug setting '%s'\n", name.c_str());
return false;
Expand Down

0 comments on commit 1aae1bc

Please sign in to comment.