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