aboutsummaryrefslogtreecommitdiffstats
path: root/src/command.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/command.cc')
-rw-r--r--src/command.cc240
1 files changed, 240 insertions, 0 deletions
diff --git a/src/command.cc b/src/command.cc
new file mode 100644
index 0000000..04bb168
--- /dev/null
+++ b/src/command.cc
@@ -0,0 +1,240 @@
+// 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.
+
+// +build ignore
+
+#include "command.h"
+
+#include <unordered_map>
+#include <unordered_set>
+
+#include "dep.h"
+#include "eval.h"
+#include "flags.h"
+#include "log.h"
+#include "strutil.h"
+#include "var.h"
+
+namespace {
+
+class AutoVar : public Var {
+ public:
+ AutoVar() : Var(VarOrigin::AUTOMATIC) {}
+ virtual const char* Flavor() const override { return "undefined"; }
+
+ virtual void AppendVar(Evaluator*, Value*) override { CHECK(false); }
+
+ virtual StringPiece String() const override {
+ ERROR("$(value %s) is not implemented yet", sym_);
+ return "";
+ }
+
+ virtual string DebugString() const override {
+ return string("AutoVar(") + sym_ + ")";
+ }
+
+ protected:
+ AutoVar(CommandEvaluator* ce, const char* sym) : ce_(ce), sym_(sym) {}
+ virtual ~AutoVar() = default;
+
+ CommandEvaluator* ce_;
+ const char* sym_;
+};
+
+#define DECLARE_AUTO_VAR_CLASS(name) \
+ class name : public AutoVar { \
+ public: \
+ name(CommandEvaluator* ce, const char* sym) : AutoVar(ce, sym) {} \
+ virtual ~name() = default; \
+ virtual void Eval(Evaluator* ev, string* s) const override; \
+ }
+
+DECLARE_AUTO_VAR_CLASS(AutoAtVar);
+DECLARE_AUTO_VAR_CLASS(AutoLessVar);
+DECLARE_AUTO_VAR_CLASS(AutoHatVar);
+DECLARE_AUTO_VAR_CLASS(AutoPlusVar);
+DECLARE_AUTO_VAR_CLASS(AutoStarVar);
+DECLARE_AUTO_VAR_CLASS(AutoNotImplementedVar);
+
+class AutoSuffixDVar : public AutoVar {
+ public:
+ AutoSuffixDVar(CommandEvaluator* ce, const char* sym, Var* wrapped)
+ : AutoVar(ce, sym), wrapped_(wrapped) {}
+ virtual ~AutoSuffixDVar() = default;
+ virtual void Eval(Evaluator* ev, string* s) const override;
+
+ private:
+ Var* wrapped_;
+};
+
+class AutoSuffixFVar : public AutoVar {
+ public:
+ AutoSuffixFVar(CommandEvaluator* ce, const char* sym, Var* wrapped)
+ : AutoVar(ce, sym), wrapped_(wrapped) {}
+ virtual ~AutoSuffixFVar() = default;
+ virtual void Eval(Evaluator* ev, string* s) const override;
+
+ private:
+ Var* wrapped_;
+};
+
+void AutoAtVar::Eval(Evaluator*, string* s) const {
+ *s += ce_->current_dep_node()->output.str();
+}
+
+void AutoLessVar::Eval(Evaluator*, string* s) const {
+ auto& ai = ce_->current_dep_node()->actual_inputs;
+ if (!ai.empty())
+ *s += ai[0].str();
+}
+
+void AutoHatVar::Eval(Evaluator*, string* s) const {
+ unordered_set<StringPiece> seen;
+ WordWriter ww(s);
+ for (Symbol ai : ce_->current_dep_node()->actual_inputs) {
+ if (seen.insert(ai.str()).second)
+ ww.Write(ai.str());
+ }
+}
+
+void AutoPlusVar::Eval(Evaluator*, string* s) const {
+ WordWriter ww(s);
+ for (Symbol ai : ce_->current_dep_node()->actual_inputs) {
+ ww.Write(ai.str());
+ }
+}
+
+void AutoStarVar::Eval(Evaluator*, string* s) const {
+ const DepNode* n = ce_->current_dep_node();
+ if (!n->output_pattern.IsValid())
+ return;
+ Pattern pat(n->output_pattern.str());
+ pat.Stem(n->output.str()).AppendToString(s);
+}
+
+void AutoNotImplementedVar::Eval(Evaluator* ev, string*) const {
+ ev->Error(StringPrintf("Automatic variable `$%s' isn't supported yet", sym_));
+}
+
+void AutoSuffixDVar::Eval(Evaluator* ev, string* s) const {
+ string buf;
+ wrapped_->Eval(ev, &buf);
+ WordWriter ww(s);
+ for (StringPiece tok : WordScanner(buf)) {
+ ww.Write(Dirname(tok));
+ }
+}
+
+void AutoSuffixFVar::Eval(Evaluator* ev, string* s) const {
+ string buf;
+ wrapped_->Eval(ev, &buf);
+ WordWriter ww(s);
+ for (StringPiece tok : WordScanner(buf)) {
+ ww.Write(Basename(tok));
+ }
+}
+
+void ParseCommandPrefixes(StringPiece* s, bool* echo, bool* ignore_error) {
+ *s = TrimLeftSpace(*s);
+ while (true) {
+ char c = s->get(0);
+ if (c == '@') {
+ *echo = false;
+ } else if (c == '-') {
+ *ignore_error = true;
+ } else if (c == '+') {
+ // ignore recursion marker
+ } else {
+ break;
+ }
+ *s = TrimLeftSpace(s->substr(1));
+ }
+}
+
+} // namespace
+
+CommandEvaluator::CommandEvaluator(Evaluator* ev) : ev_(ev) {
+#define INSERT_AUTO_VAR(name, sym) \
+ do { \
+ Var* v = new name(this, sym); \
+ Intern(sym).SetGlobalVar(v); \
+ Intern(sym "D").SetGlobalVar(new AutoSuffixDVar(this, sym "D", v)); \
+ Intern(sym "F").SetGlobalVar(new AutoSuffixFVar(this, sym "F", v)); \
+ } while (0)
+ INSERT_AUTO_VAR(AutoAtVar, "@");
+ INSERT_AUTO_VAR(AutoLessVar, "<");
+ INSERT_AUTO_VAR(AutoHatVar, "^");
+ INSERT_AUTO_VAR(AutoPlusVar, "+");
+ INSERT_AUTO_VAR(AutoStarVar, "*");
+ // TODO: Implement them.
+ INSERT_AUTO_VAR(AutoNotImplementedVar, "%");
+ INSERT_AUTO_VAR(AutoNotImplementedVar, "?");
+ INSERT_AUTO_VAR(AutoNotImplementedVar, "|");
+}
+
+void CommandEvaluator::Eval(DepNode* n, vector<Command*>* commands) {
+ ev_->set_loc(n->loc);
+ ev_->set_current_scope(n->rule_vars);
+ current_dep_node_ = n;
+ for (Value* v : n->cmds) {
+ const string&& cmds_buf = v->Eval(ev_);
+ StringPiece cmds = cmds_buf;
+ bool global_echo = !g_flags.is_silent_mode;
+ bool global_ignore_error = false;
+ ParseCommandPrefixes(&cmds, &global_echo, &global_ignore_error);
+ if (cmds == "")
+ continue;
+ while (true) {
+ size_t lf_cnt;
+ size_t index = FindEndOfLine(cmds, 0, &lf_cnt);
+ if (index == cmds.size())
+ index = string::npos;
+ StringPiece cmd = TrimLeftSpace(cmds.substr(0, index));
+ cmds = cmds.substr(index + 1);
+
+ bool echo = global_echo;
+ bool ignore_error = global_ignore_error;
+ ParseCommandPrefixes(&cmd, &echo, &ignore_error);
+
+ if (!cmd.empty()) {
+ Command* command = new Command(n->output);
+ command->cmd = cmd.as_string();
+ command->echo = echo;
+ command->ignore_error = ignore_error;
+ commands->push_back(command);
+ }
+ if (index == string::npos)
+ break;
+ }
+ continue;
+ }
+
+ if (!ev_->delayed_output_commands().empty()) {
+ vector<Command*> output_commands;
+ for (const string& cmd : ev_->delayed_output_commands()) {
+ Command* c = new Command(n->output);
+ c->cmd = cmd;
+ c->echo = false;
+ c->ignore_error = false;
+ output_commands.push_back(c);
+ }
+ // Prepend |output_commands|.
+ commands->swap(output_commands);
+ copy(output_commands.begin(), output_commands.end(),
+ back_inserter(*commands));
+ ev_->clear_delayed_output_commands();
+ }
+
+ ev_->set_current_scope(NULL);
+}