summaryrefslogtreecommitdiffstats
path: root/src/graph.cc
diff options
context:
space:
mode:
authorEvan Martin <martine@danga.com>2011-03-07 17:56:18 (GMT)
committerEvan Martin <martine@danga.com>2011-03-07 17:56:18 (GMT)
commita5980c6bb2d6ccb6ffed2d92304c55ba94622963 (patch)
tree76f248bf8fcddadd9cb89d95ea0a9db54ac4cf11 /src/graph.cc
parentad0b24ea377c447b1b282be986cdfc1343c387ff (diff)
downloadNinja-a5980c6bb2d6ccb6ffed2d92304c55ba94622963.zip
Ninja-a5980c6bb2d6ccb6ffed2d92304c55ba94622963.tar.gz
Ninja-a5980c6bb2d6ccb6ffed2d92304c55ba94622963.tar.bz2
canonicalize paths loaded from depfiles
If a C file #includes "../foo.cc", then gcc will emit paths like "bar/../foo.cc" into the dependency file; canonicalize these when we load the file. Add a test module for testing the graph dirty recomputation directly, without all the build classes around it.
Diffstat (limited to 'src/graph.cc')
-rw-r--r--src/graph.cc59
1 files changed, 58 insertions, 1 deletions
diff --git a/src/graph.cc b/src/graph.cc
index aead200..bdbcf3b 100644
--- a/src/graph.cc
+++ b/src/graph.cc
@@ -20,6 +20,59 @@
#include "ninja.h"
#include "parsers.h"
+// Canonicalize a path like "foo/../bar.h" into just "bar.h".
+bool CanonicalizePath(string* path, string* err) {
+ // Try to fast-path out the common case.
+ if (path->find("/.") == string::npos &&
+ path->find("./") == string::npos) {
+ return true;
+ }
+
+ string inpath = *path;
+ vector<const char*> parts;
+ for (string::size_type start = 0; start < inpath.size(); ++start) {
+ string::size_type end = inpath.find('/', start);
+ if (end == string::npos)
+ end = inpath.size();
+ else
+ inpath[end] = 0;
+ parts.push_back(inpath.data() + start);
+ start = end;
+ }
+
+ vector<const char*>::iterator i = parts.begin();
+ while (i != parts.end()) {
+ const char* part = *i;
+ if (part[0] == '.') {
+ if (part[1] == 0) {
+ // "."; strip.
+ parts.erase(i);
+ continue;
+ } else if (part[1] == '.' && part[2] == 0) {
+ // ".."; go up one.
+ if (i == parts.begin()) {
+ *err = "can't canonicalize path '" + *path + "' that reaches "
+ "above its directory";
+ return false;
+ }
+ --i;
+ parts.erase(i, i + 2);
+ continue;
+ }
+ }
+ ++i;
+ }
+ path->clear();
+
+ for (i = parts.begin(); i != parts.end(); ++i) {
+ if (!path->empty())
+ path->push_back('/');
+ path->append(*i);
+ }
+
+ return true;
+}
+
bool FileStat::Stat(DiskInterface* disk_interface) {
mtime_ = disk_interface->Stat(path_);
return mtime_ > 0;
@@ -124,7 +177,8 @@ string Edge::GetDescription() {
return rule_->description_.Evaluate(&env);
}
-bool Edge::LoadDepFile(State* state, DiskInterface* disk_interface, string* err) {
+bool Edge::LoadDepFile(State* state, DiskInterface* disk_interface,
+ string* err) {
EdgeEnv env(this);
string path = rule_->depfile_.Evaluate(&env);
@@ -155,6 +209,9 @@ bool Edge::LoadDepFile(State* state, DiskInterface* disk_interface, string* err)
// Add all its in-edges.
for (vector<string>::iterator i = makefile.ins_.begin();
i != makefile.ins_.end(); ++i) {
+ if (!CanonicalizePath(&*i, err))
+ return false;
+
Node* node = state->GetNode(*i);
for (vector<Node*>::iterator j = inputs_.begin(); j != inputs_.end(); ++j) {
if (*j == node) {