• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
__asan_default_options()49 extern "C" const char* __asan_default_options() {
50   return "detect_leaks=0:allow_user_segv_handler=1";
51 }
52 
Init()53 static void Init() {
54   InitSymtab();
55   InitFuncTable();
56   InitDepNodePool();
57   InitParser();
58 }
59 
Quit()60 static void Quit() {
61   ReportAllStats();
62 
63   QuitParser();
64   QuitDepNodePool();
65   QuitFuncTable();
66   QuitSymtab();
67 }
68 
ReadBootstrapMakefile(const vector<Symbol> & targets,vector<Stmt * > * stmts)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 
87   if (!g_flags.no_builtin_rules) {
88     bootstrap += (
89         // http://www.gnu.org/software/make/manual/make.html#Catalogue-of-Rules
90         // The document above is actually not correct. See default.c:
91         // http://git.savannah.gnu.org/cgit/make.git/tree/default.c?id=4.1
92         ".c.o:\n"
93         "\t$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<\n"
94         ".cc.o:\n"
95         "\t$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<\n"
96         // TODO: Add more builtin rules.
97     );
98   }
99   if (g_flags.generate_ninja) {
100     bootstrap += StringPrintf("MAKE?=make -j%d\n",
101                               g_flags.num_jobs <= 1 ? 1 : g_flags.num_jobs / 2);
102   } else {
103     bootstrap += StringPrintf("MAKE?=%s\n",
104                               JoinStrings(g_flags.subkati_args, " ").c_str());
105   }
106   bootstrap +=
107       StringPrintf("MAKECMDGOALS?=%s\n", JoinSymbols(targets, " ").c_str());
108 
109   char cwd[PATH_MAX];
110   if (!getcwd(cwd, PATH_MAX)) {
111     fprintf(stderr, "getcwd failed\n");
112     CHECK(false);
113   }
114   bootstrap += StringPrintf("CURDIR:=%s\n", cwd);
115   Parse(Intern(bootstrap).str(), Loc("*bootstrap*", 0), stmts);
116 }
117 
SetVar(StringPiece l,VarOrigin origin)118 static void SetVar(StringPiece l, VarOrigin origin) {
119   size_t found = l.find('=');
120   CHECK(found != string::npos);
121   Symbol lhs = Intern(l.substr(0, found));
122   StringPiece rhs = l.substr(found + 1);
123   lhs.SetGlobalVar(
124       new RecursiveVar(Value::NewLiteral(rhs.data()), origin, rhs.data()));
125 }
126 
127 extern "C" char** environ;
128 
129 class SegfaultHandler {
130  public:
131   explicit SegfaultHandler(Evaluator* ev);
132   ~SegfaultHandler();
133 
134   void handle(int, siginfo_t*, void*);
135 
136  private:
137   static SegfaultHandler* global_handler;
138 
dumpstr(const char * s) const139   void dumpstr(const char* s) const {
140     (void)write(STDERR_FILENO, s, strlen(s));
141   }
dumpint(int i) const142   void dumpint(int i) const {
143     char buf[11];
144     char* ptr = buf + sizeof(buf) - 1;
145 
146     if (i < 0) {
147       i = -i;
148       dumpstr("-");
149     } else if (i == 0) {
150       dumpstr("0");
151       return;
152     }
153 
154     *ptr = '\0';
155     while (ptr > buf && i > 0) {
156       *--ptr = '0' + (i % 10);
157       i = i / 10;
158     }
159 
160     dumpstr(ptr);
161   }
162 
163   Evaluator* ev_;
164 
165   struct sigaction orig_action_;
166   struct sigaction new_action_;
167 };
168 
169 SegfaultHandler* SegfaultHandler::global_handler = nullptr;
170 
SegfaultHandler(Evaluator * ev)171 SegfaultHandler::SegfaultHandler(Evaluator* ev) : ev_(ev) {
172   CHECK(global_handler == nullptr);
173   global_handler = this;
174 
175   // Construct an alternate stack, so that we can handle stack overflows.
176   stack_t ss;
177   ss.ss_sp = malloc(SIGSTKSZ * 2);
178   CHECK(ss.ss_sp != nullptr);
179   ss.ss_size = SIGSTKSZ * 2;
180   ss.ss_flags = 0;
181   if (sigaltstack(&ss, nullptr) == -1) {
182     PERROR("sigaltstack");
183   }
184 
185   // Register our segfault handler using the alternate stack, falling
186   // back to the default handler.
187   sigemptyset(&new_action_.sa_mask);
188   new_action_.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_RESETHAND;
189   new_action_.sa_sigaction = [](int sig, siginfo_t* info, void* context) {
190     if (global_handler != nullptr) {
191       global_handler->handle(sig, info, context);
192     }
193 
194     raise(SIGSEGV);
195   };
196   sigaction(SIGSEGV, &new_action_, &orig_action_);
197 }
198 
handle(int sig,siginfo_t * info,void * context)199 void SegfaultHandler::handle(int sig, siginfo_t* info, void* context) {
200   // Avoid fprintf in case it allocates or tries to do anything else that may
201   // hang.
202   dumpstr("*kati*: Segmentation fault, last evaluated line was ");
203   dumpstr(ev_->loc().filename);
204   dumpstr(":");
205   dumpint(ev_->loc().lineno);
206   dumpstr("\n");
207 
208   // Run the original handler, in case we've been preloaded with libSegFault
209   // or similar.
210   if (orig_action_.sa_sigaction != nullptr) {
211     orig_action_.sa_sigaction(sig, info, context);
212   }
213 }
214 
~SegfaultHandler()215 SegfaultHandler::~SegfaultHandler() {
216   sigaction(SIGSEGV, &orig_action_, nullptr);
217   global_handler = nullptr;
218 }
219 
Run(const vector<Symbol> & targets,const vector<StringPiece> & cl_vars,const string & orig_args)220 static int Run(const vector<Symbol>& targets,
221                const vector<StringPiece>& cl_vars,
222                const string& orig_args) {
223   double start_time = GetTime();
224 
225   if (g_flags.generate_ninja && (g_flags.regen || g_flags.dump_kati_stamp)) {
226     ScopedTimeReporter tr("regen check time");
227     if (!NeedsRegen(start_time, orig_args)) {
228       fprintf(stderr, "No need to regenerate ninja file\n");
229       return 0;
230     }
231     if (g_flags.dump_kati_stamp) {
232       printf("Need to regenerate ninja file\n");
233       return 0;
234     }
235     ClearGlobCache();
236   }
237 
238   SetAffinityForSingleThread();
239 
240   MakefileCacheManager* cache_mgr = NewMakefileCacheManager();
241 
242   Intern("MAKEFILE_LIST")
243       .SetGlobalVar(new SimpleVar(StringPrintf(" %s", g_flags.makefile),
244                                   VarOrigin::FILE));
245   for (char** p = environ; *p; p++) {
246     SetVar(*p, VarOrigin::ENVIRONMENT);
247   }
248   unique_ptr<Evaluator> ev(new Evaluator());
249   SegfaultHandler segfault(ev.get());
250 
251   vector<Stmt*> bootstrap_asts;
252   ReadBootstrapMakefile(targets, &bootstrap_asts);
253   ev->set_is_bootstrap(true);
254   for (Stmt* stmt : bootstrap_asts) {
255     LOG("%s", stmt->DebugString().c_str());
256     stmt->Eval(ev.get());
257   }
258   ev->set_is_bootstrap(false);
259 
260   ev->set_is_commandline(true);
261   for (StringPiece l : cl_vars) {
262     vector<Stmt*> asts;
263     Parse(Intern(l).str(), Loc("*bootstrap*", 0), &asts);
264     CHECK(asts.size() == 1);
265     asts[0]->Eval(ev.get());
266   }
267   ev->set_is_commandline(false);
268 
269   {
270     ScopedTimeReporter tr("eval time");
271     Makefile* mk = cache_mgr->ReadMakefile(g_flags.makefile);
272     for (Stmt* stmt : mk->stmts()) {
273       LOG("%s", stmt->DebugString().c_str());
274       stmt->Eval(ev.get());
275     }
276   }
277 
278   for (ParseErrorStmt* err : GetParseErrors()) {
279     WARN_LOC(err->loc(), "warning for parse error in an unevaluated line: %s",
280              err->msg.c_str());
281   }
282 
283   vector<NamedDepNode> nodes;
284   {
285     ScopedTimeReporter tr("make dep time");
286     MakeDep(ev.get(), ev->rules(), ev->rule_vars(), targets, &nodes);
287   }
288 
289   if (g_flags.is_syntax_check_only)
290     return 0;
291 
292   if (g_flags.generate_ninja) {
293     ScopedTimeReporter tr("generate ninja time");
294     GenerateNinja(nodes, ev.get(), orig_args, start_time);
295     ev->DumpStackStats();
296     return 0;
297   }
298 
299   for (const auto& p : ev->exports()) {
300     const Symbol name = p.first;
301     if (p.second) {
302       Var* v = ev->LookupVar(name);
303       const string&& value = v->Eval(ev.get());
304       LOG("setenv(%s, %s)", name.c_str(), value.c_str());
305       setenv(name.c_str(), value.c_str(), 1);
306     } else {
307       LOG("unsetenv(%s)", name.c_str());
308       unsetenv(name.c_str());
309     }
310   }
311 
312   {
313     ScopedTimeReporter tr("exec time");
314     Exec(nodes, ev.get());
315   }
316 
317   ev->DumpStackStats();
318 
319   for (Stmt* stmt : bootstrap_asts)
320     delete stmt;
321   delete cache_mgr;
322 
323   return 0;
324 }
325 
FindFirstMakefie()326 static void FindFirstMakefie() {
327   if (g_flags.makefile != NULL)
328     return;
329   if (Exists("GNUmakefile")) {
330     g_flags.makefile = "GNUmakefile";
331 #if !defined(__APPLE__)
332   } else if (Exists("makefile")) {
333     g_flags.makefile = "makefile";
334 #endif
335   } else if (Exists("Makefile")) {
336     g_flags.makefile = "Makefile";
337   }
338 }
339 
HandleRealpath(int argc,char ** argv)340 static void HandleRealpath(int argc, char** argv) {
341   char buf[PATH_MAX];
342   for (int i = 0; i < argc; i++) {
343     if (realpath(argv[i], buf))
344       printf("%s\n", buf);
345   }
346 }
347 
main(int argc,char * argv[])348 int main(int argc, char* argv[]) {
349   if (argc >= 2 && !strcmp(argv[1], "--realpath")) {
350     HandleRealpath(argc - 2, argv + 2);
351     return 0;
352   }
353   Init();
354   string orig_args;
355   for (int i = 0; i < argc; i++) {
356     if (i)
357       orig_args += ' ';
358     orig_args += argv[i];
359   }
360   g_flags.Parse(argc, argv);
361   FindFirstMakefie();
362   if (g_flags.makefile == NULL)
363     ERROR("*** No targets specified and no makefile found.");
364   // This depends on command line flags.
365   if (g_flags.use_find_emulator)
366     InitFindEmulator();
367   int r = Run(g_flags.targets, g_flags.cl_vars, orig_args);
368   Quit();
369   return r;
370 }
371