Merge remote-tracking branch 'aosp/upstream' into kati
4421dda [C++] ifdef should fail when there are multiple variables
407d8d4 [C++] Resolve symlinks in the top directory
31505ba [C++] Fix find -name for symlinks
f2d3172 [C++] Fix a memory corruption
6ce977d [C++] Regenerate ninja files when symlink was changed
9c5ec1d [go] Mark find_command.mk and wildcard.mk as FAIL
b717561 [C++] Make FindEmulator's symlink support better
14ea0f1 [C++] Fix NormalizePath("../../foo")
bd3bb23 [C++] Fix FindEmulator for paths in symlinks
c58db99 [C++] Make the fast pass of EscapeShell consistent with the rest
5de5826 [C++] Do not escape ! in a shell script
71cf60b [C++] Stop using realpath(1) to handle $(realpath) in recipe
cb4724e [C++] Fix realpath implementation for multiple parameters
34556cc [C++] Do not find first Makefile when -f is specified
171dc82 [C++] Make the output of find_test compatible with find(1)
680e98b Exit 1 in runtest.rb
9fe05bf Install realpath
9d918f8 Remove ninja_normalized_path2.mk
212b7a5 [C++] Fix a off-by-one error in StripShellComment
d0251f7 Change the shell in Travis CI to bash
0613e01 Revert "Always use SHELL=/bin/bash in our test"
a73e93b Always use SHELL=/bin/bash in our test
b7be8f1 [C++] Fix err_include.mk for -c -n
3c60c12 [C++] Fix err_include.mk
e3c6231 Install ninja-build on Travis CI
Bug: 24580686
Change-Id: I2419d78affcd4cfd1de284a7dc79c0e8a4535fc9
diff --git a/.travis.yml b/.travis.yml
index 9d2dcc5..9c785dc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,7 +8,8 @@
before_script:
- sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu/ trusty main universe"
- sudo apt-get update -qq
- - sudo apt-get install -y libstdc++-4.8-dev clang-3.5
+ - sudo apt-get install -y libstdc++-4.8-dev clang-3.5 ninja-build realpath
+ - sudo ln -sf bash /bin/sh
script:
- make -j4 ckati
diff --git a/eval.cc b/eval.cc
index 72661e3..25b9c52 100644
--- a/eval.cc
+++ b/eval.cc
@@ -187,7 +187,11 @@
switch (stmt->op) {
case CondOp::IFDEF:
case CondOp::IFNDEF: {
- Symbol lhs = Intern(stmt->lhs->Eval(this));
+ string var_name;
+ stmt->lhs->Eval(this, &var_name);
+ if (var_name.find_first_of(" \t") != string::npos)
+ Error("*** invalid syntax in conditional.");
+ Symbol lhs = Intern(var_name);
Var* v = LookupVarInCurrentScope(lhs);
const string&& s = v->Eval(this);
is_true = (s.empty() == (stmt->op == CondOp::IFNDEF));
diff --git a/fileutil.cc b/fileutil.cc
index d2035f3..abfad9d 100644
--- a/fileutil.cc
+++ b/fileutil.cc
@@ -41,17 +41,21 @@
return true;
}
+double GetTimestampFromStat(const struct stat& st) {
+#if defined(__linux__)
+ return st.st_mtime + st.st_mtim.tv_nsec * 0.001 * 0.001 * 0.001;
+#else
+ return st.st_mtime;
+#endif
+}
+
double GetTimestamp(StringPiece filename) {
CHECK(filename.size() < PATH_MAX);
struct stat st;
if (stat(filename.as_string().c_str(), &st) < 0) {
return -2.0;
}
-#if defined(__linux__)
- return st.st_mtime + st.st_mtim.tv_nsec * 0.001 * 0.001 * 0.001;
-#else
- return st.st_mtime;
-#endif
+ return GetTimestampFromStat(st);
}
int RunCommand(const string& shell, const string& cmd,
diff --git a/fileutil.h b/fileutil.h
index ff7b9e0..5a4740d 100644
--- a/fileutil.h
+++ b/fileutil.h
@@ -25,6 +25,7 @@
using namespace std;
bool Exists(StringPiece f);
+double GetTimestampFromStat(const struct stat& st);
double GetTimestamp(StringPiece f);
enum struct RedirectStderr {
diff --git a/find.cc b/find.cc
index eb796f1..4c7cd8f 100644
--- a/find.cc
+++ b/find.cc
@@ -28,6 +28,7 @@
//#undef NOLOG
+#include "fileutil.h"
#include "log.h"
#include "string_piece.h"
#include "strutil.h"
@@ -36,7 +37,7 @@
class FindCond {
public:
virtual ~FindCond() = default;
- virtual bool IsTrue(const string& name, unsigned char type) const = 0;
+ virtual bool IsTrue(const string& path, unsigned char type) const = 0;
protected:
FindCond() = default;
};
@@ -48,8 +49,8 @@
explicit NameCond(const string& n)
: name_(n) {
}
- virtual bool IsTrue(const string& name, unsigned char) const {
- return fnmatch(name_.c_str(), name.c_str(), 0) == 0;
+ virtual bool IsTrue(const string& path, unsigned char) const override {
+ return fnmatch(name_.c_str(), Basename(path).data(), 0) == 0;
}
private:
string name_;
@@ -60,7 +61,7 @@
explicit TypeCond(unsigned char t)
: type_(t) {
}
- virtual bool IsTrue(const string&, unsigned char type) const {
+ virtual bool IsTrue(const string&, unsigned char type) const override {
return type == type_;
}
private:
@@ -72,8 +73,8 @@
NotCond(FindCond* c)
: c_(c) {
}
- virtual bool IsTrue(const string& name, unsigned char type) const {
- return !c_->IsTrue(name, type);
+ virtual bool IsTrue(const string& path, unsigned char type) const override {
+ return !c_->IsTrue(path, type);
}
private:
unique_ptr<FindCond> c_;
@@ -84,9 +85,9 @@
AndCond(FindCond* c1, FindCond* c2)
: c1_(c1), c2_(c2) {
}
- virtual bool IsTrue(const string& name, unsigned char type) const {
- if (c1_->IsTrue(name, type))
- return c2_->IsTrue(name, type);
+ virtual bool IsTrue(const string& path, unsigned char type) const override {
+ if (c1_->IsTrue(path, type))
+ return c2_->IsTrue(path, type);
return false;
}
private:
@@ -98,9 +99,9 @@
OrCond(FindCond* c1, FindCond* c2)
: c1_(c1), c2_(c2) {
}
- virtual bool IsTrue(const string& name, unsigned char type) const {
- if (!c1_->IsTrue(name, type))
- return c2_->IsTrue(name, type);
+ virtual bool IsTrue(const string& path, unsigned char type) const override {
+ if (!c1_->IsTrue(path, type))
+ return c2_->IsTrue(path, type);
return true;
}
private:
@@ -115,7 +116,11 @@
return NULL;
}
virtual bool RunFind(const FindCommand& fc, int d,
- string* path, string* out) const = 0;
+ string* path,
+ unordered_map<const DirentNode*, string>* cur_read_dirs,
+ string* out) const = 0;
+
+ virtual bool IsDirectory() const = 0;
const string& base() const { return base_; }
@@ -129,7 +134,7 @@
unsigned char type,
int d,
string* out) const {
- if (fc.print_cond && !fc.print_cond->IsTrue(base_, type))
+ if (fc.print_cond && !fc.print_cond->IsTrue(path, type))
return;
if (d < fc.mindepth)
return;
@@ -147,15 +152,47 @@
}
virtual bool RunFind(const FindCommand& fc, int d,
- string* path, string* out) const {
+ string* path,
+ unordered_map<const DirentNode*, string>*,
+ string* out) const {
PrintIfNecessary(fc, *path, type_, d, out);
return true;
}
+ virtual bool IsDirectory() const override { return false; }
+
private:
unsigned char type_;
};
+struct ScopedReadDirTracker {
+ public:
+ ScopedReadDirTracker(const DirentNode* n,
+ const string& path,
+ unordered_map<const DirentNode*, string>* cur_read_dirs)
+ : n_(NULL), cur_read_dirs_(cur_read_dirs) {
+ const auto& p = cur_read_dirs->emplace(n, path);
+ if (p.second) {
+ n_ = n;
+ } else {
+ conflicted_ = p.first->second;
+ }
+ }
+
+ ~ScopedReadDirTracker() {
+ if (n_)
+ cur_read_dirs_->erase(n_);
+ }
+
+ bool ok() const { return conflicted_.empty(); }
+ const string& conflicted() const { return conflicted_; }
+
+ private:
+ string conflicted_;
+ const DirentNode* n_;
+ unordered_map<const DirentNode*, string>* cur_read_dirs_;
+};
+
class DirentDirNode : public DirentNode {
public:
explicit DirentDirNode(const string& name)
@@ -185,10 +222,20 @@
}
virtual bool RunFind(const FindCommand& fc, int d,
- string* path, string* out) const {
- fc.read_dirs->push_back(*path);
+ string* path,
+ unordered_map<const DirentNode*, string>* cur_read_dirs,
+ string* out) const {
+ ScopedReadDirTracker srdt(this, *path, cur_read_dirs);
+ if (!srdt.ok()) {
+ fprintf(stderr, "FindEmulator: find: File system loop detected; `%s' is "
+ "part of the same file system loop as `%s'.\n",
+ path->c_str(), srdt.conflicted().c_str());
+ return true;
+ }
- if (fc.prune_cond && fc.prune_cond->IsTrue(base_, DT_DIR)) {
+ fc.read_dirs->insert(*path);
+
+ if (fc.prune_cond && fc.prune_cond->IsTrue(*path, DT_DIR)) {
if (fc.type != FindCommandType::FINDLEAVES) {
*out += *path;
*out += ' ';
@@ -207,12 +254,12 @@
for (const auto& p : children_) {
DirentNode* c = p.second;
// We will handle directories later.
- if (dynamic_cast<DirentDirNode*>(c))
+ if (c->IsDirectory())
continue;
if ((*path)[path->size()-1] != '/')
*path += '/';
*path += c->base();
- if (!c->RunFind(fc, d + 1, path, out))
+ if (!c->RunFind(fc, d + 1, path, cur_read_dirs, out))
return false;
path->resize(orig_path_size);
// Found a leaf, stop the search.
@@ -222,12 +269,12 @@
for (const auto& p : children_) {
DirentNode* c = p.second;
- if (!dynamic_cast<DirentDirNode*>(c))
+ if (!c->IsDirectory())
continue;
if ((*path)[path->size()-1] != '/')
*path += '/';
*path += c->base();
- if (!c->RunFind(fc, d + 1, path, out))
+ if (!c->RunFind(fc, d + 1, path, cur_read_dirs, out))
return false;
path->resize(orig_path_size);
}
@@ -237,7 +284,7 @@
if ((*path)[path->size()-1] != '/')
*path += '/';
*path += c->base();
- if (!c->RunFind(fc, d + 1, path, out))
+ if (!c->RunFind(fc, d + 1, path, cur_read_dirs, out))
return false;
path->resize(orig_path_size);
}
@@ -245,6 +292,8 @@
return true;
}
+ virtual bool IsDirectory() const override { return true; }
+
void Add(const string& name, DirentNode* c) {
children_.emplace(children_.end(), name, c);
}
@@ -256,58 +305,55 @@
class DirentSymlinkNode : public DirentNode {
public:
explicit DirentSymlinkNode(const string& name)
- : DirentNode(name) {
+ : DirentNode(name), to_(NULL), errno_(0) {
+ }
+
+ virtual const DirentNode* FindDir(StringPiece d) const {
+ if (errno_ == 0 && to_)
+ return to_->FindDir(d);
+ return NULL;
}
virtual bool RunFind(const FindCommand& fc, int d,
- string* path, string* out) const {
+ string* path,
+ unordered_map<const DirentNode*, string>* cur_read_dirs,
+ string* out) const {
unsigned char type = DT_LNK;
- if (fc.follows_symlinks) {
- // TODO
- LOG("FindEmulator: symlink is hard");
- return false;
-
- char buf[PATH_MAX+1];
- buf[PATH_MAX] = 0;
- LOG("path=%s", path->c_str());
- ssize_t len = readlink(path->c_str(), buf, PATH_MAX);
- if (len > 0) {
- buf[len] = 0;
- string oldpath;
- if (buf[0] != '/') {
- Dirname(*path).AppendToString(&oldpath);
- oldpath += '/';
+ if (fc.follows_symlinks && errno_ != ENOENT) {
+ if (errno_) {
+ if (fc.type != FindCommandType::FINDLEAVES) {
+ fprintf(stderr, "FindEmulator: find: `%s': %s\n",
+ path->c_str(), strerror(errno_));
}
- oldpath += buf;
-
- LOG("buf=%s old=%s", buf, oldpath.c_str());
-
- struct stat st;
- if (stat(oldpath.c_str(), &st) == 0) {
- LOG("st OK");
- if (S_ISREG(st.st_mode)) {
- type = DT_REG;
- } else if (S_ISDIR(st.st_mode)) {
- type = DT_DIR;
- } else if (S_ISCHR(st.st_mode)) {
- type = DT_CHR;
- } else if (S_ISBLK(st.st_mode)) {
- type = DT_BLK;
- } else if (S_ISFIFO(st.st_mode)) {
- type = DT_FIFO;
- } else if (S_ISLNK(st.st_mode)) {
- type = DT_LNK;
- } else if (S_ISSOCK(st.st_mode)) {
- type = DT_SOCK;
- } else {
- return false;
- }
- }
+ return true;
}
+
+ if (!to_) {
+ LOG("FindEmulator does not support %s", path->c_str());
+ return false;
+ }
+
+ return to_->RunFind(fc, d, path, cur_read_dirs, out);
}
PrintIfNecessary(fc, *path, type, d, out);
return true;
}
+
+ virtual bool IsDirectory() const override {
+ return errno_ == 0 && to_ && to_->IsDirectory();
+ }
+
+ void set_to(const DirentNode* to) {
+ to_ = to;
+ }
+
+ void set_errno(int e) {
+ errno_ = e;
+ }
+
+ private:
+ const DirentNode* to_;
+ int errno_;
};
class FindCommandParser {
@@ -545,6 +591,7 @@
bool ParseFindLeaves() {
fc_->type = FindCommandType::FINDLEAVES;
+ fc_->follows_symlinks = true;
StringPiece tok;
while (true) {
if (!GetNextToken(&tok))
@@ -663,6 +710,14 @@
!HasPrefix(s, "out"));
}
+ const DirentNode* FindDir(StringPiece d, bool* should_fallback) {
+ const DirentNode* r = root_->FindDir(d);
+ if (!r) {
+ *should_fallback = Exists(d);
+ }
+ return r;
+ }
+
virtual bool HandleFind(const string& cmd UNUSED, const FindCommand& fc,
string* out) override {
if (!CanHandle(fc.chdir)) {
@@ -674,6 +729,7 @@
if (!is_initialized_) {
ScopedTimeReporter tr("init find emulator time");
root_.reset(ConstructDirectoryTree(""));
+ ResolveSymlinks();
LOG_STAT("%d find nodes", node_cnt_);
is_initialized_ = true;
}
@@ -684,10 +740,11 @@
SPF(fc.testdir), cmd.c_str());
return false;
}
- if (!root_->FindDir(fc.testdir)) {
+ bool should_fallback = false;
+ if (!FindDir(fc.testdir, &should_fallback)) {
LOG("FindEmulator: Test dir (%.*s) not found: %s",
SPF(fc.testdir), cmd.c_str());
- return true;
+ return !should_fallback;
}
}
@@ -697,7 +754,10 @@
SPF(fc.chdir), cmd.c_str());
return false;
}
- if (!root_->FindDir(fc.chdir)) {
+ bool should_fallback = false;
+ if (!FindDir(fc.chdir, &should_fallback)) {
+ if (should_fallback)
+ return false;
if (!fc.redirect_to_devnull) {
fprintf(stderr,
"FindEmulator: cd: %.*s: No such file or directory\n",
@@ -718,8 +778,13 @@
return false;
}
- const DirentNode* base = root_->FindDir(dir);
+ bool should_fallback = false;
+ const DirentNode* base = FindDir(dir, &should_fallback);
if (!base) {
+ if (should_fallback) {
+ out->resize(orig_out_size);
+ return false;
+ }
if (!fc.redirect_to_devnull) {
fprintf(stderr,
"FindEmulator: find: `%s': No such file or directory\n",
@@ -729,7 +794,8 @@
}
string path = finddir.as_string();
- if (!base->RunFind(fc, 0, &path, out)) {
+ unordered_map<const DirentNode*, string> cur_read_dirs;
+ if (!base->RunFind(fc, 0, &path, &cur_read_dirs, out)) {
LOG("FindEmulator: RunFind failed: %s", cmd.c_str());
out->resize(orig_out_size);
return false;
@@ -748,12 +814,7 @@
}
private:
- static unsigned char GetDtType(const string& path) {
- struct stat st;
- if (lstat(path.c_str(), &st)) {
- PERROR("stat for %s", path.c_str());
- }
-
+ static unsigned char GetDtTypeFromStat(const struct stat& st) {
if (S_ISREG(st.st_mode)) {
return DT_REG;
} else if (S_ISDIR(st.st_mode)) {
@@ -773,6 +834,14 @@
}
}
+ static unsigned char GetDtType(const string& path) {
+ struct stat st;
+ if (lstat(path.c_str(), &st)) {
+ PERROR("stat for %s", path.c_str());
+ }
+ return GetDtTypeFromStat(st);
+ }
+
DirentNode* ConstructDirectoryTree(const string& path) {
DIR* dir = opendir(path.empty() ? "." : path.c_str());
if (!dir)
@@ -803,7 +872,9 @@
if (d_type == DT_DIR) {
c = ConstructDirectoryTree(npath);
} else if (d_type == DT_LNK) {
- c = new DirentSymlinkNode(npath);
+ auto s = new DirentSymlinkNode(npath);
+ symlinks_.push_back(make_pair(npath, s));
+ c = s;
} else {
c = new DirentFileNode(npath, d_type);
}
@@ -815,7 +886,56 @@
return n;
}
+ void ResolveSymlinks() {
+ vector<pair<string, DirentSymlinkNode*>> symlinks;
+ symlinks.swap(symlinks_);
+ for (const auto& p : symlinks) {
+ const string& path = p.first;
+ DirentSymlinkNode* s = p.second;
+
+ char buf[PATH_MAX+1];
+ buf[PATH_MAX] = 0;
+ ssize_t len = readlink(path.c_str(), buf, PATH_MAX);
+ if (len < 0) {
+ WARN("readlink failed: %s", path.c_str());
+ continue;
+ }
+ buf[len] = 0;
+
+ struct stat st;
+ unsigned char type = DT_UNKNOWN;
+ if (stat(path.c_str(), &st) == 0) {
+ type = GetDtTypeFromStat(st);
+ } else {
+ s->set_errno(errno);
+ LOG("stat failed: %s: %s", path.c_str(), strerror(errno));
+ }
+
+ if (*buf != '/') {
+ const string npath = ConcatDir(Dirname(path), buf);
+ bool should_fallback = false;
+ const DirentNode* to = FindDir(npath, &should_fallback);
+ if (to) {
+ s->set_to(to);
+ continue;
+ }
+ }
+
+ if (type == DT_DIR) {
+ if (path.find('/') == string::npos) {
+ s->set_to(ConstructDirectoryTree(path));
+ }
+ } else if (type != DT_LNK && type != DT_UNKNOWN) {
+ s->set_to(new DirentFileNode(path, type));
+ }
+ }
+
+ if (!symlinks_.empty())
+ ResolveSymlinks();
+ }
+
unique_ptr<DirentNode> root_;
+ vector<pair<string, DirentSymlinkNode*>> symlinks_;
int node_cnt_;
bool is_initialized_;
};
@@ -825,7 +945,7 @@
FindCommand::FindCommand()
: follows_symlinks(false), depth(INT_MAX), mindepth(INT_MIN),
redirect_to_devnull(false),
- read_dirs(new vector<string>()) {
+ read_dirs(new unordered_set<string>()) {
}
FindCommand::~FindCommand() {
diff --git a/find.h b/find.h
index 725451c..ccd50e0 100644
--- a/find.h
+++ b/find.h
@@ -17,6 +17,7 @@
#include <memory>
#include <string>
+#include <unordered_set>
#include <vector>
#include "string_piece.h"
@@ -48,7 +49,7 @@
int mindepth;
bool redirect_to_devnull;
- unique_ptr<vector<string>> read_dirs;
+ unique_ptr<unordered_set<string>> read_dirs;
private:
FindCommand(const FindCommand&) = delete;
diff --git a/find_test.cc b/find_test.cc
index b4664a2..1ba451a 100644
--- a/find_test.cc
+++ b/find_test.cc
@@ -18,6 +18,8 @@
#include <string>
+#include "strutil.h"
+
int main(int argc, char* argv[]) {
if (argc == 1) {
fprintf(stderr, "TODO: Write unit tests\n");
@@ -41,5 +43,8 @@
fprintf(stderr, "Find emulator does not support this command\n");
return 1;
}
- printf("%s\n", out.c_str());
+
+ for (StringPiece tok : WordScanner(out)) {
+ printf("%.*s\n", SPF(tok));
+ }
}
diff --git a/func.cc b/func.cc
index 6368e02..34fca3e 100644
--- a/func.cc
+++ b/func.cc
@@ -58,7 +58,7 @@
switch (*in) {
case '#':
if (quote == 0 && isspace(prev_char)) {
- while (*in && *in != '\n')
+ while (in[1] && *in != '\n')
in++;
break;
}
@@ -383,7 +383,11 @@
void RealpathFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
const string&& text = args[0]->Eval(ev);
if (ev->avoid_io()) {
- *s += "$(realpath ";
+ *s += "$(";
+ string kati_binary;
+ GetExecutablePath(&kati_binary);
+ *s += kati_binary;
+ *s += " --realpath ";
*s += text;
*s += " 2> /dev/null)";
return;
@@ -394,7 +398,7 @@
ScopedTerminator st(tok);
char buf[PATH_MAX];
if (realpath(tok.data(), buf))
- *s += buf;
+ ww.Write(buf);
}
}
diff --git a/main.cc b/main.cc
index 5918601..930348b 100644
--- a/main.cc
+++ b/main.cc
@@ -47,18 +47,6 @@
InitFuncTable();
InitDepNodePool();
InitParser();
-
- if (g_flags.makefile == NULL) {
- if (Exists("GNUmakefile")) {
- g_flags.makefile = "GNUmakefile";
-#if !defined(__APPLE__)
- } else if (Exists("makefile")) {
- g_flags.makefile = "makefile";
-#endif
- } else if (Exists("Makefile")) {
- g_flags.makefile = "Makefile";
- }
- }
}
static void Quit() {
@@ -225,7 +213,33 @@
return 0;
}
+static void FindFirstMakefie() {
+ if (g_flags.makefile != NULL)
+ return;
+ if (Exists("GNUmakefile")) {
+ g_flags.makefile = "GNUmakefile";
+#if !defined(__APPLE__)
+ } else if (Exists("makefile")) {
+ g_flags.makefile = "makefile";
+#endif
+ } else if (Exists("Makefile")) {
+ g_flags.makefile = "Makefile";
+ }
+}
+
+static void HandleRealpath(int argc, char** argv) {
+ char buf[PATH_MAX];
+ for (int i = 0; i < argc; i++) {
+ if (realpath(argv[i], buf))
+ printf("%s\n", buf);
+ }
+}
+
int main(int argc, char* argv[]) {
+ if (argc >= 2 && !strcmp(argv[1], "--realpath")) {
+ HandleRealpath(argc - 2, argv + 2);
+ return 0;
+ }
Init();
string orig_args;
for (int i = 0; i < argc; i++) {
@@ -234,6 +248,7 @@
orig_args += argv[i];
}
g_flags.Parse(argc, argv);
+ FindFirstMakefie();
if (g_flags.makefile == NULL)
ERROR("*** No targets specified and no makefile found.");
// This depends on command line flags.
diff --git a/ninja.cc b/ninja.cc
index 4e4c460..21c935f 100644
--- a/ninja.cc
+++ b/ninja.cc
@@ -471,7 +471,7 @@
}
void EscapeShell(string* s) const {
- if (s->find_first_of("$`!\\\"") == string::npos)
+ if (s->find_first_of("$`\\\"") == string::npos)
return;
string r;
bool last_dollar = false;
@@ -489,7 +489,6 @@
break;
case '`':
case '"':
- case '!':
case '\\':
r += '\\';
// fall through.
@@ -932,8 +931,21 @@
// directory which affects the results of find command.
if (s == "" || s == "." || ShouldIgnoreDirty(s))
continue;
- double ts = GetTimestamp(s);
- should_run_command |= (ts < 0 || gen_time < ts);
+
+ struct stat st;
+ if (lstat(s.c_str(), &st) != 0) {
+ should_run_command = true;
+ continue;
+ }
+ double ts = GetTimestampFromStat(st);
+ if (gen_time < ts) {
+ should_run_command = true;
+ continue;
+ }
+ if (S_ISLNK(st.st_mode)) {
+ ts = GetTimestamp(s);
+ should_run_command |= (ts < 0 || gen_time < ts);
+ }
}
if (!should_run_command) {
diff --git a/runtest.rb b/runtest.rb
index a3babfe..c4938b9 100755
--- a/runtest.rb
+++ b/runtest.rb
@@ -102,7 +102,7 @@
log.gsub!(/^ninja: error: (.*, needed by .*),.*/,
'*** No rule to make target \\1.')
log.gsub!(/^ninja: warning: multiple rules generate (.*)\. builds involving this target will not be correct.*$/,
- 'ninja: warning: multiple rules generate \\1.')
+ 'ninja: warning: multiple rules generate \\1.')
if mk =~ /err_error_in_recipe.mk/
# This test expects ninja fails. Strip ninja specific error logs.
log.gsub!(/^FAILED: .*\n/, '')
@@ -135,6 +135,8 @@
if !via_ninja
expected.gsub!(/^ninja: warning: .*\n/, '')
end
+ # Normalization for "include foo" with C++ kati.
+ expected.gsub!(/(: )(\S+): (No such file or directory)\n\*\*\* No rule to make target "\2"./, '\1\2: \3')
expected
end
@@ -145,8 +147,6 @@
output.gsub!(/^\*kati\*.*\n/, '')
output.gsub!(/^c?kati: /, '')
output.gsub!(/[`'"]/, '"')
- output.gsub!(/(: )(?:open )?(\S+): [Nn](o such file or directory)\nNOTE:.*/,
- "\\1\\2: N\\3\n*** No rule to make target \"\\2\".")
output.gsub!(/\/bin\/sh: ([^:]*): command not found/,
"\\1: Command not found")
output.gsub!(/.*: warning for parse error in an unevaluated line: .*\n/, '')
@@ -154,6 +154,9 @@
output.gsub!(/^\/bin\/sh: line 0: /, '')
output.gsub!(/ (\.\/+)+kati\.\S+/, '') # kati log files in find_command.mk
output.gsub!(/ (\.\/+)+test\S+.json/, '') # json files in find_command.mk
+ # Normalization for "include foo" with Go kati.
+ output.gsub!(/(: )open (\S+): n(o such file or directory)\nNOTE:.*/,
+ "\\1\\2: N\\3")
output
end
@@ -390,6 +393,7 @@
if !unexpected_passes.empty? || !failures.empty?
puts "FAIL! (#{failures.size + unexpected_passes.size} fails #{passes.size} passes)"
+ exit 1
else
puts 'PASS!'
end
diff --git a/strutil.cc b/strutil.cc
index 09deaf4..7707482 100644
--- a/strutil.cc
+++ b/strutil.cc
@@ -297,6 +297,7 @@
// /..
j = start_index;
} else {
+ size_t orig_j = j;
j -= 4;
j = o->rfind('/', j);
if (j == string::npos) {
@@ -304,6 +305,11 @@
} else {
j++;
}
+ if (StringPiece(o->data() + j, 3) == "../") {
+ j = orig_j;
+ (*o)[j] = c;
+ j++;
+ }
}
} else if (!prev_dir.empty()) {
if (c) {
diff --git a/strutil_test.cc b/strutil_test.cc
index 048a974..eb1e195 100644
--- a/strutil_test.cc
+++ b/strutil_test.cc
@@ -109,6 +109,10 @@
ASSERT_EQ(NormalizePath("/../../foo"), "/foo");
ASSERT_EQ(NormalizePath("/a/../../foo"), "/foo");
ASSERT_EQ(NormalizePath("/a/b/.."), "/a");
+ ASSERT_EQ(NormalizePath("../../a/b"), "../../a/b");
+ ASSERT_EQ(NormalizePath("../../../a/b"), "../../../a/b");
+ ASSERT_EQ(NormalizePath(".././../a/b"), "../../a/b");
+ ASSERT_EQ(NormalizePath("./../../a/b"), "../../a/b");
}
} // namespace
diff --git a/testcase/err_ifdef_two_args.mk b/testcase/err_ifdef_two_args.mk
new file mode 100644
index 0000000..4c9eb74
--- /dev/null
+++ b/testcase/err_ifdef_two_args.mk
@@ -0,0 +1,4 @@
+# TODO(go): Fix
+
+ifdef a b
+endif
diff --git a/testcase/err_ifdef_two_args2.mk b/testcase/err_ifdef_two_args2.mk
new file mode 100644
index 0000000..43231f7
--- /dev/null
+++ b/testcase/err_ifdef_two_args2.mk
@@ -0,0 +1,5 @@
+# TODO(go): Fix
+
+x := a b
+ifdef $(x)
+endif
diff --git a/testcase/excl_in_shell.mk b/testcase/excl_in_shell.mk
new file mode 100644
index 0000000..1eccf4d
--- /dev/null
+++ b/testcase/excl_in_shell.mk
@@ -0,0 +1,2 @@
+test:
+ @if ! false; then echo PASS; else echo FAIL; fi
diff --git a/testcase/find_command.mk b/testcase/find_command.mk
index 0947091..a0bbc82 100644
--- a/testcase/find_command.mk
+++ b/testcase/find_command.mk
@@ -1,5 +1,5 @@
-# TODO(ninja): This test is only for ckati. ninja: multiple problems
-# go: implement generic builtin find
+# TODO(go|ninja): This test is only for ckati. ninja: multiple problems
+# go: symlink support isn't enough.
# ninja: find . finds ninja temporary files
# ninja: escaping ! doesn't seem to be working
# ninja: stderr gets reordered
@@ -33,6 +33,20 @@
mkdir -p build/tools
cp ../../testcase/tools/findleaves.py build/tools
+ mkdir -p testdir3/b/c/d
+ ln -s b testdir3/a
+ touch testdir3/b/c/d/e
+
+ mkdir -p testdir4/a/b
+ ln -s self testdir4/self
+ ln -s .. testdir4/a/b/c
+ ln -s b testdir4/a/l
+
+ mkdir -p testdir5
+ ln -s a testdir5/a
+ ln -s b testdir5/c
+ ln -s c testdir5/b
+
test2:
@echo no options
$(call run_find, find testdir)
@@ -94,14 +108,27 @@
$(call run_find, find testdir -maxdepth 1hoge)
$(call run_find, find testdir -maxdepth -1)
@echo findleaves
- $(call run_find, build/tools/findleaves.py . file1)
- $(call run_find, build/tools/findleaves.py . file3)
- $(call run_find, build/tools/findleaves.py --prune=dir1 . file3)
- $(call run_find, build/tools/findleaves.py --prune=dir1 --prune=dir2 . file3)
- $(call run_find, build/tools/findleaves.py --mindepth=1 . file1)
- $(call run_find, build/tools/findleaves.py --mindepth=2 . file1)
- $(call run_find, build/tools/findleaves.py --mindepth=3 . file1)
+ $(call run_find, build/tools/findleaves.py testdir file1)
+ $(call run_find, build/tools/findleaves.py testdir file3)
+ $(call run_find, build/tools/findleaves.py --prune=dir1 testdir file3)
+ $(call run_find, build/tools/findleaves.py --prune=dir1 --prune=dir2 testdir file3)
+ $(call run_find, build/tools/findleaves.py --mindepth=1 testdir file1)
+ $(call run_find, build/tools/findleaves.py --mindepth=2 testdir file1)
+ $(call run_find, build/tools/findleaves.py --mindepth=3 testdir file1)
$(call run_find, build/tools/findleaves.py --mindepth=2 testdir file1)
@echo missing chdir / testdir
$(call run_find, cd xxx && find .)
$(call run_find, if [ -d xxx ]; then find .; fi)
+
+test3:
+ $(call run_find, find testdir3/a/c)
+ $(call run_find, if [ -d testdir3/a/c ]; then find testdir3/a/c; fi)
+ $(call run_find, cd testdir3/a/c && find .)
+ $(call run_find, build/tools/findleaves.py testdir3 e)
+
+test4:
+ $(call run_find, find -L testdir4)
+
+test5:
+ $(call run_find, find -L testdir5)
+ $(call run_find, build/tools/findleaves.py testdir5 x)
diff --git a/testcase/ifdef_ret_in_arg.mk b/testcase/ifdef_ret_in_arg.mk
new file mode 100644
index 0000000..91d5fca
--- /dev/null
+++ b/testcase/ifdef_ret_in_arg.mk
@@ -0,0 +1,8 @@
+define x
+a
+b
+endef
+$(x):=PASS
+ifdef $(x)
+$(info $($(x)))
+endif
diff --git a/testcase/ninja_normalized_path2.mk b/testcase/ninja_normalized_path2.mk
deleted file mode 100644
index 0ec71f0..0000000
--- a/testcase/ninja_normalized_path2.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-# Ninja only supports duplicated targets for pure dependencies.
-# These will both be mapped to the same target. Two rules like
-# this should cause an warning (and really should cause an warning
-# in make too -- this can be very confusing, and can be racy)
-ifndef KATI
-$(info ninja: warning: multiple rules generate a/b.)
-endif
-
-test: a/b a/./b
-
-a/b:
- mkdir -p $(dir $@)
- echo $@
-
-a/./b:
diff --git a/testcase/ninja_regen_find_link.sh b/testcase/ninja_regen_find_link.sh
new file mode 100755
index 0000000..94a6bb1
--- /dev/null
+++ b/testcase/ninja_regen_find_link.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+set -e
+
+mk="$@"
+if echo "${mk}" | grep kati > /dev/null; then
+ mk="${mk} --use_find_emulator"
+fi
+function build() {
+ ${mk} $@ 2> /dev/null
+ if [ -e ninja.sh ]; then ./ninja.sh -j1 $@; fi
+}
+
+cat <<EOF > Makefile
+V := \$(shell find -L linkdir/d/link)
+all:
+ @echo \$(V)
+EOF
+
+mkdir -p dir1 dir2 linkdir/d
+touch dir1/file1 dir2/file2
+ln -s ../../dir1 linkdir/d/link
+build
+
+touch dir1/file1_2
+build
+
+rm linkdir/d/link
+ln -s ../../dir2 linkdir/d/link
+build
diff --git a/testcase/realpath.mk b/testcase/realpath.mk
index 8afb791..0d57e3b 100644
--- a/testcase/realpath.mk
+++ b/testcase/realpath.mk
@@ -1,9 +1,13 @@
foo = $(realpath ./foo)
bar = $(realpath ./bar)
+foofoo = $(realpath ./foo ./foo)
+foobar = $(realpath ./foo ./bar)
test: foo
echo $(foo)
echo $(bar)
+ echo $(foofoo)
+ echo $(foobar)
foo:
touch foo
diff --git a/testcase/wildcard.mk b/testcase/wildcard.mk
index 884605e..33adfd6 100644
--- a/testcase/wildcard.mk
+++ b/testcase/wildcard.mk
@@ -1,3 +1,5 @@
+# TODO(go): Fix
+
MAKEVER:=$(shell make --version | ruby -n0e 'puts $$_[/Make (\d)/,1]')
files = $(wildcard M*)