diff options
| author | Colin Cross <ccross@android.com> | 2015-10-26 16:43:39 -0700 |
|---|---|---|
| committer | Colin Cross <ccross@android.com> | 2015-10-26 16:44:04 -0700 |
| commit | d72dd1e220c4bcd69579590c49ef345de3766bda (patch) | |
| tree | 2b2bc9a8cc66b65a08e5128f4097cda2f5888f34 | |
| parent | f39f4f76b603a6e1e10e3b3e65698a1864825591 (diff) | |
| parent | 4421dda489d793ea1a59b1acf460b540b3b4174b (diff) | |
| download | platform_build_kati-brillo-m7-dev.tar.gz platform_build_kati-brillo-m7-dev.tar.bz2 platform_build_kati-brillo-m7-dev.zip | |
Merge remote-tracking branch 'aosp/upstream' into katibrillo-m7-releasebrillo-m7-dev
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
| -rw-r--r-- | .travis.yml | 3 | ||||
| -rw-r--r-- | eval.cc | 6 | ||||
| -rw-r--r-- | fileutil.cc | 14 | ||||
| -rw-r--r-- | fileutil.h | 1 | ||||
| -rw-r--r-- | find.cc | 276 | ||||
| -rw-r--r-- | find.h | 3 | ||||
| -rw-r--r-- | find_test.cc | 7 | ||||
| -rw-r--r-- | func.cc | 10 | ||||
| -rw-r--r-- | main.cc | 39 | ||||
| -rw-r--r-- | ninja.cc | 20 | ||||
| -rwxr-xr-x | runtest.rb | 10 | ||||
| -rw-r--r-- | strutil.cc | 6 | ||||
| -rw-r--r-- | strutil_test.cc | 4 | ||||
| -rw-r--r-- | testcase/err_ifdef_two_args.mk | 4 | ||||
| -rw-r--r-- | testcase/err_ifdef_two_args2.mk | 5 | ||||
| -rw-r--r-- | testcase/excl_in_shell.mk | 2 | ||||
| -rw-r--r-- | testcase/find_command.mk | 45 | ||||
| -rw-r--r-- | testcase/ifdef_ret_in_arg.mk | 8 | ||||
| -rw-r--r-- | testcase/ninja_normalized_path2.mk | 15 | ||||
| -rwxr-xr-x | testcase/ninja_regen_find_link.sh | 44 | ||||
| -rw-r--r-- | testcase/realpath.mk | 4 | ||||
| -rw-r--r-- | testcase/wildcard.mk | 2 |
22 files changed, 395 insertions, 133 deletions
diff --git a/.travis.yml b/.travis.yml index 9d2dcc5..9c785dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,8 @@ cache: apt 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 @@ -187,7 +187,11 @@ void Evaluator::EvalIf(const IfStmt* stmt) { 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 @@ bool Exists(StringPiece filename) { 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, @@ -25,6 +25,7 @@ using namespace std; bool Exists(StringPiece f); +double GetTimestampFromStat(const struct stat& st); double GetTimestamp(StringPiece f); enum struct RedirectStderr { @@ -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 @@ class NameCond : public FindCond { 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 @@ class TypeCond : public FindCond { 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 @@ class NotCond : public FindCond { 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 @@ class AndCond : public FindCond { 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 @@ class OrCond : public FindCond { 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 @@ class DirentNode { 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 @@ class DirentNode { 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 @@ class DirentFileNode : public DirentNode { } 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 @@ class DirentDirNode : public DirentNode { } 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 @@ class DirentDirNode : public DirentNode { 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 @@ class DirentDirNode : public DirentNode { 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 @@ class DirentDirNode : public DirentNode { 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 @@ class DirentDirNode : public DirentNode { 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 DirentDirNode : public DirentNode { 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 += '/'; - } - 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; - } + 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_)); } + 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 @@ class FindCommandParser { bool ParseFindLeaves() { fc_->type = FindCommandType::FINDLEAVES; + fc_->follows_symlinks = true; StringPiece tok; while (true) { if (!GetNextToken(&tok)) @@ -663,6 +710,14 @@ class FindEmulatorImpl : public FindEmulator { !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 @@ class FindEmulatorImpl : public FindEmulator { 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 @@ class FindEmulatorImpl : public FindEmulator { 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 @@ class FindEmulatorImpl : public FindEmulator { 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 @@ class FindEmulatorImpl : public FindEmulator { 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 @@ class FindEmulatorImpl : public FindEmulator { } 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 @@ class FindEmulatorImpl : public FindEmulator { } 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 @@ class FindEmulatorImpl : public FindEmulator { } } + 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 @@ class FindEmulatorImpl : public FindEmulator { 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 @@ class FindEmulatorImpl : public FindEmulator { 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 @@ class FindEmulatorImpl : public FindEmulator { 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() { @@ -17,6 +17,7 @@ #include <memory> #include <string> +#include <unordered_set> #include <vector> #include "string_piece.h" @@ -48,7 +49,7 @@ struct FindCommand { 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 @@ int main(int argc, char* argv[]) { 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)); + } } @@ -58,7 +58,7 @@ void StripShellComment(string* cmd) { 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 AddprefixFunc(const vector<Value*>& args, Evaluator* ev, string* s) { 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 @@ void RealpathFunc(const vector<Value*>& args, Evaluator* ev, string* s) { ScopedTerminator st(tok); char buf[PATH_MAX]; if (realpath(tok.data(), buf)) - *s += buf; + ww.Write(buf); } } @@ -47,18 +47,6 @@ static void Init() { 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 @@ static int Run(const vector<Symbol>& targets, 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 @@ int main(int argc, char* argv[]) { 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. @@ -471,7 +471,7 @@ class NinjaGenerator { } 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 @@ class NinjaGenerator { break; case '`': case '"': - case '!': case '\\': r += '\\'; // fall through. @@ -932,8 +931,21 @@ bool NeedsRegen(double start_time, const string& orig_args) { // 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) { @@ -102,7 +102,7 @@ def normalize_ninja_log(log, mk) 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 @@ def normalize_make_log(expected, mk, via_ninja) 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 @@ def normalize_kati_log(output) 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 @@ def normalize_kati_log(output) 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 @@ puts if !unexpected_passes.empty? || !failures.empty? puts "FAIL! (#{failures.size + unexpected_passes.size} fails #{passes.size} passes)" + exit 1 else puts 'PASS!' end @@ -297,6 +297,7 @@ void NormalizePath(string* o) { // /.. j = start_index; } else { + size_t orig_j = j; j -= 4; j = o->rfind('/', j); if (j == string::npos) { @@ -304,6 +305,11 @@ void NormalizePath(string* o) { } 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 @@ void TestNormalizePath() { 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 @@ test1: 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 @@ endif $(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*) |
