diff options
-rw-r--r-- | Makefile.ckati | 1 | ||||
-rw-r--r-- | main.cc | 1 | ||||
-rw-r--r-- | ninja.cc | 317 | ||||
-rw-r--r-- | ninja.h | 4 | ||||
-rw-r--r-- | regen.cc | 297 | ||||
-rw-r--r-- | regen.h | 24 |
6 files changed, 349 insertions, 295 deletions
diff --git a/Makefile.ckati b/Makefile.ckati index 9c1f016..0ba8385 100644 --- a/Makefile.ckati +++ b/Makefile.ckati @@ -38,6 +38,7 @@ KATI_CXX_SRCS := \ main.cc \ ninja.cc \ parser.cc \ + regen.cc \ rule.cc \ stats.cc \ stmt.cc \ @@ -33,6 +33,7 @@ #include "log.h" #include "ninja.h" #include "parser.h" +#include "regen.h" #include "stats.h" #include "stmt.h" #include "string_piece.h" @@ -21,7 +21,6 @@ #include <sys/stat.h> #include <unistd.h> -#include <algorithm> #include <map> #include <string> #include <unordered_map> @@ -189,28 +188,23 @@ class NinjaGenerator { void Generate(const vector<DepNode*>& nodes, const string& orig_args) { - unlink(GetStampFilename().c_str()); + unlink(GetNinjaStampFilename().c_str()); GenerateNinja(nodes, orig_args); GenerateShell(); GenerateStamp(orig_args); } - static string GetNinjaFilename() { - return GetFilename("build%s.ninja"); - } - - static string GetShellScriptFilename() { - return GetFilename("ninja%s.sh"); - } - - static string GetStampFilename() { - return GetFilename(".kati_stamp%s"); - } - static string GetStampTempFilename() { return GetFilename(".kati_stamp%s.tmp"); } + static string GetFilename(const char* fmt) { + string r = g_flags.ninja_dir ? g_flags.ninja_dir : "."; + r += '/'; + r += StringPrintf(fmt, g_flags.ninja_suffix ? g_flags.ninja_suffix : ""); + return r; + } + private: string GenRuleName() { return StringPrintf("rule%d", rule_id_++); @@ -579,13 +573,6 @@ class NinjaGenerator { return GetFilename("env%s.sh"); } - static string GetFilename(const char* fmt) { - string r = g_flags.ninja_dir ? g_flags.ninja_dir : "."; - r += '/'; - r += StringPrintf(fmt, g_flags.ninja_suffix ? g_flags.ninja_suffix : ""); - return r; - } - void GenerateNinja(const vector<DepNode*>& nodes, const string& orig_args) { fp_ = fopen(GetNinjaFilename().c_str(), "wb"); @@ -663,7 +650,7 @@ class NinjaGenerator { fclose(fp); - fp = fopen(GetShellScriptFilename().c_str(), "wb"); + fp = fopen(GetNinjaShellScriptFilename().c_str(), "wb"); if (fp == NULL) PERROR("fopen(ninja.sh) failed"); @@ -683,7 +670,7 @@ class NinjaGenerator { fclose(fp); - if (chmod(GetShellScriptFilename().c_str(), 0755) != 0) + if (chmod(GetNinjaShellScriptFilename().c_str(), 0755) != 0) PERROR("chmod ninja.sh failed"); } @@ -766,7 +753,7 @@ class NinjaGenerator { fclose(fp); - rename(GetStampTempFilename().c_str(), GetStampFilename().c_str()); + rename(GetStampTempFilename().c_str(), GetNinjaStampFilename().c_str()); } CommandEvaluator ce_; @@ -782,6 +769,18 @@ class NinjaGenerator { DepNode* default_target_; }; +string GetNinjaFilename() { + return NinjaGenerator::GetFilename("build%s.ninja"); +} + +string GetNinjaShellScriptFilename() { + return NinjaGenerator::GetFilename("ninja%s.sh"); +} + +string GetNinjaStampFilename() { + return NinjaGenerator::GetFilename(".kati_stamp%s"); +} + void GenerateNinja(const vector<DepNode*>& nodes, Evaluator* ev, const string& orig_args, @@ -789,273 +788,3 @@ void GenerateNinja(const vector<DepNode*>& nodes, NinjaGenerator ng(ev, start_time); ng.Generate(nodes, orig_args); } - -static bool ShouldIgnoreDirty(StringPiece s) { - Pattern pat(g_flags.ignore_dirty_pattern); - Pattern nopat(g_flags.no_ignore_dirty_pattern); - return pat.Match(s) && !nopat.Match(s); -} - -bool NeedsRegen(double start_time, const string& orig_args) { - bool retval = false; -#define RETURN_TRUE do { \ - if (g_flags.dump_kati_stamp) \ - retval = true; \ - else \ - return true; \ - } while (0) - -#define LOAD_INT(fp) ({ \ - int v = LoadInt(fp); \ - if (v < 0) { \ - fprintf(stderr, "incomplete kati_stamp, regenerating...\n"); \ - RETURN_TRUE; \ - } \ - v; \ - }) - -#define LOAD_STRING(fp, s) ({ \ - if (!LoadString(fp, s)) { \ - fprintf(stderr, "incomplete kati_stamp, regenerating...\n"); \ - RETURN_TRUE; \ - } \ - }) - - if (!Exists(NinjaGenerator::GetNinjaFilename())) { - fprintf(stderr, "%s is missing, regenerating...\n", - NinjaGenerator::GetNinjaFilename().c_str()); - return true; - } - if (!Exists(NinjaGenerator::GetShellScriptFilename())) { - fprintf(stderr, "%s is missing, regenerating...\n", - NinjaGenerator::GetShellScriptFilename().c_str()); - return true; - } - - const string& stamp_filename = NinjaGenerator::GetStampFilename(); - FILE* fp = fopen(stamp_filename.c_str(), "rb+"); - if (!fp) { - if (g_flags.dump_kati_stamp) - printf("%s: %s\n", stamp_filename.c_str(), strerror(errno)); - return true; - } - ScopedFile sfp(fp); - - double gen_time; - size_t r = fread(&gen_time, sizeof(gen_time), 1, fp); - if (r != 1) { - fprintf(stderr, "incomplete kati_stamp, regenerating...\n"); - RETURN_TRUE; - } - if (g_flags.dump_kati_stamp) - printf("Generated time: %f\n", gen_time); - - string s, s2; - int num_files = LOAD_INT(fp); - for (int i = 0; i < num_files; i++) { - LOAD_STRING(fp, &s); - double ts = GetTimestamp(s); - if (gen_time < ts) { - if (g_flags.regen_ignoring_kati_binary) { - string kati_binary; - GetExecutablePath(&kati_binary); - if (s == kati_binary) { - fprintf(stderr, "%s was modified, ignored.\n", s.c_str()); - continue; - } - } - if (ShouldIgnoreDirty(s)) { - if (g_flags.dump_kati_stamp) - printf("file %s: ignored (%f)\n", s.c_str(), ts); - continue; - } - if (g_flags.dump_kati_stamp) - printf("file %s: dirty (%f)\n", s.c_str(), ts); - else - fprintf(stderr, "%s was modified, regenerating...\n", s.c_str()); - RETURN_TRUE; - } else if (g_flags.dump_kati_stamp) { - printf("file %s: clean (%f)\n", s.c_str(), ts); - } - } - - int num_undefineds = LOAD_INT(fp); - for (int i = 0; i < num_undefineds; i++) { - LOAD_STRING(fp, &s); - if (getenv(s.c_str())) { - if (g_flags.dump_kati_stamp) { - printf("env %s: dirty (unset => %s)\n", s.c_str(), getenv(s.c_str())); - } else { - fprintf(stderr, "Environment variable %s was set, regenerating...\n", - s.c_str()); - } - RETURN_TRUE; - } else if (g_flags.dump_kati_stamp) { - printf("env %s: clean (unset)\n", s.c_str()); - } - } - - int num_envs = LOAD_INT(fp); - for (int i = 0; i < num_envs; i++) { - LOAD_STRING(fp, &s); - StringPiece val(getenv(s.c_str())); - LOAD_STRING(fp, &s2); - if (val != s2) { - if (g_flags.dump_kati_stamp) { - printf("env %s: dirty (%s => %.*s)\n", - s.c_str(), s2.c_str(), SPF(val)); - } else { - fprintf(stderr, "Environment variable %s was modified (%s => %.*s), " - "regenerating...\n", - s.c_str(), s2.c_str(), SPF(val)); - } - RETURN_TRUE; - } else if (g_flags.dump_kati_stamp) { - printf("env %s: clean (%.*s)\n", s.c_str(), SPF(val)); - } - } - - { - int num_globs = LOAD_INT(fp); - string pat; - for (int i = 0; i < num_globs; i++) { - COLLECT_STATS("glob time (regen)"); - LOAD_STRING(fp, &pat); -#if 0 - bool needs_reglob = false; - int num_dirs = LOAD_INT(fp); - for (int j = 0; j < num_dirs; j++) { - LOAD_STRING(fp, &s); - // TODO: Handle removed files properly. - needs_reglob |= gen_time < GetTimestamp(s); - } -#endif - int num_files = LOAD_INT(fp); - vector<string>* files; - Glob(pat.c_str(), &files); - sort(files->begin(), files->end()); - bool needs_regen = files->size() != static_cast<size_t>(num_files); - for (int j = 0; j < num_files; j++) { - LOAD_STRING(fp, &s); - if (!needs_regen) { - if ((*files)[j] != s) { - needs_regen = true; - break; - } - } - } - if (needs_regen) { - if (ShouldIgnoreDirty(pat)) { - if (g_flags.dump_kati_stamp) { - printf("wildcard %s: ignored\n", pat.c_str()); - } - continue; - } - if (g_flags.dump_kati_stamp) { - printf("wildcard %s: dirty\n", pat.c_str()); - } else { - fprintf(stderr, "wildcard(%s) was changed, regenerating...\n", - pat.c_str()); - } - RETURN_TRUE; - } else if (g_flags.dump_kati_stamp) { - printf("wildcard %s: clean\n", pat.c_str()); - } - } - } - - int num_crs = LOAD_INT(fp); - for (int i = 0; i < num_crs; i++) { - string cmd, expected; - LOAD_STRING(fp, &cmd); - LOAD_STRING(fp, &expected); - - { - COLLECT_STATS("stat time (regen)"); - bool has_condition = LOAD_INT(fp); - if (has_condition) { - bool should_run_command = false; - - int num_missing_dirs = LOAD_INT(fp); - for (int j = 0; j < num_missing_dirs; j++) { - LOAD_STRING(fp, &s); - should_run_command |= Exists(s); - } - - int num_read_dirs = LOAD_INT(fp); - for (int j = 0; j < num_read_dirs; j++) { - LOAD_STRING(fp, &s); - // We assume we rarely do a significant change for the top - // directory which affects the results of find command. - if (s == "" || s == "." || ShouldIgnoreDirty(s)) - continue; - - struct stat st; - if (lstat(s.c_str(), &st) != 0) { - should_run_command = true; - continue; - } - double ts = GetTimestampFromStat(st); - if (gen_time < ts) { - should_run_command = true; - continue; - } - if (S_ISLNK(st.st_mode)) { - ts = GetTimestamp(s); - should_run_command |= (ts < 0 || gen_time < ts); - } - } - - if (!should_run_command) { - if (g_flags.dump_kati_stamp) - printf("shell %s: clean (no rerun)\n", cmd.c_str()); - continue; - } - } - } - - FindCommand fc; - if (fc.Parse(cmd) && !fc.chdir.empty() && ShouldIgnoreDirty(fc.chdir)) { - if (g_flags.dump_kati_stamp) - printf("shell %s: ignored\n", cmd.c_str()); - continue; - } - - { - COLLECT_STATS_WITH_SLOW_REPORT("shell time (regen)", cmd.c_str()); - string result; - RunCommand("/bin/sh", cmd, RedirectStderr::DEV_NULL, &result); - FormatForCommandSubstitution(&result); - if (expected != result) { - if (g_flags.dump_kati_stamp) { - printf("shell %s: dirty\n", cmd.c_str()); - } else { - fprintf(stderr, "$(shell %s) was changed, regenerating...\n", - cmd.c_str()); -#if 0 - fprintf(stderr, "%s => %s\n", - expected.c_str(), result.c_str()); -#endif - } - RETURN_TRUE; - } else if (g_flags.dump_kati_stamp) { - printf("shell %s: clean (rerun)\n", cmd.c_str()); - } - } - } - - LoadString(fp, &s); - if (orig_args != s) { - fprintf(stderr, "arguments changed, regenerating...\n"); - RETURN_TRUE; - } - - if (!retval) { - if (fseek(fp, 0, SEEK_SET) < 0) - PERROR("fseek"); - size_t r = fwrite(&start_time, sizeof(start_time), 1, fp); - CHECK(r == 1); - } - - return retval; -} @@ -32,7 +32,9 @@ void GenerateNinja(const vector<DepNode*>& nodes, const string& orig_args, double start_time); -bool NeedsRegen(double start_time, const string& orig_args); +string GetNinjaFilename(); +string GetNinjaShellScriptFilename(); +string GetNinjaStampFilename(); // Exposed only for test. bool GetDepfileFromCommand(string* cmd, string* out); diff --git a/regen.cc b/regen.cc new file mode 100644 index 0000000..2c7cc73 --- /dev/null +++ b/regen.cc @@ -0,0 +1,297 @@ +// Copyright 2016 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. + +#include "regen.h" + +#include <sys/stat.h> + +#include <algorithm> + +#include "fileutil.h" +#include "find.h" +#include "io.h" +#include "log.h" +#include "ninja.h" +#include "stats.h" +#include "strutil.h" + +static bool ShouldIgnoreDirty(StringPiece s) { + Pattern pat(g_flags.ignore_dirty_pattern); + Pattern nopat(g_flags.no_ignore_dirty_pattern); + return pat.Match(s) && !nopat.Match(s); +} + +bool NeedsRegen(double start_time, const string& orig_args) { + bool retval = false; +#define RETURN_TRUE do { \ + if (g_flags.dump_kati_stamp) \ + retval = true; \ + else \ + return true; \ + } while (0) + +#define LOAD_INT(fp) ({ \ + int v = LoadInt(fp); \ + if (v < 0) { \ + fprintf(stderr, "incomplete kati_stamp, regenerating...\n"); \ + RETURN_TRUE; \ + } \ + v; \ + }) + +#define LOAD_STRING(fp, s) ({ \ + if (!LoadString(fp, s)) { \ + fprintf(stderr, "incomplete kati_stamp, regenerating...\n"); \ + RETURN_TRUE; \ + } \ + }) + + if (!Exists(GetNinjaFilename())) { + fprintf(stderr, "%s is missing, regenerating...\n", + GetNinjaFilename().c_str()); + return true; + } + if (!Exists(GetNinjaShellScriptFilename())) { + fprintf(stderr, "%s is missing, regenerating...\n", + GetNinjaShellScriptFilename().c_str()); + return true; + } + + const string& stamp_filename = GetNinjaStampFilename(); + FILE* fp = fopen(stamp_filename.c_str(), "rb+"); + if (!fp) { + if (g_flags.dump_kati_stamp) + printf("%s: %s\n", stamp_filename.c_str(), strerror(errno)); + return true; + } + ScopedFile sfp(fp); + + double gen_time; + size_t r = fread(&gen_time, sizeof(gen_time), 1, fp); + if (r != 1) { + fprintf(stderr, "incomplete kati_stamp, regenerating...\n"); + RETURN_TRUE; + } + if (g_flags.dump_kati_stamp) + printf("Generated time: %f\n", gen_time); + + string s, s2; + int num_files = LOAD_INT(fp); + for (int i = 0; i < num_files; i++) { + LOAD_STRING(fp, &s); + double ts = GetTimestamp(s); + if (gen_time < ts) { + if (g_flags.regen_ignoring_kati_binary) { + string kati_binary; + GetExecutablePath(&kati_binary); + if (s == kati_binary) { + fprintf(stderr, "%s was modified, ignored.\n", s.c_str()); + continue; + } + } + if (ShouldIgnoreDirty(s)) { + if (g_flags.dump_kati_stamp) + printf("file %s: ignored (%f)\n", s.c_str(), ts); + continue; + } + if (g_flags.dump_kati_stamp) + printf("file %s: dirty (%f)\n", s.c_str(), ts); + else + fprintf(stderr, "%s was modified, regenerating...\n", s.c_str()); + RETURN_TRUE; + } else if (g_flags.dump_kati_stamp) { + printf("file %s: clean (%f)\n", s.c_str(), ts); + } + } + + int num_undefineds = LOAD_INT(fp); + for (int i = 0; i < num_undefineds; i++) { + LOAD_STRING(fp, &s); + if (getenv(s.c_str())) { + if (g_flags.dump_kati_stamp) { + printf("env %s: dirty (unset => %s)\n", s.c_str(), getenv(s.c_str())); + } else { + fprintf(stderr, "Environment variable %s was set, regenerating...\n", + s.c_str()); + } + RETURN_TRUE; + } else if (g_flags.dump_kati_stamp) { + printf("env %s: clean (unset)\n", s.c_str()); + } + } + + int num_envs = LOAD_INT(fp); + for (int i = 0; i < num_envs; i++) { + LOAD_STRING(fp, &s); + StringPiece val(getenv(s.c_str())); + LOAD_STRING(fp, &s2); + if (val != s2) { + if (g_flags.dump_kati_stamp) { + printf("env %s: dirty (%s => %.*s)\n", + s.c_str(), s2.c_str(), SPF(val)); + } else { + fprintf(stderr, "Environment variable %s was modified (%s => %.*s), " + "regenerating...\n", + s.c_str(), s2.c_str(), SPF(val)); + } + RETURN_TRUE; + } else if (g_flags.dump_kati_stamp) { + printf("env %s: clean (%.*s)\n", s.c_str(), SPF(val)); + } + } + + { + int num_globs = LOAD_INT(fp); + string pat; + for (int i = 0; i < num_globs; i++) { + COLLECT_STATS("glob time (regen)"); + LOAD_STRING(fp, &pat); +#if 0 + bool needs_reglob = false; + int num_dirs = LOAD_INT(fp); + for (int j = 0; j < num_dirs; j++) { + LOAD_STRING(fp, &s); + // TODO: Handle removed files properly. + needs_reglob |= gen_time < GetTimestamp(s); + } +#endif + int num_files = LOAD_INT(fp); + vector<string>* files; + Glob(pat.c_str(), &files); + sort(files->begin(), files->end()); + bool needs_regen = files->size() != static_cast<size_t>(num_files); + for (int j = 0; j < num_files; j++) { + LOAD_STRING(fp, &s); + if (!needs_regen) { + if ((*files)[j] != s) { + needs_regen = true; + break; + } + } + } + if (needs_regen) { + if (ShouldIgnoreDirty(pat)) { + if (g_flags.dump_kati_stamp) { + printf("wildcard %s: ignored\n", pat.c_str()); + } + continue; + } + if (g_flags.dump_kati_stamp) { + printf("wildcard %s: dirty\n", pat.c_str()); + } else { + fprintf(stderr, "wildcard(%s) was changed, regenerating...\n", + pat.c_str()); + } + RETURN_TRUE; + } else if (g_flags.dump_kati_stamp) { + printf("wildcard %s: clean\n", pat.c_str()); + } + } + } + + int num_crs = LOAD_INT(fp); + for (int i = 0; i < num_crs; i++) { + string cmd, expected; + LOAD_STRING(fp, &cmd); + LOAD_STRING(fp, &expected); + + { + COLLECT_STATS("stat time (regen)"); + bool has_condition = LOAD_INT(fp); + if (has_condition) { + bool should_run_command = false; + + int num_missing_dirs = LOAD_INT(fp); + for (int j = 0; j < num_missing_dirs; j++) { + LOAD_STRING(fp, &s); + should_run_command |= Exists(s); + } + + int num_read_dirs = LOAD_INT(fp); + for (int j = 0; j < num_read_dirs; j++) { + LOAD_STRING(fp, &s); + // We assume we rarely do a significant change for the top + // directory which affects the results of find command. + if (s == "" || s == "." || ShouldIgnoreDirty(s)) + continue; + + struct stat st; + if (lstat(s.c_str(), &st) != 0) { + should_run_command = true; + continue; + } + double ts = GetTimestampFromStat(st); + if (gen_time < ts) { + should_run_command = true; + continue; + } + if (S_ISLNK(st.st_mode)) { + ts = GetTimestamp(s); + should_run_command |= (ts < 0 || gen_time < ts); + } + } + + if (!should_run_command) { + if (g_flags.dump_kati_stamp) + printf("shell %s: clean (no rerun)\n", cmd.c_str()); + continue; + } + } + } + + FindCommand fc; + if (fc.Parse(cmd) && !fc.chdir.empty() && ShouldIgnoreDirty(fc.chdir)) { + if (g_flags.dump_kati_stamp) + printf("shell %s: ignored\n", cmd.c_str()); + continue; + } + + { + COLLECT_STATS_WITH_SLOW_REPORT("shell time (regen)", cmd.c_str()); + string result; + RunCommand("/bin/sh", cmd, RedirectStderr::DEV_NULL, &result); + FormatForCommandSubstitution(&result); + if (expected != result) { + if (g_flags.dump_kati_stamp) { + printf("shell %s: dirty\n", cmd.c_str()); + } else { + fprintf(stderr, "$(shell %s) was changed, regenerating...\n", + cmd.c_str()); +#if 0 + fprintf(stderr, "%s => %s\n", + expected.c_str(), result.c_str()); +#endif + } + RETURN_TRUE; + } else if (g_flags.dump_kati_stamp) { + printf("shell %s: clean (rerun)\n", cmd.c_str()); + } + } + } + + LoadString(fp, &s); + if (orig_args != s) { + fprintf(stderr, "arguments changed, regenerating...\n"); + RETURN_TRUE; + } + + if (!retval) { + if (fseek(fp, 0, SEEK_SET) < 0) + PERROR("fseek"); + size_t r = fwrite(&start_time, sizeof(start_time), 1, fp); + CHECK(r == 1); + } + + return retval; +} @@ -0,0 +1,24 @@ +// Copyright 2016 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. + +#ifndef REGEN_H_ +#define REGEN_H_ + +#include <string> + +using namespace std; + +bool NeedsRegen(double start_time, const string& orig_args); + +#endif // REGEN_H_ |