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