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