// Copyright 2015 Google Inc. All rights reserved // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // +build ignore #include #include #include #include #include #include "ast.h" #include "dep.h" #include "eval.h" #include "exec.h" #include "file.h" #include "file_cache.h" #include "fileutil.h" #include "find.h" #include "flags.h" #include "func.h" #include "log.h" #include "ninja.h" #include "parser.h" #include "stats.h" #include "string_piece.h" #include "stringprintf.h" #include "strutil.h" #include "symtab.h" #include "timeutil.h" #include "var.h" static const char* g_makefile; static bool g_is_syntax_check_only; static bool g_generate_ninja; static const char* g_ninja_suffix; static const char* g_ninja_dir; static bool g_use_find_emulator; static bool ParseCommandLineOptionWithArg(StringPiece option, char* argv[], int* index, const char** out_arg) { const char* arg = argv[*index]; if (!HasPrefix(arg, option)) return false; if (arg[option.size()] == '\0') { ++*index; *out_arg = argv[*index]; return true; } if (arg[option.size()] == '=') { *out_arg = arg + option.size() + 1; return true; } // E.g, -j999 if (option.size() == 2) { *out_arg = arg + option.size(); return true; } return false; } static void ParseCommandLine(int argc, char* argv[], vector* targets, vector* cl_vars) { // TODO: Decide the appropriate number based on the number of cores. g_num_jobs = 32; const char* num_jobs_str; for (int i = 1; i < argc; i++) { const char* arg = argv[i]; if (!strcmp(arg, "-f")) { g_makefile = argv[++i]; } else if (!strcmp(arg, "-c")) { g_is_syntax_check_only = true; } else if (!strcmp(arg, "-i")) { g_is_dry_run = true; } else if (!strcmp(arg, "--kati_stats")) { g_enable_stat_logs = true; } else if (!strcmp(arg, "--ninja")) { g_generate_ninja = true; } else if (!strcmp(arg, "--detect_android_echo")) { g_detect_android_echo = true; } else if (!strcmp(arg, "--error_on_env_change")) { g_error_on_env_change = true; } else if (ParseCommandLineOptionWithArg( "-j", argv, &i, &num_jobs_str)) { g_num_jobs = strtol(num_jobs_str, NULL, 10); if (g_num_jobs <= 0) { ERROR("Invalid -j flag: %s", num_jobs_str); } } else if (ParseCommandLineOptionWithArg( "--ninja_suffix", argv, &i, &g_ninja_suffix)) { } else if (ParseCommandLineOptionWithArg( "--ninja_dir", argv, &i, &g_ninja_dir)) { } else if (!strcmp(arg, "--use_find_emulator")) { g_use_find_emulator = true; } else if (!strcmp(arg, "--gen_regen_rule")) { // TODO: Make this default once we have removed unnecessary // command line change from Android build. g_gen_regen_rule = true; } else if (ParseCommandLineOptionWithArg( "--goma_dir", argv, &i, &g_goma_dir)) { } else if (ParseCommandLineOptionWithArg( "--ignore_optional_include", argv, &i, &g_ignore_optional_include_pattern)) { } else if (arg[0] == '-') { ERROR("Unknown flag: %s", arg); } else { if (strchr(arg, '=')) { cl_vars->push_back(arg); } else { targets->push_back(Intern(arg)); } } } } static void Init() { InitSymtab(); InitFuncTable(); InitDepNodePool(); InitParser(); if (g_makefile == NULL) { if (Exists("GNUmakefile")) { g_makefile = "GNUmakefile"; #if !defined(__APPLE__) } else if (Exists("makefile")) { g_makefile = "makefile"; #endif } else if (Exists("Makefile")) { g_makefile = "Makefile"; } else { ERROR("*** No targets specified and no makefile found."); } } } static void Quit() { ReportAllStats(); QuitParser(); QuitDepNodePool(); QuitFuncTable(); QuitSymtab(); } static void ReadBootstrapMakefile(const vector& targets, vector* asts) { string bootstrap = ( "CC:=cc\n" #if defined(__APPLE__) "CXX:=c++\n" #else "CXX:=g++\n" #endif "AR:=ar\n" "MAKE:=kati\n" // Pretend to be GNU make 3.81, for compatibility. "MAKE_VERSION:=3.81\n" "SHELL:=/bin/sh\n" // TODO: Add more builtin vars. // http://www.gnu.org/software/make/manual/make.html#Catalogue-of-Rules // The document above is actually not correct. See default.c: // http://git.savannah.gnu.org/cgit/make.git/tree/default.c?id=4.1 ".c.o:\n" "\t$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<\n" ".cc.o:\n" "\t$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<\n" // TODO: Add more builtin rules. ); bootstrap += StringPrintf("MAKECMDGOALS:=%s\n", JoinSymbols(targets, " ").c_str()); char cwd[PATH_MAX]; if (!getcwd(cwd, PATH_MAX)) { fprintf(stderr, "getcwd failed\n"); CHECK(false); } bootstrap += StringPrintf("CURDIR:=%s\n", cwd); Parse(Intern(bootstrap).str(), Loc("*bootstrap*", 0), asts); } static void SetVar(StringPiece l, VarOrigin origin, Vars* vars) { size_t found = l.find('='); CHECK(found != string::npos); Symbol lhs = Intern(l.substr(0, found)); StringPiece rhs = l.substr(found + 1); vars->Assign(lhs, new RecursiveVar(NewLiteral(rhs.data()), origin, rhs.data())); } extern "C" char** environ; static void FillDefaultVars(const vector& cl_vars, Vars* vars) { for (char** p = environ; *p; p++) { SetVar(*p, VarOrigin::ENVIRONMENT, vars); } for (StringPiece l : cl_vars) { SetVar(l, VarOrigin::COMMAND_LINE, vars); } } static int Run(const vector& targets, const vector& cl_vars, const string& orig_args) { MakefileCacheManager* cache_mgr = NewMakefileCacheManager(); Vars* vars = new Vars(); FillDefaultVars(cl_vars, vars); Evaluator* ev = new Evaluator(vars); vector bootstrap_asts; ReadBootstrapMakefile(targets, &bootstrap_asts); ev->set_is_bootstrap(true); for (AST* ast : bootstrap_asts) { LOG("%s", ast->DebugString().c_str()); ast->Eval(ev); } ev->set_is_bootstrap(false); vars->Assign(Intern("MAKEFILE_LIST"), new SimpleVar(make_shared( StringPrintf(" %s", g_makefile)), VarOrigin::FILE)); { ScopedTimeReporter tr("eval time"); Makefile* mk = cache_mgr->ReadMakefile(g_makefile); for (AST* ast : mk->asts()) { LOG("%s", ast->DebugString().c_str()); ast->Eval(ev); } } vector nodes; { ScopedTimeReporter tr("make dep time"); MakeDep(ev, ev->rules(), ev->rule_vars(), targets, &nodes); } for (const auto& p : ev->exports()) { const Symbol name = p.first; if (p.second) { Var* v = ev->LookupVar(name); shared_ptr value = v->Eval(ev); LOG("setenv(%s, %s)", name.c_str(), value->c_str()); setenv(name.c_str(), value->c_str(), 1); } else { LOG("unsetenv(%s)", name.c_str()); unsetenv(name.c_str()); } } if (g_is_syntax_check_only) return 0; if (g_generate_ninja) { ScopedTimeReporter tr("generate ninja time"); GenerateNinja(g_ninja_suffix, g_ninja_dir, nodes, ev, !targets.empty(), orig_args); return 0; } { ScopedTimeReporter tr("exec time"); Exec(nodes, ev); } for (AST* ast : bootstrap_asts) delete ast; delete ev; delete vars; delete cache_mgr; return 0; } int main(int argc, char* argv[]) { Init(); string orig_args; for (int i = 0; i < argc; i++) { if (i) orig_args += ' '; orig_args += argv[i]; } vector targets; vector cl_vars; ParseCommandLine(argc, argv, &targets, &cl_vars); // This depends on command line flags. if (g_use_find_emulator) InitFindEmulator(); int r = Run(targets, cl_vars, orig_args); Quit(); return r; }