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   for (StringPiece l : cl_vars) {
    158     SetVar(l, VarOrigin::COMMAND_LINE);
    159   }
    160 
    161   {
    162     ScopedTimeReporter tr("eval time");
    163     Makefile* mk = cache_mgr->ReadMakefile(g_flags.makefile);
    164     for (Stmt* stmt : mk->stmts()) {
    165       LOG("%s", stmt->DebugString().c_str());
    166       stmt->Eval(ev);
    167     }
    168   }
    169 
    170   for (ParseErrorStmt* err : GetParseErrors()) {
    171     WARN("%s:%d: warning for parse error in an unevaluated line: %s",
    172          LOCF(err->loc()), err->msg.c_str());
    173   }
    174 
    175   vector<DepNode*> nodes;
    176   {
    177     ScopedTimeReporter tr("make dep time");
    178     MakeDep(ev, ev->rules(), ev->rule_vars(), targets, &nodes);
    179   }
    180 
    181   if (g_flags.is_syntax_check_only)
    182     return 0;
    183 
    184   if (g_flags.generate_ninja) {
    185     ScopedTimeReporter tr("generate ninja time");
    186     GenerateNinja(nodes, ev, orig_args, start_time);
    187     return 0;
    188   }
    189 
    190   for (const auto& p : ev->exports()) {
    191     const Symbol name = p.first;
    192     if (p.second) {
    193       Var* v = ev->LookupVar(name);
    194       const string&& value = v->Eval(ev);
    195       LOG("setenv(%s, %s)", name.c_str(), value.c_str());
    196       setenv(name.c_str(), value.c_str(), 1);
    197     } else {
    198       LOG("unsetenv(%s)", name.c_str());
    199       unsetenv(name.c_str());
    200     }
    201   }
    202 
    203   {
    204     ScopedTimeReporter tr("exec time");
    205     Exec(nodes, ev);
    206   }
    207 
    208   for (Stmt* stmt : bootstrap_asts)
    209     delete stmt;
    210   delete ev;
    211   delete cache_mgr;
    212 
    213   return 0;
    214 }
    215 
    216 static void FindFirstMakefie() {
    217   if (g_flags.makefile != NULL)
    218     return;
    219   if (Exists("GNUmakefile")) {
    220     g_flags.makefile = "GNUmakefile";
    221 #if !defined(__APPLE__)
    222   } else if (Exists("makefile")) {
    223     g_flags.makefile = "makefile";
    224 #endif
    225   } else if (Exists("Makefile")) {
    226     g_flags.makefile = "Makefile";
    227   }
    228 }
    229 
    230 static void HandleRealpath(int argc, char** argv) {
    231   char buf[PATH_MAX];
    232   for (int i = 0; i < argc; i++) {
    233     if (realpath(argv[i], buf))
    234       printf("%s\n", buf);
    235   }
    236 }
    237 
    238 int main(int argc, char* argv[]) {
    239   if (argc >= 2 && !strcmp(argv[1], "--realpath")) {
    240     HandleRealpath(argc - 2, argv + 2);
    241     return 0;
    242   }
    243   Init();
    244   string orig_args;
    245   for (int i = 0; i < argc; i++) {
    246     if (i)
    247       orig_args += ' ';
    248     orig_args += argv[i];
    249   }
    250   g_flags.Parse(argc, argv);
    251   FindFirstMakefie();
    252   if (g_flags.makefile == NULL)
    253     ERROR("*** No targets specified and no makefile found.");
    254   // This depends on command line flags.
    255   if (g_flags.use_find_emulator)
    256     InitFindEmulator();
    257   int r = Run(g_flags.targets, g_flags.cl_vars, orig_args);
    258   Quit();
    259   return r;
    260 }
    261