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 <stdio.h>
     19 #include <string.h>
     20 #include <stdlib.h>
     21 #include <time.h>
     22 #include <unistd.h>
     23 
     24 #include "affinity.h"
     25 #include "dep.h"
     26 #include "eval.h"
     27 #include "exec.h"
     28 #include "file.h"
     29 #include "file_cache.h"
     30 #include "fileutil.h"
     31 #include "find.h"
     32 #include "flags.h"
     33 #include "func.h"
     34 #include "log.h"
     35 #include "ninja.h"
     36 #include "parser.h"
     37 #include "regen.h"
     38 #include "stats.h"
     39 #include "stmt.h"
     40 #include "string_piece.h"
     41 #include "stringprintf.h"
     42 #include "strutil.h"
     43 #include "symtab.h"
     44 #include "timeutil.h"
     45 #include "var.h"
     46 
     47 static void Init() {
     48   InitSymtab();
     49   InitFuncTable();
     50   InitDepNodePool();
     51   InitParser();
     52 }
     53 
     54 static void Quit() {
     55   ReportAllStats();
     56 
     57   QuitParser();
     58   QuitDepNodePool();
     59   QuitFuncTable();
     60   QuitSymtab();
     61 }
     62 
     63 static void ReadBootstrapMakefile(const vector<Symbol>& targets,
     64                                   vector<Stmt*>* stmts) {
     65   string bootstrap = (
     66       "CC?=cc\n"
     67 #if defined(__APPLE__)
     68       "CXX?=c++\n"
     69 #else
     70       "CXX?=g++\n"
     71 #endif
     72       "AR?=ar\n"
     73       // Pretend to be GNU make 3.81, for compatibility.
     74       "MAKE_VERSION?=3.81\n"
     75       "KATI?=ckati\n"
     76       // Overwrite $SHELL environment variable.
     77       "SHELL=/bin/sh\n"
     78       // TODO: Add more builtin vars.
     79 
     80       // http://www.gnu.org/software/make/manual/make.html#Catalogue-of-Rules
     81       // The document above is actually not correct. See default.c:
     82       // http://git.savannah.gnu.org/cgit/make.git/tree/default.c?id=4.1
     83       ".c.o:\n"
     84       "\t$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<\n"
     85       ".cc.o:\n"
     86       "\t$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<\n"
     87       // TODO: Add more builtin rules.
     88                       );
     89   if (g_flags.generate_ninja) {
     90     bootstrap += StringPrintf("MAKE?=make -j%d\n",
     91                               g_flags.num_jobs <= 1 ? 1 : g_flags.num_jobs / 2);
     92   } else {
     93     bootstrap += StringPrintf("MAKE?=%s\n",
     94                               JoinStrings(g_flags.subkati_args, " ").c_str());
     95   }
     96   bootstrap += StringPrintf("MAKECMDGOALS?=%s\n",
     97                             JoinSymbols(targets, " ").c_str());
     98 
     99   char cwd[PATH_MAX];
    100   if (!getcwd(cwd, PATH_MAX)) {
    101     fprintf(stderr, "getcwd failed\n");
    102     CHECK(false);
    103   }
    104   bootstrap += StringPrintf("CURDIR:=%s\n", cwd);
    105   Parse(Intern(bootstrap).str(), Loc("*bootstrap*", 0), stmts);
    106 }
    107 
    108 static void SetVar(StringPiece l, VarOrigin origin) {
    109   size_t found = l.find('=');
    110   CHECK(found != string::npos);
    111   Symbol lhs = Intern(l.substr(0, found));
    112   StringPiece rhs = l.substr(found + 1);
    113   lhs.SetGlobalVar(
    114       new RecursiveVar(NewLiteral(rhs.data()), origin, rhs.data()));
    115 }
    116 
    117 extern "C" char** environ;
    118 
    119 static int Run(const vector<Symbol>& targets,
    120                const vector<StringPiece>& cl_vars,
    121                const string& orig_args) {
    122   double start_time = GetTime();
    123 
    124   if (g_flags.generate_ninja && (g_flags.regen || g_flags.dump_kati_stamp)) {
    125     ScopedTimeReporter tr("regen check time");
    126     if (!NeedsRegen(start_time, orig_args)) {
    127       fprintf(stderr, "No need to regenerate ninja file\n");
    128       return 0;
    129     }
    130     if (g_flags.dump_kati_stamp) {
    131       printf("Need to regenerate ninja file\n");
    132       return 0;
    133     }
    134     ClearGlobCache();
    135   }
    136 
    137   SetAffinityForSingleThread();
    138 
    139   MakefileCacheManager* cache_mgr = NewMakefileCacheManager();
    140 
    141   Intern("MAKEFILE_LIST").SetGlobalVar(
    142       new SimpleVar(StringPrintf(" %s", g_flags.makefile), VarOrigin::FILE));
    143   for (char** p = environ; *p; p++) {
    144     SetVar(*p, VarOrigin::ENVIRONMENT);
    145   }
    146   Evaluator* ev = new Evaluator();
    147 
    148   vector<Stmt*> bootstrap_asts;
    149   ReadBootstrapMakefile(targets, &bootstrap_asts);
    150   ev->set_is_bootstrap(true);
    151   for (Stmt* stmt : bootstrap_asts) {
    152     LOG("%s", stmt->DebugString().c_str());
    153     stmt->Eval(ev);
    154   }
    155   ev->set_is_bootstrap(false);
    156 
    157   ev->set_is_commandline(true);
    158   for (StringPiece l : cl_vars) {
    159     vector<Stmt*> asts;
    160     Parse(Intern(l).str(), Loc("*bootstrap*", 0), &asts);
    161     CHECK(asts.size() == 1);
    162     asts[0]->Eval(ev);
    163   }
    164   ev->set_is_commandline(false);
    165 
    166   {
    167     ScopedTimeReporter tr("eval time");
    168     Makefile* mk = cache_mgr->ReadMakefile(g_flags.makefile);
    169     for (Stmt* stmt : mk->stmts()) {
    170       LOG("%s", stmt->DebugString().c_str());
    171       stmt->Eval(ev);
    172     }
    173   }
    174 
    175   for (ParseErrorStmt* err : GetParseErrors()) {
    176     WARN_LOC(err->loc(), "warning for parse error in an unevaluated line: %s",
    177              err->msg.c_str());
    178   }
    179 
    180   vector<DepNode*> nodes;
    181   {
    182     ScopedTimeReporter tr("make dep time");
    183     MakeDep(ev, ev->rules(), ev->rule_vars(), targets, &nodes);
    184   }
    185 
    186   if (g_flags.is_syntax_check_only)
    187     return 0;
    188 
    189   if (g_flags.generate_ninja) {
    190     ScopedTimeReporter tr("generate ninja time");
    191     GenerateNinja(nodes, ev, orig_args, start_time);
    192     return 0;
    193   }
    194 
    195   for (const auto& p : ev->exports()) {
    196     const Symbol name = p.first;
    197     if (p.second) {
    198       Var* v = ev->LookupVar(name);
    199       const string&& value = v->Eval(ev);
    200       LOG("setenv(%s, %s)", name.c_str(), value.c_str());
    201       setenv(name.c_str(), value.c_str(), 1);
    202     } else {
    203       LOG("unsetenv(%s)", name.c_str());
    204       unsetenv(name.c_str());
    205     }
    206   }
    207 
    208   {
    209     ScopedTimeReporter tr("exec time");
    210     Exec(nodes, ev);
    211   }
    212 
    213   for (Stmt* stmt : bootstrap_asts)
    214     delete stmt;
    215   delete ev;
    216   delete cache_mgr;
    217 
    218   return 0;
    219 }
    220 
    221 static void FindFirstMakefie() {
    222   if (g_flags.makefile != NULL)
    223     return;
    224   if (Exists("GNUmakefile")) {
    225     g_flags.makefile = "GNUmakefile";
    226 #if !defined(__APPLE__)
    227   } else if (Exists("makefile")) {
    228     g_flags.makefile = "makefile";
    229 #endif
    230   } else if (Exists("Makefile")) {
    231     g_flags.makefile = "Makefile";
    232   }
    233 }
    234 
    235 static void HandleRealpath(int argc, char** argv) {
    236   char buf[PATH_MAX];
    237   for (int i = 0; i < argc; i++) {
    238     if (realpath(argv[i], buf))
    239       printf("%s\n", buf);
    240   }
    241 }
    242 
    243 int main(int argc, char* argv[]) {
    244   if (argc >= 2 && !strcmp(argv[1], "--realpath")) {
    245     HandleRealpath(argc - 2, argv + 2);
    246     return 0;
    247   }
    248   Init();
    249   string orig_args;
    250   for (int i = 0; i < argc; i++) {
    251     if (i)
    252       orig_args += ' ';
    253     orig_args += argv[i];
    254   }
    255   g_flags.Parse(argc, argv);
    256   FindFirstMakefie();
    257   if (g_flags.makefile == NULL)
    258     ERROR("*** No targets specified and no makefile found.");
    259   // This depends on command line flags.
    260   if (g_flags.use_find_emulator)
    261     InitFindEmulator();
    262   int r = Run(g_flags.targets, g_flags.cl_vars, orig_args);
    263   Quit();
    264   return r;
    265 }
    266