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 
Init()47 static void Init() {
48   InitSymtab();
49   InitFuncTable();
50   InitDepNodePool();
51   InitParser();
52 }
53 
Quit()54 static void Quit() {
55   ReportAllStats();
56 
57   QuitParser();
58   QuitDepNodePool();
59   QuitFuncTable();
60   QuitSymtab();
61 }
62 
ReadBootstrapMakefile(const vector<Symbol> & targets,vector<Stmt * > * stmts)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 
SetVar(StringPiece l,VarOrigin origin)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 
Run(const vector<Symbol> & targets,const vector<StringPiece> & cl_vars,const string & orig_args)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 
FindFirstMakefie()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 
HandleRealpath(int argc,char ** argv)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 
main(int argc,char * argv[])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