Home | History | Annotate | Download | only in kati
      1 // Copyright 2015 Google Inc. All rights reserved
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //      http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 // +build ignore
     16 
     17 #include <limits.h>
     18 #include <signal.h>
     19 #include <stdio.h>
     20 #include <stdlib.h>
     21 #include <string.h>
     22 #include <time.h>
     23 #include <unistd.h>
     24 
     25 #include "affinity.h"
     26 #include "dep.h"
     27 #include "eval.h"
     28 #include "exec.h"
     29 #include "file.h"
     30 #include "file_cache.h"
     31 #include "fileutil.h"
     32 #include "find.h"
     33 #include "flags.h"
     34 #include "func.h"
     35 #include "log.h"
     36 #include "ninja.h"
     37 #include "parser.h"
     38 #include "regen.h"
     39 #include "stats.h"
     40 #include "stmt.h"
     41 #include "string_piece.h"
     42 #include "stringprintf.h"
     43 #include "strutil.h"
     44 #include "symtab.h"
     45 #include "timeutil.h"
     46 #include "var.h"
     47 
     48 // We know that there are leaks in Kati. Turn off LeakSanitizer by default.
     49 extern "C" const char* __asan_default_options() {
     50   return "detect_leaks=0:allow_user_segv_handler=1";
     51 }
     52 
     53 static void Init() {
     54   InitSymtab();
     55   InitFuncTable();
     56   InitDepNodePool();
     57   InitParser();
     58 }
     59 
     60 static void Quit() {
     61   ReportAllStats();
     62 
     63   QuitParser();
     64   QuitDepNodePool();
     65   QuitFuncTable();
     66   QuitSymtab();
     67 }
     68 
     69 static void ReadBootstrapMakefile(const vector<Symbol>& targets,
     70                                   vector<Stmt*>* stmts) {
     71   string bootstrap =
     72       ("CC?=cc\n"
     73 #if defined(__APPLE__)
     74        "CXX?=c++\n"
     75 #else
     76        "CXX?=g++\n"
     77 #endif
     78        "AR?=ar\n"
     79        // Pretend to be GNU make 3.81, for compatibility.
     80        "MAKE_VERSION?=3.81\n"
     81        "KATI?=ckati\n"
     82        // Overwrite $SHELL environment variable.
     83        "SHELL=/bin/sh\n"
     84        // TODO: Add more builtin vars.
     85 
     86        // http://www.gnu.org/software/make/manual/make.html#Catalogue-of-Rules
     87        // The document above is actually not correct. See default.c:
     88        // http://git.savannah.gnu.org/cgit/make.git/tree/default.c?id=4.1
     89        ".c.o:\n"
     90        "\t$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<\n"
     91        ".cc.o:\n"
     92        "\t$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<\n"
     93        // TODO: Add more builtin rules.
     94       );
     95   if (g_flags.generate_ninja) {
     96     bootstrap += StringPrintf("MAKE?=make -j%d\n",
     97                               g_flags.num_jobs <= 1 ? 1 : g_flags.num_jobs / 2);
     98   } else {
     99     bootstrap += StringPrintf("MAKE?=%s\n",
    100                               JoinStrings(g_flags.subkati_args, " ").c_str());
    101   }
    102   bootstrap +=
    103       StringPrintf("MAKECMDGOALS?=%s\n", JoinSymbols(targets, " ").c_str());
    104 
    105   char cwd[PATH_MAX];
    106   if (!getcwd(cwd, PATH_MAX)) {
    107     fprintf(stderr, "getcwd failed\n");
    108     CHECK(false);
    109   }
    110   bootstrap += StringPrintf("CURDIR:=%s\n", cwd);
    111   Parse(Intern(bootstrap).str(), Loc("*bootstrap*", 0), stmts);
    112 }
    113 
    114 static void SetVar(StringPiece l, VarOrigin origin) {
    115   size_t found = l.find('=');
    116   CHECK(found != string::npos);
    117   Symbol lhs = Intern(l.substr(0, found));
    118   StringPiece rhs = l.substr(found + 1);
    119   lhs.SetGlobalVar(
    120       new RecursiveVar(NewLiteral(rhs.data()), origin, rhs.data()));
    121 }
    122 
    123 extern "C" char** environ;
    124 
    125 class SegfaultHandler {
    126  public:
    127   explicit SegfaultHandler(Evaluator* ev);
    128   ~SegfaultHandler();
    129 
    130   void handle(int, siginfo_t*, void*);
    131 
    132  private:
    133   static SegfaultHandler* global_handler;
    134 
    135   void dumpstr(const char* s) const {
    136     (void)write(STDERR_FILENO, s, strlen(s));
    137   }
    138   void dumpint(int i) const {
    139     char buf[11];
    140     char* ptr = buf + sizeof(buf) - 1;
    141 
    142     if (i < 0) {
    143       i = -i;
    144       dumpstr("-");
    145     } else if (i == 0) {
    146       dumpstr("0");
    147       return;
    148     }
    149 
    150     *ptr = '\0';
    151     while (ptr > buf && i > 0) {
    152       *--ptr = '0' + (i % 10);
    153       i = i / 10;
    154     }
    155 
    156     dumpstr(ptr);
    157   }
    158 
    159   Evaluator* ev_;
    160 
    161   struct sigaction orig_action_;
    162   struct sigaction new_action_;
    163 };
    164 
    165 SegfaultHandler* SegfaultHandler::global_handler = nullptr;
    166 
    167 SegfaultHandler::SegfaultHandler(Evaluator* ev) : ev_(ev) {
    168   CHECK(global_handler == nullptr);
    169   global_handler = this;
    170 
    171   // Construct an alternate stack, so that we can handle stack overflows.
    172   stack_t ss;
    173   ss.ss_sp = malloc(SIGSTKSZ * 2);
    174   CHECK(ss.ss_sp != nullptr);
    175   ss.ss_size = SIGSTKSZ * 2;
    176   ss.ss_flags = 0;
    177   if (sigaltstack(&ss, nullptr) == -1) {
    178     PERROR("sigaltstack");
    179   }
    180 
    181   // Register our segfault handler using the alternate stack, falling
    182   // back to the default handler.
    183   sigemptyset(&new_action_.sa_mask);
    184   new_action_.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESETHAND;
    185   new_action_.sa_sigaction = [](int sig, siginfo_t* info, void* context) {
    186     if (global_handler != nullptr) {
    187       global_handler->handle(sig, info, context);
    188     }
    189 
    190     raise(SIGSEGV);
    191   };
    192   sigaction(SIGSEGV, &new_action_, &orig_action_);
    193 }
    194 
    195 void SegfaultHandler::handle(int sig, siginfo_t* info, void* context) {
    196   // Avoid fprintf in case it allocates or tries to do anything else that may
    197   // hang.
    198   dumpstr("*kati*: Segmentation fault, last evaluated line was ");
    199   dumpstr(ev_->loc().filename);
    200   dumpstr(":");
    201   dumpint(ev_->loc().lineno);
    202   dumpstr("\n");
    203 
    204   // Run the original handler, in case we've been preloaded with libSegFault
    205   // or similar.
    206   if (orig_action_.sa_sigaction != nullptr) {
    207     orig_action_.sa_sigaction(sig, info, context);
    208   }
    209 }
    210 
    211 SegfaultHandler::~SegfaultHandler() {
    212   sigaction(SIGSEGV, &orig_action_, nullptr);
    213   global_handler = nullptr;
    214 }
    215 
    216 static int Run(const vector<Symbol>& targets,
    217                const vector<StringPiece>& cl_vars,
    218                const string& orig_args) {
    219   double start_time = GetTime();
    220 
    221   if (g_flags.generate_ninja && (g_flags.regen || g_flags.dump_kati_stamp)) {
    222     ScopedTimeReporter tr("regen check time");
    223     if (!NeedsRegen(start_time, orig_args)) {
    224       fprintf(stderr, "No need to regenerate ninja file\n");
    225       return 0;
    226     }
    227     if (g_flags.dump_kati_stamp) {
    228       printf("Need to regenerate ninja file\n");
    229       return 0;
    230     }
    231     ClearGlobCache();
    232   }
    233 
    234   SetAffinityForSingleThread();
    235 
    236   MakefileCacheManager* cache_mgr = NewMakefileCacheManager();
    237 
    238   Intern("MAKEFILE_LIST")
    239       .SetGlobalVar(new SimpleVar(StringPrintf(" %s", g_flags.makefile),
    240                                   VarOrigin::FILE));
    241   for (char** p = environ; *p; p++) {
    242     SetVar(*p, VarOrigin::ENVIRONMENT);
    243   }
    244   unique_ptr<Evaluator> ev(new Evaluator());
    245   SegfaultHandler segfault(ev.get());
    246 
    247   vector<Stmt*> bootstrap_asts;
    248   ReadBootstrapMakefile(targets, &bootstrap_asts);
    249   ev->set_is_bootstrap(true);
    250   for (Stmt* stmt : bootstrap_asts) {
    251     LOG("%s", stmt->DebugString().c_str());
    252     stmt->Eval(ev.get());
    253   }
    254   ev->set_is_bootstrap(false);
    255 
    256   ev->set_is_commandline(true);
    257   for (StringPiece l : cl_vars) {
    258     vector<Stmt*> asts;
    259     Parse(Intern(l).str(), Loc("*bootstrap*", 0), &asts);
    260     CHECK(asts.size() == 1);
    261     asts[0]->Eval(ev.get());
    262   }
    263   ev->set_is_commandline(false);
    264 
    265   {
    266     ScopedTimeReporter tr("eval time");
    267     Makefile* mk = cache_mgr->ReadMakefile(g_flags.makefile);
    268     for (Stmt* stmt : mk->stmts()) {
    269       LOG("%s", stmt->DebugString().c_str());
    270       stmt->Eval(ev.get());
    271     }
    272   }
    273 
    274   for (ParseErrorStmt* err : GetParseErrors()) {
    275     WARN_LOC(err->loc(), "warning for parse error in an unevaluated line: %s",
    276              err->msg.c_str());
    277   }
    278 
    279   vector<DepNode*> nodes;
    280   {
    281     ScopedTimeReporter tr("make dep time");
    282     MakeDep(ev.get(), ev->rules(), ev->rule_vars(), targets, &nodes);
    283   }
    284 
    285   if (g_flags.is_syntax_check_only)
    286     return 0;
    287 
    288   if (g_flags.generate_ninja) {
    289     ScopedTimeReporter tr("generate ninja time");
    290     GenerateNinja(nodes, ev.get(), orig_args, start_time);
    291     ev->DumpStackStats();
    292     return 0;
    293   }
    294 
    295   for (const auto& p : ev->exports()) {
    296     const Symbol name = p.first;
    297     if (p.second) {
    298       Var* v = ev->LookupVar(name);
    299       const string&& value = v->Eval(ev.get());
    300       LOG("setenv(%s, %s)", name.c_str(), value.c_str());
    301       setenv(name.c_str(), value.c_str(), 1);
    302     } else {
    303       LOG("unsetenv(%s)", name.c_str());
    304       unsetenv(name.c_str());
    305     }
    306   }
    307 
    308   {
    309     ScopedTimeReporter tr("exec time");
    310     Exec(nodes, ev.get());
    311   }
    312 
    313   ev->DumpStackStats();
    314 
    315   for (Stmt* stmt : bootstrap_asts)
    316     delete stmt;
    317   delete cache_mgr;
    318 
    319   return 0;
    320 }
    321 
    322 static void FindFirstMakefie() {
    323   if (g_flags.makefile != NULL)
    324     return;
    325   if (Exists("GNUmakefile")) {
    326     g_flags.makefile = "GNUmakefile";
    327 #if !defined(__APPLE__)
    328   } else if (Exists("makefile")) {
    329     g_flags.makefile = "makefile";
    330 #endif
    331   } else if (Exists("Makefile")) {
    332     g_flags.makefile = "Makefile";
    333   }
    334 }
    335 
    336 static void HandleRealpath(int argc, char** argv) {
    337   char buf[PATH_MAX];
    338   for (int i = 0; i < argc; i++) {
    339     if (realpath(argv[i], buf))
    340       printf("%s\n", buf);
    341   }
    342 }
    343 
    344 int main(int argc, char* argv[]) {
    345   if (argc >= 2 && !strcmp(argv[1], "--realpath")) {
    346     HandleRealpath(argc - 2, argv + 2);
    347     return 0;
    348   }
    349   Init();
    350   string orig_args;
    351   for (int i = 0; i < argc; i++) {
    352     if (i)
    353       orig_args += ' ';
    354     orig_args += argv[i];
    355   }
    356   g_flags.Parse(argc, argv);
    357   FindFirstMakefie();
    358   if (g_flags.makefile == NULL)
    359     ERROR("*** No targets specified and no makefile found.");
    360   // This depends on command line flags.
    361   if (g_flags.use_find_emulator)
    362     InitFindEmulator();
    363   int r = Run(g_flags.targets, g_flags.cl_vars, orig_args);
    364   Quit();
    365   return r;
    366 }
    367