diff options
author | Shinichiro Hamaji <shinichiro.hamaji@gmail.com> | 2015-06-29 15:45:21 +0900 |
---|---|---|
committer | Shinichiro Hamaji <shinichiro.hamaji@gmail.com> | 2015-06-29 15:45:21 +0900 |
commit | df1fc8b2ead00bad8c23065affb9576a4b03cff5 (patch) | |
tree | 5e0db116b19a12256b0866496e54abdca7041ce8 | |
parent | 5f86e1a2df170eadf0601f1bff6ab12a643a68c5 (diff) | |
download | android_build_kati-df1fc8b2ead00bad8c23065affb9576a4b03cff5.tar.gz android_build_kati-df1fc8b2ead00bad8c23065affb9576a4b03cff5.tar.bz2 android_build_kati-df1fc8b2ead00bad8c23065affb9576a4b03cff5.zip |
[C++] Implement the first version of NinjaGenerator
-rw-r--r-- | eval.h | 4 | ||||
-rw-r--r-- | func.cc | 37 | ||||
-rw-r--r-- | ninja.cc | 231 |
3 files changed, 268 insertions, 4 deletions
@@ -70,6 +70,9 @@ class Evaluator { void set_current_scope(Vars* v) { current_scope_ = v; } + bool avoid_io() const { return avoid_io_; } + void set_avoid_io(bool a) { avoid_io_ = a; } + private: Var* EvalRHS(StringPiece lhs, Value* rhs, StringPiece orig_rhs, AssignOp op, bool is_override = false); @@ -86,6 +89,7 @@ class Evaluator { Loc loc_; bool is_bootstrap_; + bool avoid_io_; }; #endif // EVAL_H_ @@ -242,6 +242,13 @@ void JoinFunc(const vector<Value*>& args, Evaluator* ev, string* s) { void WildcardFunc(const vector<Value*>& args, Evaluator* ev, string* s) { shared_ptr<string> pat = args[0]->Eval(ev); + if (ev->avoid_io()) { + *s += "$(/bin/ls -d "; + *s += *pat; + *s += " 2> /dev/null)"; + return; + } + WordWriter ww(s); for (StringPiece tok : WordScanner(*pat)) { ScopedTerminator st(tok); @@ -317,6 +324,11 @@ void AddprefixFunc(const vector<Value*>& args, Evaluator* ev, string* s) { void RealpathFunc(const vector<Value*>& args, Evaluator* ev, string* s) { shared_ptr<string> text = args[0]->Eval(ev); + if (ev->avoid_io()) { + *s += "KATI_TODO(realpath)"; + return; + } + WordWriter ww(s); for (StringPiece tok : WordScanner(*text)) { ScopedTerminator st(tok); @@ -390,6 +402,13 @@ void EvalFunc(const vector<Value*>& args, Evaluator* ev, string*) { void ShellFunc(const vector<Value*>& args, Evaluator* ev, string* s) { shared_ptr<string> cmd = args[0]->Eval(ev); + if (ev->avoid_io()) { + *s += "$("; + *s += *cmd; + *s += ")"; + return; + } + LOG("ShellFunc: %s", cmd->c_str()); string out; // TODO: Handle $(SHELL). @@ -459,20 +478,32 @@ void FlavorFunc(const vector<Value*>& args, Evaluator* ev, string* s) { *s += var->Flavor(); } -void InfoFunc(const vector<Value*>& args, Evaluator* ev, string*) { +void InfoFunc(const vector<Value*>& args, Evaluator* ev, string* s) { shared_ptr<string> a = args[0]->Eval(ev); + if (ev->avoid_io()) { + *s += "KATI_TODO(info)"; + return; + } printf("%s\n", a->c_str()); fflush(stdout); } -void WarningFunc(const vector<Value*>& args, Evaluator* ev, string*) { +void WarningFunc(const vector<Value*>& args, Evaluator* ev, string* s) { shared_ptr<string> a = args[0]->Eval(ev); + if (ev->avoid_io()) { + *s += "KATI_TODO(warning)"; + return; + } printf("%s:%d: %s\n", LOCF(ev->loc()), a->c_str()); fflush(stdout); } -void ErrorFunc(const vector<Value*>& args, Evaluator* ev, string*) { +void ErrorFunc(const vector<Value*>& args, Evaluator* ev, string* s) { shared_ptr<string> a = args[0]->Eval(ev); + if (ev->avoid_io()) { + *s += "KATI_TODO(error)"; + return; + } ev->Error(StringPrintf("*** %s.", a->c_str())); } @@ -16,6 +16,235 @@ #include "ninja.h" -void GenerateNinja(const vector<DepNode*>& nodes, Evaluator* ev) { +#include <stdio.h> + +#include <memory> +#include <string> +#include <unordered_set> + +#include "command.h" +#include "dep.h" +#include "eval.h" +#include "log.h" +#include "string_piece.h" +#include "stringprintf.h" +#include "strutil.h" +#include "var.h" + +class NinjaGenerator { + public: + explicit NinjaGenerator(Evaluator* ev) + : ce_(ev), ev_(ev), fp_(NULL), rule_id_(0) { + ev_->set_avoid_io(true); + } + + ~NinjaGenerator() { + ev_->set_avoid_io(false); + } + + void Generate(const vector<DepNode*>& nodes) { + GenerateShell(); + GenerateNinja(nodes); + } + + private: + string GenRuleName() { + return StringPrintf("rule%d", rule_id_++); + } + + StringPiece TranslateCommand(const char* in) { + const size_t orig_size = cmd_buf_.size(); + bool prev_backslash = false; + char quote = 0; + bool done = false; + for (; *in && !done; in++) { + switch (*in) { + case '#': + if (quote == 0 && !prev_backslash) { + done = true; + break; + } + + case '\'': + case '"': + case '`': + if (quote) { + if (quote == *in) + quote = 0; + } else if (!prev_backslash) { + quote = *in; + } + + case '$': + cmd_buf_ += "$$"; + break; + + case '\t': + cmd_buf_ += ' '; + break; + + case '\n': + if (prev_backslash) { + cmd_buf_[cmd_buf_.size()-1] = ' '; + } else { + cmd_buf_ += ' '; + } + + case '\\': + prev_backslash = !prev_backslash; + cmd_buf_ += '\\'; + break; + + default: + cmd_buf_ += *in; + prev_backslash = false; + } + } + + while (true) { + char c = cmd_buf_[cmd_buf_.size()-1]; + if (!isspace(c) && c != ';') + break; + cmd_buf_.resize(cmd_buf_.size() - 1); + } + + return StringPiece(cmd_buf_.data() + orig_size, + cmd_buf_.size() - orig_size); + } + + void GenShellScript(const vector<Command*>& commands) { + //bool use_gomacc = false; + bool should_ignore_error = false; + cmd_buf_.clear(); + for (const Command* c : commands) { + if (!cmd_buf_.empty()) { + if (should_ignore_error) { + cmd_buf_ += " ; "; + } else { + cmd_buf_ += " && "; + } + } + should_ignore_error = c->ignore_error; + + const char* in = c->cmd->c_str(); + while (isspace(*in)) + in++; + + bool needs_subshell = commands.size() > 1; + if (*in == '(') { + needs_subshell = false; + } + + if (needs_subshell) + cmd_buf_ += '('; + StringPiece translated = TranslateCommand(in); + if (translated.empty()) { + cmd_buf_ += "true"; + } else { + // TODO: flip use_gomacc + } + + if (c == commands.back() && c->ignore_error) { + cmd_buf_ += " ; true"; + } + + if (needs_subshell) + cmd_buf_ += ')'; + } + } + + void EmitNode(DepNode* node) { + auto p = done_.insert(node->output); + if (!p.second) + return; + + if (node->cmds.empty() && node->deps.empty() && !node->is_phony) + return; + + vector<Command*> commands; + ce_.Eval(node, &commands); + + string rule_name = "phony"; + if (!commands.empty()) { + rule_name = GenRuleName(); + fprintf(fp_, "rule %s\n", rule_name.c_str()); + fprintf(fp_, " description = build $out\n"); + + GenShellScript(commands); + // TODO: depfile + + // It seems Linux is OK with ~130kB. + // TODO: Find this number automatically. + if (cmd_buf_.size() > 100 * 1000) { + fprintf(fp_, " rspfile = $out.rsp\n"); + fprintf(fp_, " rspfile_content = %s\n", cmd_buf_.c_str()); + fprintf(fp_, " command = sh $out.rsp\n"); + } else { + fprintf(fp_, " command = %s\n", cmd_buf_.c_str()); + } + } + + EmitBuild(node, rule_name); + // goma + + for (DepNode* d : node->deps) { + EmitNode(d); + } + } + + void EmitBuild(DepNode* node, const string& rule_name) { + fprintf(fp_, "build %.*s: %s", SPF(node->output), rule_name.c_str()); + vector<StringPiece> order_onlys; + for (DepNode* d : node->deps) { + if (d->is_order_only) { + order_onlys.push_back(d->output); + } else { + fprintf(fp_, " %.*s", SPF(d->output)); + } + } + if (!order_onlys.empty()) { + fprintf(fp_, " |"); + for (StringPiece oo : order_onlys) { + fprintf(fp_, " %.*s", SPF(oo)); + } + } + fprintf(fp_, "\n"); + } + + void GenerateNinja(const vector<DepNode*>& nodes) { + fp_ = fopen("build.ninja", "wb"); + if (fp_ == NULL) + PERROR("fopen(build.ninja) failed"); + + fprintf(fp_, "# Generated by kati\n"); + fprintf(fp_, "\n"); + + for (DepNode* node : nodes) { + EmitNode(node); + } + + fclose(fp_); + } + + void GenerateShell() { +#if 0 + Var* v = ev->LookupVar("SHELL"); + shell_ = v->Eval(ev); + if (shell_->empty()) + shell_ = make_shared<string>("/bin/sh"); +#endif + } + + CommandEvaluator ce_; + Evaluator* ev_; + FILE* fp_; + unordered_set<StringPiece> done_; + int rule_id_; + string cmd_buf_; +}; + +void GenerateNinja(const vector<DepNode*>& nodes, Evaluator* ev) { + NinjaGenerator ng(ev); + ng.Generate(nodes); } |