diff options
| author | Dan Willemsen <dwillemsen@google.com> | 2020-04-28 20:50:36 -0700 |
|---|---|---|
| committer | Dan Willemsen <dwillemsen@google.com> | 2020-04-28 20:50:36 -0700 |
| commit | 1decd163d97a3f8b7ab88cbcff501ed1de9615af (patch) | |
| tree | 699f6fa2bcc091cd4f17f4e3795d17c69f9d6f54 | |
| parent | d4641a8f4179234bf95f334fac6621edcaf40370 (diff) | |
| parent | 79efeda18cc5aa79f2a4e4566f5a955327701296 (diff) | |
| download | platform_build_kati-1decd163d97a3f8b7ab88cbcff501ed1de9615af.tar.gz platform_build_kati-1decd163d97a3f8b7ab88cbcff501ed1de9615af.tar.bz2 platform_build_kati-1decd163d97a3f8b7ab88cbcff501ed1de9615af.zip | |
Merge remote-tracking branch 'aosp/upstream'
* aosp/upstream:
On-demand find emulator initialization
Support '*' in finddirs
Support '..' in find emulation
Add new check for non-phony targets that have no commands
Remove unused `parents` from DepNode
Test: treehugger
Change-Id: Idd4196c17eaa6e265e2f5dd0f7f552468e7a43e0
| -rw-r--r-- | dep.cc | 47 | ||||
| -rw-r--r-- | dep.h | 1 | ||||
| -rw-r--r-- | find.cc | 451 | ||||
| -rw-r--r-- | find.h | 1 | ||||
| -rw-r--r-- | find_test.cc | 26 | ||||
| -rw-r--r-- | flags.cc | 10 | ||||
| -rw-r--r-- | flags.h | 4 | ||||
| -rw-r--r-- | stats.cc | 3 | ||||
| -rw-r--r-- | strutil.cc | 2 | ||||
| -rw-r--r-- | strutil_test.cc | 13 | ||||
| -rw-r--r-- | testcase/real_no_cmds.sh | 53 | ||||
| -rw-r--r-- | testcase/real_no_cmds_or_deps.sh | 47 |
12 files changed, 476 insertions, 182 deletions
@@ -790,6 +790,53 @@ class DepBuilder { n->order_onlys.push_back({input, c}); } + // Block on werror_writable/werror_phony_looks_real, because otherwise we + // can't rely on is_phony being valid for this check. + if (!n->is_phony && n->cmds.empty() && g_flags.werror_writable && + g_flags.werror_phony_looks_real) { + if (n->deps.empty() && n->order_onlys.empty()) { + if (g_flags.werror_real_no_cmds_or_deps) { + ERROR_LOC( + n->loc, + "*** target \"%s\" has no commands or deps that could create it", + output.c_str()); + } else if (g_flags.warn_real_no_cmds_or_deps) { + WARN_LOC(n->loc, + "warning: target \"%s\" has no commands or deps that could " + "create it", + output.c_str()); + } + } else { + if (n->actual_inputs.size() == 1) { + if (g_flags.werror_real_no_cmds) { + ERROR_LOC(n->loc, + "*** target \"%s\" has no commands. Should \"%s\" be " + "using .KATI_IMPLICIT_OUTPUTS?", + output.c_str(), n->actual_inputs[0].c_str()); + } else if (g_flags.warn_real_no_cmds) { + WARN_LOC(n->loc, + "warning: target \"%s\" has no commands. Should \"%s\" be " + "using .KATI_IMPLICIT_OUTPUTS?", + output.c_str(), n->actual_inputs[0].c_str()); + } + } else { + if (g_flags.werror_real_no_cmds) { + ERROR_LOC( + n->loc, + "*** target \"%s\" has no commands that could create output " + "file. Is a dependency missing .KATI_IMPLICIT_OUTPUTS?", + output.c_str()); + } else if (g_flags.warn_real_no_cmds) { + WARN_LOC( + n->loc, + "warning: target \"%s\" has no commands that could create " + "output file. Is a dependency missing .KATI_IMPLICIT_OUTPUTS?", + output.c_str()); + } + } + } + } + n->has_rule = true; n->is_default_target = first_rule_ == output; if (cur_rule_vars_->empty()) { @@ -39,7 +39,6 @@ struct DepNode { vector<Value*> cmds; vector<NamedDepNode> deps; vector<NamedDepNode> order_onlys; - vector<NamedDepNode> parents; bool has_rule; bool is_default_target; bool is_phony; @@ -31,6 +31,7 @@ #include "fileutil.h" #include "log.h" +#include "stats.h" #include "string_piece.h" #include "strutil.h" #include "timeutil.h" @@ -44,6 +45,8 @@ } \ } while (0) +static unsigned int find_emulator_node_cnt = 0; + class FindCond { public: virtual ~FindCond() = default; @@ -139,6 +142,12 @@ class DirentNode { virtual ~DirentNode() = default; virtual const DirentNode* FindDir(StringPiece) const { return NULL; } + virtual bool FindNodes(const FindCommand&, + vector<pair<string, const DirentNode*>>&, + string*, + StringPiece) const { + return true; + } virtual bool RunFind(const FindCommand& fc, const Loc& loc, int d, @@ -221,7 +230,8 @@ struct ScopedReadDirTracker { class DirentDirNode : public DirentNode { public: - explicit DirentDirNode(const string& name) : DirentNode(name) {} + explicit DirentDirNode(const DirentDirNode* parent, const string& name) + : DirentNode(name), parent_(parent), name_(name) {} ~DirentDirNode() { for (auto& p : children_) { @@ -230,13 +240,25 @@ class DirentDirNode : public DirentNode { } virtual const DirentNode* FindDir(StringPiece d) const override { + if (!is_initialized_) { + initialize(); + } + if (d.empty() || d == ".") return this; + if (d == "..") + return parent_; + size_t index = d.find('/'); const string& p = d.substr(0, index).as_string(); if (p.empty() || p == ".") return FindDir(d.substr(index + 1)); - ; + if (p == "..") { + if (parent_ == NULL) + return NULL; + return parent_->FindDir(d.substr(index + 1)); + } + for (auto& child : children_) { if (p == child.first) { if (index == string::npos) @@ -248,12 +270,83 @@ class DirentDirNode : public DirentNode { return NULL; } + virtual bool FindNodes(const FindCommand& fc, + vector<pair<string, const DirentNode*>>& results, + string* path, + StringPiece d) const override { + if (!is_initialized_) { + initialize(); + } + + if (!path->empty()) + path->append("/"); + + size_t orig_path_size = path->size(); + + size_t index = d.find('/'); + const string& p = d.substr(0, index).as_string(); + + if (p.empty() || p == ".") { + path->append(p); + if (index == string::npos) { + results.emplace_back(*path, this); + return true; + } + return FindNodes(fc, results, path, d.substr(index + 1)); + } + if (p == "..") { + if (parent_ == NULL) { + LOG("FindEmulator does not support leaving the source directory: %s", + path->c_str()); + return false; + } + path->append(p); + if (index == string::npos) { + results.emplace_back(*path, parent_); + return true; + } + return parent_->FindNodes(fc, results, path, d.substr(index + 1)); + } + + bool is_wild = p.find_first_of("?*[") != string::npos; + if (is_wild) { + fc.read_dirs->insert(*path); + } + + for (auto& child : children_) { + bool matches = false; + if (is_wild) { + matches = (fnmatch(p.c_str(), child.first.c_str(), FNM_PERIOD) == 0); + } else { + matches = (p == child.first); + } + if (matches) { + path->append(child.first); + if (index == string::npos) { + results.emplace_back(*path, child.second); + } else { + if (!child.second->FindNodes(fc, results, path, + d.substr(index + 1))) { + return false; + } + } + path->resize(orig_path_size); + } + } + + return true; + } + virtual bool RunFind(const FindCommand& fc, const Loc& loc, int d, string* path, unordered_map<const DirentNode*, string>* cur_read_dirs, vector<string>& out) const override { + if (!is_initialized_) { + initialize(); + } + ScopedReadDirTracker srdt(this, *path, cur_read_dirs); if (!srdt.ok()) { FIND_WARN_LOC(loc, @@ -336,25 +429,77 @@ class DirentDirNode : public DirentNode { virtual bool IsDirectory() const override { return true; } - void Add(const string& name, DirentNode* c) { - children_.emplace(children_.end(), name, c); + private: + static unsigned char GetDtTypeFromStat(const struct stat& st) { + if (S_ISREG(st.st_mode)) { + return DT_REG; + } else if (S_ISDIR(st.st_mode)) { + return DT_DIR; + } else if (S_ISCHR(st.st_mode)) { + return DT_CHR; + } else if (S_ISBLK(st.st_mode)) { + return DT_BLK; + } else if (S_ISFIFO(st.st_mode)) { + return DT_FIFO; + } else if (S_ISLNK(st.st_mode)) { + return DT_LNK; + } else if (S_ISSOCK(st.st_mode)) { + return DT_SOCK; + } else { + return DT_UNKNOWN; + } + } + + 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); } - private: - vector<pair<string, DirentNode*>> children_; + void initialize() const; + + const DirentDirNode* parent_; + + mutable vector<pair<string, DirentNode*>> children_; + mutable string name_; + mutable bool is_initialized_ = false; }; class DirentSymlinkNode : public DirentNode { public: - explicit DirentSymlinkNode(const string& name) - : DirentNode(name), to_(NULL), errno_(0) {} + explicit DirentSymlinkNode(const DirentDirNode* parent, const string& name) + : DirentNode(name), name_(name), parent_(parent) {} virtual const DirentNode* FindDir(StringPiece d) const override { + if (!is_initialized_) { + initialize(); + } if (errno_ == 0 && to_) return to_->FindDir(d); return NULL; } + virtual bool FindNodes(const FindCommand& fc, + vector<pair<string, const DirentNode*>>& results, + string* path, + StringPiece d) const override { + if (!is_initialized_) { + initialize(); + } + if (errno_ != 0) { + return true; + } + if (!to_) { + LOG("FindEmulator does not support symlink %s", path->c_str()); + return false; + } + if (to_->IsDirectory()) + fc.read_dirs->insert(*path); + return to_->FindNodes(fc, results, path, d); + } + virtual bool RunFind(const FindCommand& fc, const Loc& loc, int d, @@ -362,6 +507,9 @@ class DirentSymlinkNode : public DirentNode { unordered_map<const DirentNode*, string>* cur_read_dirs, vector<string>& out) const override { unsigned char type = DT_LNK; + if (fc.follows_symlinks && !is_initialized_) { + initialize(); + } if (fc.follows_symlinks && errno_ != ENOENT) { if (errno_) { if (fc.type != FindCommandType::FINDLEAVES) { @@ -383,18 +531,100 @@ class DirentSymlinkNode : public DirentNode { } virtual bool IsDirectory() const override { + if (!is_initialized_) { + initialize(); + } return errno_ == 0 && to_ && to_->IsDirectory(); } - void set_to(const DirentNode* to) { to_ = to; } + private: + void initialize() const { + COLLECT_STATS("init find emulator DirentSymlinkNode::initialize"); + char buf[PATH_MAX + 1]; + buf[PATH_MAX] = 0; + ssize_t len = readlink(name_.c_str(), buf, PATH_MAX); + if (len <= 0) { + errno_ = errno; + WARN("readlink failed: %s", name_.c_str()); + name_ = ""; + is_initialized_ = true; + return; + } + buf[len] = 0; + + struct stat st; + if (stat(name_.c_str(), &st) != 0) { + errno_ = errno; + LOG("stat failed: %s: %s", name_.c_str(), strerror(errno)); + name_ = ""; + is_initialized_ = true; + return; + } + + // absolute symlinks aren't supported by the find emulator + if (*buf != '/') { + to_ = parent_->FindDir(buf); + } - void set_errno(int e) { errno_ = e; } + name_ = ""; + is_initialized_ = true; + } - private: - const DirentNode* to_; - int errno_; + mutable string name_; + const DirentDirNode* parent_; + + mutable const DirentNode* to_ = nullptr; + mutable int errno_ = 0; + mutable bool is_initialized_ = false; }; +void DirentDirNode::initialize() const { + COLLECT_STATS("init find emulator DirentDirNode::initialize"); + DIR* dir = opendir(name_.empty() ? "." : name_.c_str()); + if (!dir) { + if (errno == ENOENT || errno == EACCES) { + LOG("opendir failed: %s", name_.c_str()); + name_ = ""; + is_initialized_ = true; + return; + } else { + PERROR("opendir failed: %s", name_.c_str()); + } + } + + struct dirent* ent; + while ((ent = readdir(dir)) != NULL) { + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") || + !strcmp(ent->d_name, ".repo") || !strcmp(ent->d_name, ".git")) + continue; + + string npath = name_; + if (!name_.empty()) + npath += '/'; + npath += ent->d_name; + + DirentNode* c = NULL; + auto d_type = ent->d_type; + if (d_type == DT_UNKNOWN) { + d_type = GetDtType(npath); + CHECK(d_type != DT_UNKNOWN); + } + if (d_type == DT_DIR) { + c = new DirentDirNode(this, npath); + } else if (d_type == DT_LNK) { + c = new DirentSymlinkNode(this, npath); + } else { + c = new DirentFileNode(npath, d_type); + } + find_emulator_node_cnt++; + children_.emplace(children_.end(), ent->d_name, c); + } + closedir(dir); + + name_ = ""; + is_initialized_ = true; +} + class FindCommandParser { public: FindCommandParser(StringPiece cmd, FindCommand* fc) @@ -631,7 +861,7 @@ class FindCommandParser { return false; } fc_->redirect_to_devnull = true; - } else if (tok.find_first_of("|;&><*'\"") != string::npos) { + } else if (tok.find_first_of("|;&><'\"") != string::npos) { return false; } else { fc_->finddirs.push_back(tok.as_string()); @@ -715,6 +945,8 @@ class FindCommandParser { if (tok == "cd") { if (!GetNextToken(&tok) || tok.empty() || !fc_->chdir.empty()) return false; + if (tok.find_first_of("?*[") != string::npos) + return false; fc_->chdir = tok.as_string(); if (!GetNextToken(&tok) || (tok != ";" && tok != "&&")) return false; @@ -769,15 +1001,13 @@ static FindEmulator* g_instance; class FindEmulatorImpl : public FindEmulator { public: - FindEmulatorImpl() : node_cnt_(0), is_initialized_(false) { - g_instance = this; - } + FindEmulatorImpl() { g_instance = this; } virtual ~FindEmulatorImpl() = default; bool CanHandle(StringPiece s) const { - return (!HasPrefix(s, "../") && !HasPrefix(s, "/") && - !HasPrefix(s, ".repo") && !HasPrefix(s, ".git")); + return (!HasPrefix(s, "/") && !HasPrefix(s, ".repo") && + !HasPrefix(s, ".git")); } const DirentNode* FindDir(StringPiece d, bool* should_fallback) { @@ -798,17 +1028,6 @@ class FindEmulatorImpl : public FindEmulator { return false; } - if (!is_initialized_) { - ScopedTimeReporter tr("init find emulator time"); - root_.reset(ConstructDirectoryTree("")); - if (!root_) { - ERROR("FindEmulator: Cannot open root directory"); - } - ResolveSymlinks(); - LOG_STAT("%d find nodes", node_cnt_); - is_initialized_ = true; - } - if (!fc.testdir.empty()) { if (!CanHandle(fc.testdir)) { LOG("FindEmulator: Cannot handle test dir (%.*s): %s", SPF(fc.testdir), @@ -823,7 +1042,7 @@ class FindEmulatorImpl : public FindEmulator { } } - const DirentNode* root = root_.get(); + const DirentNode* root = root_; if (!fc.chdir.empty()) { if (!CanHandle(fc.chdir)) { @@ -846,16 +1065,20 @@ class FindEmulatorImpl : public FindEmulator { vector<string> results; for (const string& finddir : fc.finddirs) { - if (!CanHandle(finddir)) { - LOG("FindEmulator: Cannot handle find dir (%s): %s", finddir.c_str(), + string fullpath = ConcatDir(fc.chdir, finddir); + if (!CanHandle(fullpath)) { + LOG("FindEmulator: Cannot handle find dir (%s): %s", fullpath.c_str(), cmd.c_str()); return false; } - const DirentNode* base; - base = root->FindDir(finddir); - if (!base) { - if (Exists(finddir)) { + string findnodestr; + vector<pair<string, const DirentNode*>> bases; + if (!root->FindNodes(fc, bases, &findnodestr, finddir)) { + return false; + } + if (bases.empty()) { + if (Exists(fullpath)) { return false; } if (!fc.redirect_to_devnull) { @@ -866,11 +1089,15 @@ class FindEmulatorImpl : public FindEmulator { continue; } - string path = finddir; - unordered_map<const DirentNode*, string> cur_read_dirs; - if (!base->RunFind(fc, loc, 0, &path, &cur_read_dirs, results)) { - LOG("FindEmulator: RunFind failed: %s", cmd.c_str()); - return false; + // bash guarantees that globs are sorted + sort(bases.begin(), bases.end()); + + for (auto [path, base] : bases) { + unordered_map<const DirentNode*, string> cur_read_dirs; + if (!base->RunFind(fc, loc, 0, &path, &cur_read_dirs, results)) { + LOG("FindEmulator: RunFind failed: %s", cmd.c_str()); + return false; + } } } @@ -897,141 +1124,7 @@ class FindEmulatorImpl : public FindEmulator { } private: - static unsigned char GetDtTypeFromStat(const struct stat& st) { - if (S_ISREG(st.st_mode)) { - return DT_REG; - } else if (S_ISDIR(st.st_mode)) { - return DT_DIR; - } else if (S_ISCHR(st.st_mode)) { - return DT_CHR; - } else if (S_ISBLK(st.st_mode)) { - return DT_BLK; - } else if (S_ISFIFO(st.st_mode)) { - return DT_FIFO; - } else if (S_ISLNK(st.st_mode)) { - return DT_LNK; - } else if (S_ISSOCK(st.st_mode)) { - return DT_SOCK; - } else { - return DT_UNKNOWN; - } - } - - 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) { - if (errno == ENOENT || errno == EACCES) { - LOG("opendir failed: %s", path.c_str()); - return NULL; - } else { - PERROR("opendir failed: %s", path.c_str()); - } - } - - DirentDirNode* n = new DirentDirNode(path); - - struct dirent* ent; - while ((ent = readdir(dir)) != NULL) { - if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") || - !strcmp(ent->d_name, ".repo") || !strcmp(ent->d_name, ".git")) - continue; - - string npath = path; - if (!path.empty()) - npath += '/'; - npath += ent->d_name; - - DirentNode* c = NULL; - auto d_type = ent->d_type; - if (d_type == DT_UNKNOWN) { - d_type = GetDtType(npath); - CHECK(d_type != DT_UNKNOWN); - } - if (d_type == DT_DIR) { - c = ConstructDirectoryTree(npath); - if (c == NULL) { - continue; - } - } else if (d_type == DT_LNK) { - auto s = new DirentSymlinkNode(npath); - symlinks_.push_back(make_pair(npath, s)); - c = s; - } else { - c = new DirentFileNode(npath, d_type); - } - node_cnt_++; - n->Add(ent->d_name, c); - } - closedir(dir); - - 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) { - DirentNode* dir = ConstructDirectoryTree(path); - if (dir != NULL) { - s->set_to(dir); - } else { - s->set_errno(errno); - } - } - } 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_; + DirentNode* root_ = new DirentDirNode(nullptr, ""); }; } // namespace @@ -1066,6 +1159,10 @@ FindEmulator* FindEmulator::Get() { return g_instance; } +unsigned int FindEmulator::GetNodeCount() { + return find_emulator_node_cnt; +} + void InitFindEmulator() { new FindEmulatorImpl(); } @@ -68,6 +68,7 @@ class FindEmulator { string* out) = 0; static FindEmulator* Get(); + static unsigned int GetNodeCount(); protected: FindEmulator() = default; diff --git a/find_test.cc b/find_test.cc index aefe38b..c9c86b2 100644 --- a/find_test.cc +++ b/find_test.cc @@ -77,7 +77,7 @@ void CompareFind(const string& cmd) { } string emulated; if (!FindEmulator::Get()->HandleFind(cmd, fc, Loc(), &emulated)) { - fprintf(stderr, "Find emulator cannot parse `%s`\n", cmd.c_str()); + fprintf(stderr, "Find emulator cannot handle `%s`\n", cmd.c_str()); exit(1); } @@ -109,8 +109,6 @@ void CompareFind(const string& cmd) { } void ExpectParseFailure(const string& cmd) { - Run(cmd); - FindCommand fc; if (fc.Parse(cmd)) { fprintf(stderr, "Expected parse failure for `%s`\n", cmd.c_str()); @@ -131,6 +129,7 @@ int FindUnitTests() { // drwxr-x--- top // lrwxrwxrwx top/E -> missing // lrwxrwxrwx top/C -> A + // lrwxrwxrwx top/F -> A/B // -rw-r----- top/a // drwxr-x--- top/A // lrwxrwxrwx top/A/D -> B @@ -141,6 +140,7 @@ int FindUnitTests() { Run("cd top && ln -s A C"); Run("cd top/A && ln -s B D"); Run("cd top && ln -s missing E"); + Run("cd top && ln -s A/B F"); Run("touch top/a top/A/b top/A/B/z"); InitFindEmulator(); @@ -153,6 +153,9 @@ int FindUnitTests() { CompareFind("find -L top/C"); CompareFind("find -L top/C/."); + // A file in finddir + CompareFind("find top/A/b"); + CompareFind("cd top && find C"); CompareFind("cd top && find -L C"); CompareFind("cd top/C && find ."); @@ -165,7 +168,24 @@ int FindUnitTests() { CompareFind("find top \\! -name 'a*'"); CompareFind("find top \\( -name 'a*' \\)"); + // Basic use of .. + CompareFind("cd top/C; find ../A"); + + // Use of .. in chdir + CompareFind("cd top/A/..; find ."); + + // .. through a symlink in chdir, should list under top/A/... + CompareFind("cd top/F; find ../"); + // .. through a symlink in finddir, should do the same + CompareFind("cd top; find F/.."); + + // * in a finddir + CompareFind("find top/*/B"); + ExpectParseFailure("find top -name a\\*"); + // * in a chdir is not supported + ExpectParseFailure("cd top/*/B && find ."); + return unit_test_failed ? 1 : 0; } @@ -134,6 +134,16 @@ void Flags::Parse(int argc, char** argv) { werror_phony_looks_real = true; } else if (!strcmp(arg, "--werror_writable")) { werror_writable = true; + } else if (!strcmp(arg, "--warn_real_no_cmds_or_deps")) { + warn_real_no_cmds_or_deps = true; + } else if (!strcmp(arg, "--werror_real_no_cmds_or_deps")) { + warn_real_no_cmds_or_deps = true; + werror_real_no_cmds_or_deps = true; + } else if (!strcmp(arg, "--warn_real_no_cmds")) { + warn_real_no_cmds = true; + } else if (!strcmp(arg, "--werror_real_no_cmds")) { + warn_real_no_cmds = true; + werror_real_no_cmds = true; } else if (ParseCommandLineOptionWithArg("-j", argv, &i, &num_jobs_str)) { num_jobs = strtol(num_jobs_str, NULL, 10); if (num_jobs <= 0) { @@ -56,6 +56,10 @@ struct Flags { bool warn_phony_looks_real; bool werror_phony_looks_real; bool werror_writable; + bool warn_real_no_cmds_or_deps; + bool werror_real_no_cmds_or_deps; + bool warn_real_no_cmds; + bool werror_real_no_cmds; const char* default_pool; const char* goma_dir; const char* ignore_dirty_pattern; @@ -20,6 +20,7 @@ #include <mutex> #include <vector> +#include "find.h" #include "flags.h" #include "log.h" #include "stringprintf.h" @@ -139,4 +140,6 @@ void ReportAllStats() { st->DumpTop(); } delete g_stats; + + LOG_STAT("%u find nodes", FindEmulator::GetNodeCount()); } @@ -494,7 +494,7 @@ string SortWordsInString(StringPiece s) { string ConcatDir(StringPiece b, StringPiece n) { string r; - if (!b.empty()) { + if (!b.empty() && (n.empty() || n[0] != '/')) { b.AppendToString(&r); r += '/'; } diff --git a/strutil_test.cc b/strutil_test.cc index e83b4c3..d0db1f3 100644 --- a/strutil_test.cc +++ b/strutil_test.cc @@ -198,6 +198,18 @@ void TestFindEndOfLineInvalidAccess() { ASSERT_EQ(FindEndOfLine(CreateProtectedString("a\\"), 0, &lf_cnt), 2); } +void TestConcatDir() { + ASSERT_EQ(ConcatDir("", ""), ""); + ASSERT_EQ(ConcatDir(".", ""), ""); + ASSERT_EQ(ConcatDir("", "."), ""); + ASSERT_EQ(ConcatDir("a", "b"), "a/b"); + ASSERT_EQ(ConcatDir("a/", "b"), "a/b"); + ASSERT_EQ(ConcatDir("a", "/b"), "/b"); + ASSERT_EQ(ConcatDir("a", ".."), ""); + ASSERT_EQ(ConcatDir("a", "../b"), "b"); + ASSERT_EQ(ConcatDir("a", "../../b"), "../b"); +} + } // namespace int main() { @@ -214,5 +226,6 @@ int main() { TestFindEndOfLine(); TestWordScannerInvalidAccess(); TestFindEndOfLineInvalidAccess(); + TestConcatDir(); assert(!g_failed); } diff --git a/testcase/real_no_cmds.sh b/testcase/real_no_cmds.sh new file mode 100644 index 0000000..88066d7 --- /dev/null +++ b/testcase/real_no_cmds.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# +# Copyright 2020 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 -u + +mk="$@" + +cat <<EOF > Makefile +test: out/bad.baz out/good.baz + @echo "END" +.PHONY: test + +out/bad.bar: + @echo bar >out/bad.bar + @echo baz >out/bad.baz + +out/bad.baz: out/bad.bar + +out/good.bar: .KATI_IMPLICIT_OUTPUTS := out/good.baz +out/good.bar: + @echo bar >out/good.bar + @echo baz >out/good.baz +EOF + +mkdir -p out + +if echo "${mk}" | grep -qv "kati"; then + # Make doesn't support these warnings, so write the expected output. + echo 'Makefile:9: warning: target "out/bad.baz" has no commands. Should "out/bad.bar" be using .KATI_IMPLICIT_OUTPUTS?' + echo 'END' +else + ${mk} --werror_phony_looks_real --writable=out/ --werror_writable --warn_real_no_cmds 2>&1 +fi + +if echo "${mk}" | grep -qv "kati"; then + # Make doesn't support these warnings, so write the expected output. + echo 'Makefile:9: *** target "out/bad.baz" has no commands. Should "out/bad.bar" be using .KATI_IMPLICIT_OUTPUTS?' +else + ${mk} --werror_phony_looks_real --writable=out/ --werror_writable --werror_real_no_cmds 2>&1 +fi diff --git a/testcase/real_no_cmds_or_deps.sh b/testcase/real_no_cmds_or_deps.sh new file mode 100644 index 0000000..b591803 --- /dev/null +++ b/testcase/real_no_cmds_or_deps.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# +# Copyright 2020 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 -u + +mk="$@" + +cat <<EOF > Makefile +test: out/bad out/good + @echo "END" +.PHONY: test + +out/bad: + +out/good: + @echo bar >out/good +EOF + +mkdir -p out + +if echo "${mk}" | grep -qv "kati"; then + # Make doesn't support these warnings, so write the expected output. + echo 'Makefile:5: warning: target "out/bad" has no commands or deps that could create it' + echo 'END' +else + ${mk} --werror_phony_looks_real --writable=out/ --werror_writable --warn_real_no_cmds_or_deps 2>&1 +fi + +if echo "${mk}" | grep -qv "kati"; then + # Make doesn't support these warnings, so write the expected output. + echo 'Makefile:5: *** target "out/bad" has no commands or deps that could create it' +else + ${mk} --werror_phony_looks_real --writable=out/ --werror_writable --werror_real_no_cmds_or_deps 2>&1 +fi |
