From 5fdb12ed5cec4e1c853c64026142d088ff5519e1 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Fri, 14 Sep 2012 13:14:16 +0900 Subject: Clean up getopt_long call. Remove now-unimplemented 'V' from getopt_long. Remove 'h', since it's included in the long options. Order switch cases in the same order as in the getopt_long argument. --- src/ninja.cc | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ninja.cc b/src/ninja.cc index ad56f1c..c9196f8 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -740,7 +740,7 @@ int NinjaMain(int argc, char** argv) { int opt; while (tool_name.empty() && - (opt = getopt_long(argc, argv, "d:f:hj:k:l:nt:vC:V", kLongOptions, + (opt = getopt_long(argc, argv, "d:f:j:k:l:nt:vC:", kLongOptions, NULL)) != -1) { switch (opt) { case 'd': @@ -753,14 +753,6 @@ int NinjaMain(int argc, char** argv) { case 'j': config.parallelism = atoi(optarg); break; - case 'l': { - char* end; - double value = strtod(optarg, &end); - if (end == optarg) - Fatal("-l parameter not numeric: did you mean -l 0.0?"); - config.max_load_average = value; - break; - } case 'k': { char* end; int value = strtol(optarg, &end, 10); @@ -773,15 +765,23 @@ int NinjaMain(int argc, char** argv) { config.failures_allowed = value > 0 ? value : INT_MAX; break; } + case 'l': { + char* end; + double value = strtod(optarg, &end); + if (end == optarg) + Fatal("-l parameter not numeric: did you mean -l 0.0?"); + config.max_load_average = value; + break; + } case 'n': config.dry_run = true; break; - case 'v': - config.verbosity = BuildConfig::VERBOSE; - break; case 't': tool_name = optarg; break; + case 'v': + config.verbosity = BuildConfig::VERBOSE; + break; case 'C': working_dir = optarg; break; -- cgit v0.12 From 48143c9b07eca30c99d47cadf3c0ae111b369f1b Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Fri, 14 Sep 2012 22:37:31 +0900 Subject: Change rate measurement code. For %o, remove a superfluous + 0.5: snprintf("%f") rounds already. Remove some unnecessary code. For %c, fix a TODO to add a sliding window and update after every completed edge. Else, with -j50 and several files that take 3s to compile each, this number would only update every 150s. Also give the number one decimal place so that this can measure steps slower than 1s. --- src/build.cc | 24 +++++++++--------------- src/build.h | 58 ++++++++++++++++++++++++++++++++++++++++------------------ src/metrics.h | 9 ++++----- 3 files changed, 53 insertions(+), 38 deletions(-) diff --git a/src/build.cc b/src/build.cc index e1aaad1..5a0c3b3 100644 --- a/src/build.cc +++ b/src/build.cc @@ -42,8 +42,7 @@ BuildStatus::BuildStatus(const BuildConfig& config) start_time_millis_(GetTimeMillis()), started_edges_(0), finished_edges_(0), total_edges_(0), have_blank_line_(true), progress_status_format_(NULL), - overall_rate_(), current_rate_(), - current_rate_average_count_(config.parallelism) { + overall_rate_(), current_rate_(config.parallelism) { #ifndef _WIN32 const char* term = getenv("TERM"); smart_terminal_ = isatty(1) && term && string(term) != "dumb"; @@ -136,7 +135,8 @@ void BuildStatus::BuildFinished() { printf("\n"); } -string BuildStatus::FormatProgressStatus(const char* progress_status_format) const { +string BuildStatus::FormatProgressStatus( + const char* progress_status_format) const { string out; char buf[32]; for (const char* s = progress_status_format; *s != '\0'; ++s) { @@ -177,30 +177,24 @@ string BuildStatus::FormatProgressStatus(const char* progress_status_format) con out += buf; break; - // Overall finished edges per second. + // Overall finished edges per second. case 'o': - overall_rate_.UpdateRate(finished_edges_, finished_edges_); + overall_rate_.UpdateRate(finished_edges_); overall_rate_.snprinfRate(buf, "%.1f"); out += buf; break; - // Current rate, average over the last '-j' jobs. + // Current rate, average over the last '-j' jobs. case 'c': - // TODO use sliding window? - if (finished_edges_ > current_rate_.last_update() && - finished_edges_ - current_rate_.last_update() == current_rate_average_count_) { - current_rate_.UpdateRate(current_rate_average_count_, finished_edges_); - current_rate_.Restart(); - } - current_rate_.snprinfRate(buf, "%.0f"); + current_rate_.UpdateRate(finished_edges_); + current_rate_.snprinfRate(buf, "%.1f"); out += buf; break; - default: { + default: Fatal("unknown placeholder '%%%c' in $NINJA_STATUS", *s); return ""; } - } } else { out.push_back(*s); } diff --git a/src/build.h b/src/build.h index 3e7a144..0902a4c 100644 --- a/src/build.h +++ b/src/build.h @@ -201,37 +201,59 @@ struct BuildStatus { const char* progress_status_format_; struct RateInfo { - RateInfo() : last_update_(0), rate_(-1) {} - - double rate() const { return rate_; } - int last_update() const { return last_update_; } - void Restart() { return stopwatch_.Restart(); } - - double UpdateRate(int edges, int update_hint) { - if (update_hint != last_update_) { - rate_ = edges / stopwatch_.Elapsed() + 0.5; - last_update_ = update_hint; - } - return rate_; + RateInfo() : rate_(-1) {} + + void Restart() { stopwatch_.Restart(); } + + void UpdateRate(int edges) { + if (edges && stopwatch_.Elapsed()) + rate_ = edges / stopwatch_.Elapsed(); + } + + template + void snprinfRate(T buf, const char* format) { + if (rate_ == -1) snprintf(buf, sizeof(buf), "?"); + else snprintf(buf, sizeof(buf), format, rate_); + } + + private: + Stopwatch stopwatch_; + double rate_; + }; + + struct SlidingRateInfo { + SlidingRateInfo(int n) : N(n), last_update_(-1), rate_(-1) {} + + void Restart() { stopwatch_.Restart(); } + + void UpdateRate(int update_hint) { + if (update_hint == last_update_) + return; + last_update_ = update_hint; + + if (times_.size() == N) + times_.pop(); + times_.push(stopwatch_.Elapsed()); + if (times_.back() != times_.front()) + rate_ = times_.size() / (times_.back() - times_.front()); } template void snprinfRate(T buf, const char* format) { - if (rate_ == -1) - snprintf(buf, sizeof(buf), "?"); - else - snprintf(buf, sizeof(buf), format, rate_); + if (rate_ == -1) snprintf(buf, sizeof(buf), "?"); + else snprintf(buf, sizeof(buf), format, rate_); } private: + const size_t N; + std::queue times_; Stopwatch stopwatch_; int last_update_; double rate_; }; mutable RateInfo overall_rate_; - mutable RateInfo current_rate_; - const int current_rate_average_count_; + mutable SlidingRateInfo current_rate_; #ifdef _WIN32 void* console_; diff --git a/src/metrics.h b/src/metrics.h index f5ac0de..a4ef9f7 100644 --- a/src/metrics.h +++ b/src/metrics.h @@ -59,19 +59,18 @@ private: }; /// Get the current time as relative to some epoch. -/// Epoch varies between platforms; only useful for measuring elapsed -/// time. +/// Epoch varies between platforms; only useful for measuring elapsed time. int64_t GetTimeMillis(); -/// A simple stopwatch which retruns the time -// in seconds since Restart() was called +/// A simple stopwatch which returns the time +/// in seconds since Restart() was called. class Stopwatch { public: Stopwatch() : started_(0) {} - /// Seconds since Restart() call + /// Seconds since Restart() call. double Elapsed() const { return 1e-6 * static_cast(Now() - started_); } void Restart() { started_ = Now(); } -- cgit v0.12 From 0adc69945cb9a0d6b7d150b409ddcc7194552b93 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 17 Sep 2012 15:46:55 -0700 Subject: don't emit duplicate headers for msvc helper --- src/msvc_helper-win32.cc | 2 +- src/msvc_helper.h | 4 ++-- src/msvc_helper_main-win32.cc | 2 +- src/msvc_helper_test.cc | 28 ++++++++++++++++++++++++++-- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/msvc_helper-win32.cc b/src/msvc_helper-win32.cc index 8e440fe..a9f34aa 100644 --- a/src/msvc_helper-win32.cc +++ b/src/msvc_helper-win32.cc @@ -125,7 +125,7 @@ int CLWrapper::Run(const string& command, string* extra_output) { if (!include.empty()) { include = IncludesNormalize::Normalize(include, NULL); if (!IsSystemInclude(include)) - includes_.push_back(include); + includes_.insert(include); } else if (FilterInputFilename(line)) { // Drop it. // TODO: if we support compiling multiple output files in a single diff --git a/src/msvc_helper.h b/src/msvc_helper.h index f623520..c68f631 100644 --- a/src/msvc_helper.h +++ b/src/msvc_helper.h @@ -13,7 +13,7 @@ // limitations under the License. #include -#include +#include using namespace std; /// Visual Studio's cl.exe requires some massaging to work with Ninja; @@ -50,5 +50,5 @@ struct CLWrapper { static bool FilterInputFilename(const string& line); void* env_block_; - vector includes_; + set includes_; }; diff --git a/src/msvc_helper_main-win32.cc b/src/msvc_helper_main-win32.cc index 0c8db37..ed7674c 100644 --- a/src/msvc_helper_main-win32.cc +++ b/src/msvc_helper_main-win32.cc @@ -105,7 +105,7 @@ int MSVCHelperMain(int argc, char** argv) { Fatal("opening %s: %s", depfile.c_str(), GetLastErrorString().c_str()); } fprintf(output, "%s: ", output_filename); - for (vector::iterator i = cl.includes_.begin(); + for (set::iterator i = cl.includes_.begin(); i != cl.includes_.end(); ++i) { fprintf(output, "%s\n", i->c_str()); } diff --git a/src/msvc_helper_test.cc b/src/msvc_helper_test.cc index 29fefd4..85ac039 100644 --- a/src/msvc_helper_test.cc +++ b/src/msvc_helper_test.cc @@ -48,7 +48,7 @@ TEST(MSVCHelperTest, Run) { &output); ASSERT_EQ("foo\nbar\n", output); ASSERT_EQ(1u, cl.includes_.size()); - ASSERT_EQ("foo.h", cl.includes_[0]); + ASSERT_EQ("foo.h", *cl.includes_.begin()); } TEST(MSVCHelperTest, RunFilenameFilter) { @@ -70,7 +70,7 @@ TEST(MSVCHelperTest, RunSystemInclude) { // system headers. ASSERT_EQ("", output); ASSERT_EQ(1u, cl.includes_.size()); - ASSERT_EQ("path.h", cl.includes_[0]); + ASSERT_EQ("path.h", *cl.includes_.begin()); } TEST(MSVCHelperTest, EnvBlock) { @@ -81,3 +81,27 @@ TEST(MSVCHelperTest, EnvBlock) { cl.Run("cmd /c \"echo foo is %foo%", &output); ASSERT_EQ("foo is bar\n", output); } + +TEST(MSVCHelperTest, DuplicatedHeader) { + CLWrapper cl; + string output; + cl.Run("cmd /c \"echo Note: including file: foo.h&&" + "echo Note: including file: bar.h&&" + "echo Note: including file: foo.h\"", + &output); + // We should have dropped one copy of foo.h. + ASSERT_EQ("", output); + ASSERT_EQ(2u, cl.includes_.size()); +} + +TEST(MSVCHelperTest, DuplicatedHeaderPathConverted) { + CLWrapper cl; + string output; + cl.Run("cmd /c \"echo Note: including file: sub/foo.h&&" + "echo Note: including file: bar.h&&" + "echo Note: including file: sub\\foo.h\"", + &output); + // We should have dropped one copy of foo.h. + ASSERT_EQ("", output); + ASSERT_EQ(2u, cl.includes_.size()); +} -- cgit v0.12 From de6ec48d80879ce9279b3e13c24787e9054d942f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20K=C3=BCmmel?= Date: Tue, 18 Sep 2012 09:10:47 +0200 Subject: remove some code duplication --- src/build.cc | 5 +++-- src/build.h | 28 ++++++++++++---------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/build.cc b/src/build.cc index 5a0c3b3..7310c3d 100644 --- a/src/build.cc +++ b/src/build.cc @@ -18,6 +18,7 @@ #include #include + #ifdef _WIN32 #include #else @@ -180,14 +181,14 @@ string BuildStatus::FormatProgressStatus( // Overall finished edges per second. case 'o': overall_rate_.UpdateRate(finished_edges_); - overall_rate_.snprinfRate(buf, "%.1f"); + snprinfRate(overall_rate_.rate(), buf, "%.1f"); out += buf; break; // Current rate, average over the last '-j' jobs. case 'c': current_rate_.UpdateRate(finished_edges_); - current_rate_.snprinfRate(buf, "%.1f"); + snprinfRate(current_rate_.rate(), buf, "%.1f"); out += buf; break; diff --git a/src/build.h b/src/build.h index 0902a4c..c94c8cc 100644 --- a/src/build.h +++ b/src/build.h @@ -200,31 +200,33 @@ struct BuildStatus { /// The custom progress status format to use. const char* progress_status_format_; + template + void snprinfRate(double rate, T buf, const char* format) const { + if (rate == -1) snprintf(buf, sizeof(buf), "?"); + else snprintf(buf, sizeof(buf), format, rate); + } + struct RateInfo { RateInfo() : rate_(-1) {} void Restart() { stopwatch_.Restart(); } + double rate() { return rate_; } void UpdateRate(int edges) { if (edges && stopwatch_.Elapsed()) rate_ = edges / stopwatch_.Elapsed(); } - template - void snprinfRate(T buf, const char* format) { - if (rate_ == -1) snprintf(buf, sizeof(buf), "?"); - else snprintf(buf, sizeof(buf), format, rate_); - } - private: - Stopwatch stopwatch_; double rate_; + Stopwatch stopwatch_; }; struct SlidingRateInfo { - SlidingRateInfo(int n) : N(n), last_update_(-1), rate_(-1) {} + SlidingRateInfo(int n) : rate_(-1), N(n), last_update_(-1) {} void Restart() { stopwatch_.Restart(); } + double rate() { return rate_; } void UpdateRate(int update_hint) { if (update_hint == last_update_) @@ -238,18 +240,12 @@ struct BuildStatus { rate_ = times_.size() / (times_.back() - times_.front()); } - template - void snprinfRate(T buf, const char* format) { - if (rate_ == -1) snprintf(buf, sizeof(buf), "?"); - else snprintf(buf, sizeof(buf), format, rate_); - } - private: + double rate_; + Stopwatch stopwatch_; const size_t N; std::queue times_; - Stopwatch stopwatch_; int last_update_; - double rate_; }; mutable RateInfo overall_rate_; -- cgit v0.12 From 6df040356f4e765e51f24857c7ef0f3ded4e6870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20K=C3=BCmmel?= Date: Tue, 18 Sep 2012 09:11:40 +0200 Subject: build with msvc2012 --- src/build.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/build.cc b/src/build.cc index 7310c3d..a2f2720 100644 --- a/src/build.cc +++ b/src/build.cc @@ -17,7 +17,7 @@ #include #include #include - +#include #ifdef _WIN32 #include -- cgit v0.12 From fd5260f3cc67b284558d0cfcb89cb3b157c88e1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20K=C3=BCmmel?= Date: Tue, 18 Sep 2012 09:14:39 +0200 Subject: Fix subtile buffer size error The deduced type was char* with size 4 and not char[32] with size 32. This removes strange output characters on Windows. --- src/build.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/build.h b/src/build.h index c94c8cc..8876d88 100644 --- a/src/build.h +++ b/src/build.h @@ -200,10 +200,10 @@ struct BuildStatus { /// The custom progress status format to use. const char* progress_status_format_; - template - void snprinfRate(double rate, T buf, const char* format) const { - if (rate == -1) snprintf(buf, sizeof(buf), "?"); - else snprintf(buf, sizeof(buf), format, rate); + template + void snprinfRate(double rate, char(&buf)[S], const char* format) const { + if (rate == -1) snprintf(buf, S, "?"); + else snprintf(buf, S, format, rate); } struct RateInfo { -- cgit v0.12 From 21046b4a3e153754ab8f632c0b65ec1c61f7b828 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 19 Sep 2012 17:15:55 -0700 Subject: fix spaces in headers for -t msvc --- src/msvc_helper-win32.cc | 24 ++++++++++++++++++++++++ src/msvc_helper.h | 5 +++++ src/msvc_helper_main-win32.cc | 4 ++-- src/msvc_helper_test.cc | 11 +++++++++++ 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/msvc_helper-win32.cc b/src/msvc_helper-win32.cc index a9f34aa..e2a121d 100644 --- a/src/msvc_helper-win32.cc +++ b/src/msvc_helper-win32.cc @@ -28,6 +28,22 @@ bool EndsWith(const string& input, const string& needle) { input.substr(input.size() - needle.size()) == needle); } +void Replace(string& in_out, const string& find, const string& replace) { + size_t start_pos = 0; + while ((start_pos = in_out.find(find, start_pos)) != std::string::npos) { + in_out.replace(start_pos, find.length(), replace); + start_pos += replace.length(); + } +} + +string Escape(const string& path) { + string result = path; + // TODO: very strange format, should we escape \ too? + //Replace(result, "\\", "\\\\"); + Replace(result, " ", "\\ "); + return result; +} + } // anonymous namespace // static @@ -162,3 +178,11 @@ int CLWrapper::Run(const string& command, string* extra_output) { return exit_code; } + +vector CLWrapper::GetEscapedResult() { + vector result; + for (set::iterator i = includes_.begin(); i != includes_.end(); ++i) { + result.push_back(Escape(*i)); + } + return result; +} diff --git a/src/msvc_helper.h b/src/msvc_helper.h index c68f631..102201b 100644 --- a/src/msvc_helper.h +++ b/src/msvc_helper.h @@ -14,6 +14,7 @@ #include #include +#include using namespace std; /// Visual Studio's cl.exe requires some massaging to work with Ninja; @@ -49,6 +50,10 @@ struct CLWrapper { /// Exposed for testing. static bool FilterInputFilename(const string& line); + /// Fill a vector with the unique'd headers, escaped for output as a .d + /// file. + vector GetEscapedResult(); + void* env_block_; set includes_; }; diff --git a/src/msvc_helper_main-win32.cc b/src/msvc_helper_main-win32.cc index ed7674c..5e28e6d 100644 --- a/src/msvc_helper_main-win32.cc +++ b/src/msvc_helper_main-win32.cc @@ -105,8 +105,8 @@ int MSVCHelperMain(int argc, char** argv) { Fatal("opening %s: %s", depfile.c_str(), GetLastErrorString().c_str()); } fprintf(output, "%s: ", output_filename); - for (set::iterator i = cl.includes_.begin(); - i != cl.includes_.end(); ++i) { + vector headers = cl.GetEscapedResult(); + for (vector::iterator i = headers.begin(); i != headers.end(); ++i) { fprintf(output, "%s\n", i->c_str()); } fclose(output); diff --git a/src/msvc_helper_test.cc b/src/msvc_helper_test.cc index 85ac039..7730425 100644 --- a/src/msvc_helper_test.cc +++ b/src/msvc_helper_test.cc @@ -105,3 +105,14 @@ TEST(MSVCHelperTest, DuplicatedHeaderPathConverted) { ASSERT_EQ("", output); ASSERT_EQ(2u, cl.includes_.size()); } + +TEST(MSVCHelperTest, SpacesInFilename) { + CLWrapper cl; + string output; + cl.Run("cmd /c \"echo Note: including file: sub\\some sdk\\foo.h", + &output); + ASSERT_EQ("", output); + vector headers = cl.GetEscapedResult(); + ASSERT_EQ(1u, headers.size()); + ASSERT_EQ("sub\\some\\ sdk\\foo.h", headers[0]); +} -- cgit v0.12 From 3b3e1c831cc49d8c02928aaad7683c820cce9400 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 20 Sep 2012 10:05:11 -0400 Subject: Give MinGW builds MSVC build helper superpowers Note: _WIN32 is used instead of WIN32 to enable builds with MSVC IDE, Windows SDK non-IDE command line tools, and mingw/mingw-w64 based toolchains --- configure.py | 11 ++++++----- src/msvc_helper-win32.cc | 4 ++++ src/msvc_helper_main-win32.cc | 4 ++++ src/ninja.cc | 4 ++-- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/configure.py b/configure.py index 98274e6..e458605 100755 --- a/configure.py +++ b/configure.py @@ -277,11 +277,12 @@ for name in ['build', 'util']: objs += cxx(name) if platform in ('mingw', 'windows'): - objs += cxx('subprocess-win32') + for name in ['subprocess-win32', + 'includes_normalize-win32', + 'msvc_helper-win32', + 'msvc_helper_main-win32']: + objs += cxx(name) if platform == 'windows': - objs += cxx('includes_normalize-win32') - objs += cxx('msvc_helper-win32') - objs += cxx('msvc_helper_main-win32') objs += cxx('minidump-win32') objs += cc('getopt') else: @@ -349,7 +350,7 @@ for name in ['build_log_test', 'test', 'util_test']: objs += cxx(name, variables=[('cflags', test_cflags)]) -if platform == 'windows': +if platform in ('windows', 'mingw'): for name in ['includes_normalize_test', 'msvc_helper_test']: objs += cxx(name, variables=[('cflags', test_cflags)]) diff --git a/src/msvc_helper-win32.cc b/src/msvc_helper-win32.cc index a9f34aa..cb2010f 100644 --- a/src/msvc_helper-win32.cc +++ b/src/msvc_helper-win32.cc @@ -17,6 +17,10 @@ #include #include +#ifdef __MINGW32__ +#include +#endif + #include "includes_normalize.h" #include "util.h" diff --git a/src/msvc_helper_main-win32.cc b/src/msvc_helper_main-win32.cc index ed7674c..4ff14a0 100644 --- a/src/msvc_helper_main-win32.cc +++ b/src/msvc_helper_main-win32.cc @@ -16,6 +16,10 @@ #include +#ifdef __MINGW32__ +#include +#endif + #include "util.h" #include "getopt.h" diff --git a/src/ninja.cc b/src/ninja.cc index c9196f8..6046d72 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -292,7 +292,7 @@ int ToolBrowse(Globals* globals, int argc, char* argv[]) { } #endif // _WIN32 -#if defined(WIN32) +#if defined(_WIN32) int ToolMSVC(Globals* globals, int argc, char* argv[]) { // Reset getopt: push one argument onto the front of argv, reset optind. argc++; @@ -537,7 +537,7 @@ int ChooseTool(const string& tool_name, const Tool** tool_out) { { "browse", "browse dependency graph in a web browser", Tool::RUN_AFTER_LOAD, ToolBrowse }, #endif -#if defined(WIN32) +#if defined(_WIN32) { "msvc", "build helper for MSVC cl.exe (EXPERIMENTAL)", Tool::RUN_AFTER_FLAGS, ToolMSVC }, #endif -- cgit v0.12 From 7c3b8455ef0243f9c00c3d82d2ce74d79909365e Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 20 Sep 2012 10:08:30 -0400 Subject: Silence bothersome warning from -Wextra Struct initializations such as those in `CLWrapper::Run` of file `src/msvc_helper-win32.cc` causes MinGW GCC to spew warnings. --- configure.py | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.py b/configure.py index e458605..8d9f31e 100755 --- a/configure.py +++ b/configure.py @@ -140,6 +140,7 @@ else: '-fno-rtti', '-fno-exceptions', '-fvisibility=hidden', '-pipe', + '-Wno-missing-field-initializers', '-DNINJA_PYTHON="%s"' % options.with_python] if options.debug: cflags += ['-D_GLIBCXX_DEBUG', '-D_GLIBCXX_DEBUG_PEDANTIC'] -- cgit v0.12 From 32f8ed1178089a4c3b88b95c717e0399d9da957b Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 20 Sep 2012 09:36:37 -0700 Subject: review fixes --- src/msvc_helper-win32.cc | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/msvc_helper-win32.cc b/src/msvc_helper-win32.cc index e2a121d..0eb807a 100644 --- a/src/msvc_helper-win32.cc +++ b/src/msvc_helper-win32.cc @@ -28,20 +28,19 @@ bool EndsWith(const string& input, const string& needle) { input.substr(input.size() - needle.size()) == needle); } -void Replace(string& in_out, const string& find, const string& replace) { +string Replace(const string& input, const string& find, const string& replace) { + string result = input; size_t start_pos = 0; - while ((start_pos = in_out.find(find, start_pos)) != std::string::npos) { - in_out.replace(start_pos, find.length(), replace); + while ((start_pos = result.find(find, start_pos)) != string::npos) { + result.replace(start_pos, find.length(), replace); start_pos += replace.length(); } + return result; } -string Escape(const string& path) { - string result = path; - // TODO: very strange format, should we escape \ too? - //Replace(result, "\\", "\\\\"); - Replace(result, " ", "\\ "); - return result; +string EscapeForDepfile(const string& path) { + // Depfiles don't escape single \ because they're common in paths. + return Replace(path, " ", "\\ "); } } // anonymous namespace @@ -182,7 +181,7 @@ int CLWrapper::Run(const string& command, string* extra_output) { vector CLWrapper::GetEscapedResult() { vector result; for (set::iterator i = includes_.begin(); i != includes_.end(); ++i) { - result.push_back(Escape(*i)); + result.push_back(EscapeForDepfile(*i)); } return result; } -- cgit v0.12 From 490934575c475b8e6f6453136cacb0bac633ccfb Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 20 Sep 2012 10:01:25 -0700 Subject: less random comment --- src/msvc_helper-win32.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/msvc_helper-win32.cc b/src/msvc_helper-win32.cc index 0eb807a..ee260ab 100644 --- a/src/msvc_helper-win32.cc +++ b/src/msvc_helper-win32.cc @@ -39,7 +39,7 @@ string Replace(const string& input, const string& find, const string& replace) { } string EscapeForDepfile(const string& path) { - // Depfiles don't escape single \ because they're common in paths. + // Depfiles don't escape single \. return Replace(path, " ", "\\ "); } -- cgit v0.12 From 39b2d7fc6c9bb2a06b60a65b546b4c7c89c02e46 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 20 Sep 2012 13:46:41 -0400 Subject: Always include stdio.h --- src/msvc_helper-win32.cc | 5 +---- src/msvc_helper_main-win32.cc | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/msvc_helper-win32.cc b/src/msvc_helper-win32.cc index cb2010f..e5c47a1 100644 --- a/src/msvc_helper-win32.cc +++ b/src/msvc_helper-win32.cc @@ -14,13 +14,10 @@ #include "msvc_helper.h" +#include #include #include -#ifdef __MINGW32__ -#include -#endif - #include "includes_normalize.h" #include "util.h" diff --git a/src/msvc_helper_main-win32.cc b/src/msvc_helper_main-win32.cc index 4ff14a0..78b7f4b 100644 --- a/src/msvc_helper_main-win32.cc +++ b/src/msvc_helper_main-win32.cc @@ -14,11 +14,8 @@ #include "msvc_helper.h" -#include - -#ifdef __MINGW32__ #include -#endif +#include #include "util.h" -- cgit v0.12 From 1cdfae5a8824cbe7cb583599c92e9d385d5501e4 Mon Sep 17 00:00:00 2001 From: Maxim Kalaev Date: Fri, 14 Sep 2012 13:46:01 +0300 Subject: packaging: refactored rpm building Now passing through a valid SRPM, working in a standard way. --- configure.py | 18 +++--------------- misc/packaging/ninja.spec | 21 ++++++++++++++------- misc/packaging/rpmbuild.sh | 29 +++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 22 deletions(-) create mode 100755 misc/packaging/rpmbuild.sh diff --git a/configure.py b/configure.py index 8d9f31e..f8e82f0 100755 --- a/configure.py +++ b/configure.py @@ -429,21 +429,9 @@ n.newline() if host == 'linux': n.comment('Packaging') n.rule('rpmbuild', - command="rpmbuild \ - --define 'ver git' \ - --define \"rel `git rev-parse --short HEAD`\" \ - --define '_topdir %(pwd)/rpm-build' \ - --define '_builddir %{_topdir}' \ - --define '_rpmdir %{_topdir}' \ - --define '_srcrpmdir %{_topdir}' \ - --define '_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm' \ - --define '_specdir %{_topdir}' \ - --define '_sourcedir %{_topdir}' \ - --quiet \ - -bb misc/packaging/ninja.spec", - description='Building RPM..') - n.build('rpm', 'rpmbuild', - implicit=['ninja','README', 'COPYING', doc('manual.html')]) + command="misc/packaging/rpmbuild.sh", + description='Building rpms..') + n.build('rpm', 'rpmbuild') n.newline() n.build('all', 'phony', all_targets) diff --git a/misc/packaging/ninja.spec b/misc/packaging/ninja.spec index d513c6d..2f009f6 100644 --- a/misc/packaging/ninja.spec +++ b/misc/packaging/ninja.spec @@ -5,6 +5,8 @@ Release: %{rel}%{?dist} Group: Development/Tools License: Apache 2.0 URL: https://github.com/martine/ninja +Source0: %{name}-%{version}-%{release}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release} %description Ninja is yet another build system. It takes as input the interdependencies of files (typically source code and output executables) and @@ -14,20 +16,25 @@ Ninja joins a sea of other build systems. Its distinguishing goal is to be fast. which has over 30,000 source files and whose other build systems (including one built from custom non-recursive Makefiles) can take ten seconds to start building after changing one file. Ninja is under a second. +%prep +%setup -q -n %{name}-%{version}-%{release} + %build -# Assuming we've bootstrapped already.. -../ninja manual ninja -C .. +echo Building.. +./bootstrap.py +./ninja manual %install mkdir -p %{buildroot}%{_bindir} %{buildroot}%{_docdir} -cp -p ../ninja %{buildroot}%{_bindir}/ -git log --oneline --pretty=format:'%h: %s (%an, %cd)' --abbrev-commit --all > GITLOG +cp -p ninja %{buildroot}%{_bindir}/ %files %defattr(-, root, root) -%doc GITLOG ../COPYING ../README ../doc/manual.html +%doc COPYING README doc/manual.html %{_bindir}/* %clean -mv %{_topdir}/*.rpm .. -rm -rf %{_topdir} +rm -rf %{buildroot} + +#The changelog is built automatically from Git history +%changelog diff --git a/misc/packaging/rpmbuild.sh b/misc/packaging/rpmbuild.sh new file mode 100755 index 0000000..9b74c65 --- /dev/null +++ b/misc/packaging/rpmbuild.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +echo Building ninja RPMs.. +GITROOT=$(git rev-parse --show-toplevel) +cd $GITROOT + +VER=1.0 +REL=$(git rev-parse --short HEAD)git +RPMTOPDIR=$GITROOT/rpm-build +echo "Ver: $VER, Release: $REL" + +# Create tarball +mkdir -p $RPMTOPDIR/{SOURCES,SPECS} +git archive --format=tar --prefix=ninja-${VER}-${REL}/ HEAD | gzip -c > $RPMTOPDIR/SOURCES/ninja-${VER}-${REL}.tar.gz + +# Convert git log to RPM's ChangeLog format (shown with rpm -qp --changelog ) +sed -e "s/%{ver}/$VER/" -e "s/%{rel}/$REL/" misc/packaging/ninja.spec > $RPMTOPDIR/SPECS/ninja.spec +git log --format="* %cd %aN%n- (%h) %s%d%n" --date=local | sed -r 's/[0-9]+:[0-9]+:[0-9]+ //' >> $RPMTOPDIR/SPECS/ninja.spec + +# Build SRC and binary RPMs +rpmbuild --quiet \ + --define "_topdir $RPMTOPDIR" \ + --define "_rpmdir $PWD" \ + --define "_srcrpmdir $PWD" \ + --define '_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm' \ + -ba $RPMTOPDIR/SPECS/ninja.spec && + +rm -rf $RPMTOPDIR && +echo Done -- cgit v0.12 From d7dcc4f38b137cc73791a5e4cce47cccd5527657 Mon Sep 17 00:00:00 2001 From: Nicolas Despres Date: Thu, 27 Sep 2012 20:49:22 +0200 Subject: Don't say -h is invalid whereas it is supported. getopt_long(3) was reporting "ninja: invalid option -- h" when "ninja -h" was called. Regression most probably introduced by 5fdb12ed5cec4e1c853c64026142d088ff5519e1 --- src/ninja.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ninja.cc b/src/ninja.cc index 6046d72..76b2764 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -740,7 +740,7 @@ int NinjaMain(int argc, char** argv) { int opt; while (tool_name.empty() && - (opt = getopt_long(argc, argv, "d:f:j:k:l:nt:vC:", kLongOptions, + (opt = getopt_long(argc, argv, "d:f:j:k:l:nt:vC:h", kLongOptions, NULL)) != -1) { switch (opt) { case 'd': -- cgit v0.12 From 5cd579de4e0aff88f23b580610214ae17ebda330 Mon Sep 17 00:00:00 2001 From: Thiago Farina Date: Mon, 1 Oct 2012 22:13:39 -0300 Subject: Move DryRunCommandRunner into a unnamed namespace. No functional change. Signed-off-by: Thiago Farina --- src/build.cc | 63 +++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/src/build.cc b/src/build.cc index a2f2720..9877db8 100644 --- a/src/build.cc +++ b/src/build.cc @@ -38,6 +38,45 @@ #include "subprocess.h" #include "util.h" +namespace { + +/// A CommandRunner that doesn't actually run the commands. +class DryRunCommandRunner : public CommandRunner { + public: + virtual ~DryRunCommandRunner() {} + + // Overridden from CommandRunner: + virtual bool CanRunMore(); + virtual bool StartCommand(Edge* edge); + virtual Edge* WaitForCommand(ExitStatus* status, string* /* output */); + + private: + queue finished_; +}; + +bool DryRunCommandRunner::CanRunMore() { + return true; +} + +bool DryRunCommandRunner::StartCommand(Edge* edge) { + finished_.push(edge); + return true; +} + +Edge* DryRunCommandRunner::WaitForCommand(ExitStatus* status, + string* /*output*/) { + if (finished_.empty()) { + *status = ExitFailure; + return NULL; + } + *status = ExitSuccess; + Edge* edge = finished_.front(); + finished_.pop(); + return edge; +} + +} // namespace + BuildStatus::BuildStatus(const BuildConfig& config) : config_(config), start_time_millis_(GetTimeMillis()), @@ -528,30 +567,6 @@ Edge* RealCommandRunner::WaitForCommand(ExitStatus* status, string* output) { return edge; } -/// A CommandRunner that doesn't actually run the commands. -struct DryRunCommandRunner : public CommandRunner { - virtual ~DryRunCommandRunner() {} - virtual bool CanRunMore() { - return true; - } - virtual bool StartCommand(Edge* edge) { - finished_.push(edge); - return true; - } - virtual Edge* WaitForCommand(ExitStatus* status, string* /* output */) { - if (finished_.empty()) { - *status = ExitFailure; - return NULL; - } - *status = ExitSuccess; - Edge* edge = finished_.front(); - finished_.pop(); - return edge; - } - - queue finished_; -}; - Builder::Builder(State* state, const BuildConfig& config, BuildLog* log, DiskInterface* disk_interface) : state_(state), config_(config), disk_interface_(disk_interface), -- cgit v0.12 From 39aac336925aa1e4452df9215545ba1b2326cd0c Mon Sep 17 00:00:00 2001 From: Martin Olsson Date: Wed, 3 Oct 2012 10:10:53 +0200 Subject: Fix two spelling errors --- configure.py | 2 +- src/build_test.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.py b/configure.py index f8e82f0..cb0d45e 100755 --- a/configure.py +++ b/configure.py @@ -364,7 +364,7 @@ n.newline() all_targets += ninja_test -n.comment('Ancilliary executables.') +n.comment('Ancillary executables.') objs = cxx('parser_perftest') all_targets += n.build(binary('parser_perftest'), 'link', objs, implicit=ninja_lib, variables=[('libs', libs)]) diff --git a/src/build_test.cc b/src/build_test.cc index 859e758..c208463 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -943,7 +943,7 @@ TEST_F(BuildDryRun, AllCommandsShown) { } // Test that RSP files are created when & where appropriate and deleted after -// succesful execution. +// successful execution. TEST_F(BuildTest, RspFileSuccess) { ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, -- cgit v0.12 From 931aac42522c7e4a340cc77bb115ba89d808c7a5 Mon Sep 17 00:00:00 2001 From: Petr Wolf Date: Thu, 4 Oct 2012 15:42:15 -0400 Subject: Improve the efficiency of -t clean Prevent each node from being examined for cleaning multiple times, if it is used in several other nodes --- src/clean.cc | 12 +++++++++++- src/clean.h | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/clean.cc b/src/clean.cc index 3fe23ec..c9d4cbd 100644 --- a/src/clean.cc +++ b/src/clean.cc @@ -29,6 +29,7 @@ Cleaner::Cleaner(State* state, const BuildConfig& config) : state_(state), config_(config), removed_(), + cleaned_(), cleaned_files_count_(0), disk_interface_(new RealDiskInterface), status_(0) { @@ -40,6 +41,7 @@ Cleaner::Cleaner(State* state, : state_(state), config_(config), removed_(), + cleaned_(), cleaned_files_count_(0), disk_interface_(disk_interface), status_(0) { @@ -134,9 +136,16 @@ void Cleaner::DoCleanTarget(Node* target) { } for (vector::iterator n = e->inputs_.begin(); n != e->inputs_.end(); ++n) { - DoCleanTarget(*n); + Node* next = *n; + // call DoCleanTarget recursively if this node has not been visited + if (cleaned_.count(next) == 0) { + DoCleanTarget(next); + } } } + + // mark this target to be cleaned already + cleaned_.insert(target); } int Cleaner::CleanTarget(Node* target) { @@ -249,4 +258,5 @@ void Cleaner::Reset() { status_ = 0; cleaned_files_count_ = 0; removed_.clear(); + cleaned_.clear(); } diff --git a/src/clean.h b/src/clean.h index 5938dff..8359901 100644 --- a/src/clean.h +++ b/src/clean.h @@ -95,6 +95,7 @@ class Cleaner { State* state_; const BuildConfig& config_; set removed_; + set cleaned_; int cleaned_files_count_; DiskInterface* disk_interface_; int status_; -- cgit v0.12 From a8fb6423dfcf9279817639502936b687d95ea8f8 Mon Sep 17 00:00:00 2001 From: Petr Wolf Date: Thu, 4 Oct 2012 16:07:25 -0400 Subject: Add metric for log recompacting --- src/build_log.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/build_log.cc b/src/build_log.cc index a633892..33cbfc4 100644 --- a/src/build_log.cc +++ b/src/build_log.cc @@ -341,6 +341,7 @@ void BuildLog::WriteEntry(FILE* f, const LogEntry& entry) { } bool BuildLog::Recompact(const string& path, string* err) { + METRIC_RECORD(".ninja_log recompact"); printf("Recompacting log...\n"); string temp_path = path + ".recompact"; -- cgit v0.12 From c82a8dc2df6468da90189581fde80282b77e5957 Mon Sep 17 00:00:00 2001 From: Thiago Farina Date: Wed, 10 Oct 2012 12:51:53 -0300 Subject: Allocate disk_interface near where it's needed. This avoids allocating disk_interface unnecessarily. Signed-off-by: Thiago Farina --- src/ninja.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ninja.cc b/src/ninja.cc index 76b2764..ce05521 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -825,7 +825,6 @@ int NinjaMain(int argc, char** argv) { bool rebuilt_manifest = false; reload: - RealDiskInterface disk_interface; RealFileReader file_reader; ManifestParser parser(globals.state, &file_reader); string err; @@ -838,6 +837,7 @@ reload: return tool->func(&globals, argc, argv); BuildLog build_log; + RealDiskInterface disk_interface; if (!OpenLog(&build_log, &globals, &disk_interface)) return 1; -- cgit v0.12 From 17399ca4e5c0de7e64036c46cae616c86317e51f Mon Sep 17 00:00:00 2001 From: Maxim Kalaev Date: Sat, 6 Oct 2012 23:16:21 +0200 Subject: build log: Adding test for multiple target edges --- src/build_log_test.cc | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/build_log_test.cc b/src/build_log_test.cc index a6c2a86..5275f25 100644 --- a/src/build_log_test.cc +++ b/src/build_log_test.cc @@ -245,3 +245,23 @@ TEST_F(BuildLogTest, VeryLongInputLine) { ASSERT_EQ(789, e->restat_mtime); ASSERT_NO_FATAL_FAILURE(AssertHash("command2", e->command_hash)); } + +TEST_F(BuildLogTest, MultiTargetEdge) { + AssertParse(&state_, +"build out out.d: cat\n"); + + BuildLog log; + log.RecordCommand(state_.edges_[0], 21, 22); + + ASSERT_EQ(2u, log.entries().size()); + BuildLog::LogEntry* e1 = log.LookupByOutput("out"); + ASSERT_TRUE(e1); + BuildLog::LogEntry* e2 = log.LookupByOutput("out.d"); + ASSERT_TRUE(e2); + ASSERT_EQ("out", e1->output); + ASSERT_EQ("out.d", e2->output); + ASSERT_EQ(21, e1->start_time); + ASSERT_EQ(21, e2->start_time); + ASSERT_EQ(22, e2->end_time); + ASSERT_EQ(22, e2->end_time); +} -- cgit v0.12 From 9e8bfe0365ccf78a524c0733a33b5bac2dc66f5a Mon Sep 17 00:00:00 2001 From: Maxim Kalaev Date: Fri, 12 Oct 2012 08:57:23 +0200 Subject: build log: moving HashCommand() calculation out of targets loop --- src/build_log.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/build_log.cc b/src/build_log.cc index 33cbfc4..19e1c14 100644 --- a/src/build_log.cc +++ b/src/build_log.cc @@ -130,6 +130,7 @@ bool BuildLog::OpenForWrite(const string& path, string* err) { void BuildLog::RecordCommand(Edge* edge, int start_time, int end_time, TimeStamp restat_mtime) { string command = edge->EvaluateCommand(true); + uint64_t command_hash = LogEntry::HashCommand(command); for (vector::iterator out = edge->outputs_.begin(); out != edge->outputs_.end(); ++out) { const string& path = (*out)->path(); @@ -142,7 +143,7 @@ void BuildLog::RecordCommand(Edge* edge, int start_time, int end_time, log_entry->output = path; entries_.insert(Entries::value_type(log_entry->output, log_entry)); } - log_entry->command_hash = LogEntry::HashCommand(command); + log_entry->command_hash = command_hash; log_entry->start_time = start_time; log_entry->end_time = end_time; log_entry->restat_mtime = restat_mtime; -- cgit v0.12 From 96e01852b3060f37f20784ee0941d13d8f592794 Mon Sep 17 00:00:00 2001 From: Maxim Kalaev Date: Fri, 12 Oct 2012 08:59:49 +0200 Subject: build log: mini-refactoring to use constructors to initialize entries --- src/build_log.cc | 15 +++++++++++---- src/build_log.h | 3 +++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/build_log.cc b/src/build_log.cc index 19e1c14..28fcf88 100644 --- a/src/build_log.cc +++ b/src/build_log.cc @@ -91,6 +91,15 @@ uint64_t BuildLog::LogEntry::HashCommand(StringPiece command) { return MurmurHash64A(command.str_, command.len_); } +BuildLog::LogEntry::LogEntry(const string& _output) + : output(_output) {} + +BuildLog::LogEntry::LogEntry(const string& _output, uint64_t _command_hash, + int _start_time, int _end_time, TimeStamp _restat_mtime) + : output(_output), command_hash(_command_hash), + start_time(_start_time), end_time(_end_time), restat_mtime(_restat_mtime) +{} + BuildLog::BuildLog() : log_file_(NULL), needs_recompaction_(false) {} @@ -139,8 +148,7 @@ void BuildLog::RecordCommand(Edge* edge, int start_time, int end_time, if (i != entries_.end()) { log_entry = i->second; } else { - log_entry = new LogEntry; - log_entry->output = path; + log_entry = new LogEntry(path); entries_.insert(Entries::value_type(log_entry->output, log_entry)); } log_entry->command_hash = command_hash; @@ -288,8 +296,7 @@ bool BuildLog::Load(const string& path, string* err) { if (i != entries_.end()) { entry = i->second; } else { - entry = new LogEntry; - entry->output = output; + entry = new LogEntry(output); entries_.insert(Entries::value_type(entry->output, entry)); ++unique_entry_count; } diff --git a/src/build_log.h b/src/build_log.h index 4141ff3..5a3b516 100644 --- a/src/build_log.h +++ b/src/build_log.h @@ -60,6 +60,9 @@ struct BuildLog { start_time == o.start_time && end_time == o.end_time && restat_mtime == o.restat_mtime; } + + explicit LogEntry(const string& output); + LogEntry(const string& output, uint64_t command_hash, int start_time, int end_time, TimeStamp restat_mtime); }; /// Lookup a previously-run command by its output path. -- cgit v0.12 From c4fc36cfd64fc7ee8b79efcef4d6106dca675b97 Mon Sep 17 00:00:00 2001 From: Maxim Kalaev Date: Fri, 12 Oct 2012 08:16:06 +0200 Subject: build metrics: StartEdge and EndEdge Total build time is also relevant. The costs of StartEdge and EndEdge turned out to be quite surprising. Especially if multiple targets are specified on edges. (I've tried declaring depfiles targets explicitly) --- src/build.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/build.cc b/src/build.cc index 9877db8..19775e7 100644 --- a/src/build.cc +++ b/src/build.cc @@ -728,6 +728,7 @@ bool Builder::Build(string* err) { } bool Builder::StartEdge(Edge* edge, string* err) { + METRIC_RECORD("StartEdge"); if (edge->is_phony()) return true; @@ -758,6 +759,7 @@ bool Builder::StartEdge(Edge* edge, string* err) { } void Builder::FinishEdge(Edge* edge, bool success, const string& output) { + METRIC_RECORD("FinishEdge"); TimeStamp restat_mtime = 0; if (success) { -- cgit v0.12 From 62b136315c6635a64f314dde78dbd8f1235eb306 Mon Sep 17 00:00:00 2001 From: Maxim Kalaev Date: Sun, 14 Oct 2012 20:40:46 +0200 Subject: build log: fixing parameter names --- src/build_log.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/build_log.cc b/src/build_log.cc index 28fcf88..235951f 100644 --- a/src/build_log.cc +++ b/src/build_log.cc @@ -91,13 +91,13 @@ uint64_t BuildLog::LogEntry::HashCommand(StringPiece command) { return MurmurHash64A(command.str_, command.len_); } -BuildLog::LogEntry::LogEntry(const string& _output) - : output(_output) {} +BuildLog::LogEntry::LogEntry(const string& output) + : output(output) {} -BuildLog::LogEntry::LogEntry(const string& _output, uint64_t _command_hash, - int _start_time, int _end_time, TimeStamp _restat_mtime) - : output(_output), command_hash(_command_hash), - start_time(_start_time), end_time(_end_time), restat_mtime(_restat_mtime) +BuildLog::LogEntry::LogEntry(const string& output, uint64_t command_hash, + int start_time, int end_time, TimeStamp restat_mtime) + : output(output), command_hash(command_hash), + start_time(start_time), end_time(end_time), restat_mtime(restat_mtime) {} BuildLog::BuildLog() -- cgit v0.12 From a35bd3677f3848939e821505158a7dde8f2a7407 Mon Sep 17 00:00:00 2001 From: Thiago Farina Date: Mon, 8 Oct 2012 23:48:11 -0300 Subject: Add missing 'virtual' annotation to ReadFile() override. Signed-off-by: Thiago Farina --- src/metrics.h | 7 ++----- src/ninja.cc | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/metrics.h b/src/metrics.h index a4ef9f7..044011d 100644 --- a/src/metrics.h +++ b/src/metrics.h @@ -62,12 +62,10 @@ private: /// Epoch varies between platforms; only useful for measuring elapsed time. int64_t GetTimeMillis(); - /// A simple stopwatch which returns the time /// in seconds since Restart() was called. -class Stopwatch -{ -public: +class Stopwatch { + public: Stopwatch() : started_(0) {} /// Seconds since Restart() call. @@ -80,7 +78,6 @@ private: uint64_t Now() const; }; - /// The primary interface to metrics. Use METRIC_RECORD("foobar") at the top /// of a function to get timing stats recorded for each call of the function. #define METRIC_RECORD(name) \ diff --git a/src/ninja.cc b/src/ninja.cc index 76b2764..e408ce1 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -140,7 +140,7 @@ int GuessParallelism() { /// An implementation of ManifestParser::FileReader that actually reads /// the file. struct RealFileReader : public ManifestParser::FileReader { - bool ReadFile(const string& path, string* content, string* err) { + virtual bool ReadFile(const string& path, string* content, string* err) { return ::ReadFile(path, content, err) == 0; } }; -- cgit v0.12 From 1d5daecfaf1853f87af1d99fb84c347576648474 Mon Sep 17 00:00:00 2001 From: Zaheer Chothia Date: Thu, 18 Oct 2012 15:02:48 +0200 Subject: Add support for Python 3 --- bootstrap.py | 25 +++++++++++++++++-------- configure.py | 14 +++++++++++--- misc/ninja_syntax.py | 2 +- misc/ninja_test.py | 6 +++++- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/bootstrap.py b/bootstrap.py index 3032a9b..1c05094 100755 --- a/bootstrap.py +++ b/bootstrap.py @@ -22,6 +22,14 @@ import shlex import shutil import subprocess +if sys.version_info[0] == 3: + import builtins + print_ = getattr(builtins, "print") +else: + def print_(*args): + sys.stdout.write(" ".join(str(x) for x in args)) + sys.stdout.write("\n") + os.chdir(os.path.dirname(os.path.abspath(__file__))) parser = OptionParser() @@ -44,11 +52,12 @@ if sys.platform.startswith('freebsd'): cflags.append('-I/usr/local/include') ldflags.append('-L/usr/local/lib') -print 'Building ninja manually...' +print_('Building ninja manually...') try: os.mkdir('build') -except OSError, e: +except OSError: + e = sys.exc_info()[1] if e.errno != errno.EEXIST: raise @@ -103,7 +112,7 @@ else: args.extend(['-o', binary]) if options.verbose: - print ' '.join(args) + print_(' '.join(args)) run(args) @@ -112,7 +121,7 @@ if options.verbose: verbose = ['-v'] if sys.platform.startswith('win32'): - print 'Building ninja using itself...' + print_('Building ninja using itself...') run([sys.executable, 'configure.py', '--with-ninja=%s' % binary] + conf_args) run(['./' + binary] + verbose) @@ -124,17 +133,17 @@ if sys.platform.startswith('win32'): for obj in glob.glob('*.obj'): os.unlink(obj) - print """ + print_(""" Done! Note: to work around Windows file locking, where you can't rebuild an in-use binary, to run ninja after making any changes to build ninja itself you should run ninja.bootstrap instead. Your build is also configured to use ninja.bootstrap.exe as the MSVC helper; see the --with-ninja flag of -the --help output of configure.py.""" +the --help output of configure.py.""") else: - print 'Building ninja using itself...' + print_('Building ninja using itself...') run([sys.executable, 'configure.py'] + conf_args) run(['./' + binary] + verbose) os.unlink(binary) - print 'Done!' + print_('Done!') diff --git a/configure.py b/configure.py index cb0d45e..f716067 100755 --- a/configure.py +++ b/configure.py @@ -26,6 +26,14 @@ sys.path.insert(0, 'misc') import ninja_syntax +if sys.version_info[0] == 3: + import builtins + print_ = getattr(builtins, "print") +else: + def print_(*args): + sys.stdout.write(" ".join(str(x) for x in args)) + sys.stdout.write("\n") + parser = OptionParser() platforms = ['linux', 'freebsd', 'solaris', 'mingw', 'windows'] profilers = ['gmon', 'pprof'] @@ -50,7 +58,7 @@ parser.add_option('--with-ninja', metavar='NAME', default="ninja") (options, args) = parser.parse_args() if args: - print 'ERROR: extra unparsed command-line arguments:', args + print_('ERROR: extra unparsed command-line arguments:', args) sys.exit(1) platform = options.platform @@ -256,7 +264,7 @@ if has_re2c(): n.build(src('depfile_parser.cc'), 're2c', src('depfile_parser.in.cc')) n.build(src('lexer.cc'), 're2c', src('lexer.in.cc')) else: - print ("warning: A compatible version of re2c (>= 0.11.3) was not found; " + print_("warning: A compatible version of re2c (>= 0.11.3) was not found; " "changes to src/*.in.cc will not affect your build.") n.newline() @@ -436,4 +444,4 @@ if host == 'linux': n.build('all', 'phony', all_targets) -print 'wrote %s.' % BUILD_FILENAME +print_('wrote %s.' % BUILD_FILENAME) diff --git a/misc/ninja_syntax.py b/misc/ninja_syntax.py index 66babbe..3572dd9 100644 --- a/misc/ninja_syntax.py +++ b/misc/ninja_syntax.py @@ -71,7 +71,7 @@ class Writer(object): if variables: if isinstance(variables, dict): - iterator = variables.iteritems() + iterator = iter(variables.items()) else: iterator = iter(variables) diff --git a/misc/ninja_test.py b/misc/ninja_test.py index b56033e..2aef7ff 100755 --- a/misc/ninja_test.py +++ b/misc/ninja_test.py @@ -15,7 +15,11 @@ # limitations under the License. import unittest -from StringIO import StringIO + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO import ninja_syntax -- cgit v0.12 From f4b1133d5c36b531a39606da4e1cadc38a784a2e Mon Sep 17 00:00:00 2001 From: Zaheer Chothia Date: Fri, 19 Oct 2012 09:46:09 +0200 Subject: Python scripts: use built-in print function. --- bootstrap.py | 22 ++++++++-------------- configure.py | 16 +++++----------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/bootstrap.py b/bootstrap.py index 1c05094..7dfc543 100755 --- a/bootstrap.py +++ b/bootstrap.py @@ -13,6 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import print_function + from optparse import OptionParser import sys import os @@ -22,14 +24,6 @@ import shlex import shutil import subprocess -if sys.version_info[0] == 3: - import builtins - print_ = getattr(builtins, "print") -else: - def print_(*args): - sys.stdout.write(" ".join(str(x) for x in args)) - sys.stdout.write("\n") - os.chdir(os.path.dirname(os.path.abspath(__file__))) parser = OptionParser() @@ -52,7 +46,7 @@ if sys.platform.startswith('freebsd'): cflags.append('-I/usr/local/include') ldflags.append('-L/usr/local/lib') -print_('Building ninja manually...') +print('Building ninja manually...') try: os.mkdir('build') @@ -112,7 +106,7 @@ else: args.extend(['-o', binary]) if options.verbose: - print_(' '.join(args)) + print(' '.join(args)) run(args) @@ -121,7 +115,7 @@ if options.verbose: verbose = ['-v'] if sys.platform.startswith('win32'): - print_('Building ninja using itself...') + print('Building ninja using itself...') run([sys.executable, 'configure.py', '--with-ninja=%s' % binary] + conf_args) run(['./' + binary] + verbose) @@ -133,7 +127,7 @@ if sys.platform.startswith('win32'): for obj in glob.glob('*.obj'): os.unlink(obj) - print_(""" + print(""" Done! Note: to work around Windows file locking, where you can't rebuild an @@ -142,8 +136,8 @@ you should run ninja.bootstrap instead. Your build is also configured to use ninja.bootstrap.exe as the MSVC helper; see the --with-ninja flag of the --help output of configure.py.""") else: - print_('Building ninja using itself...') + print('Building ninja using itself...') run([sys.executable, 'configure.py'] + conf_args) run(['./' + binary] + verbose) os.unlink(binary) - print_('Done!') + print('Done!') diff --git a/configure.py b/configure.py index f716067..e41cf4e 100755 --- a/configure.py +++ b/configure.py @@ -19,6 +19,8 @@ Projects that use ninja themselves should either write a similar script or use a meta-build system that supports Ninja output.""" +from __future__ import print_function + from optparse import OptionParser import os import sys @@ -26,14 +28,6 @@ sys.path.insert(0, 'misc') import ninja_syntax -if sys.version_info[0] == 3: - import builtins - print_ = getattr(builtins, "print") -else: - def print_(*args): - sys.stdout.write(" ".join(str(x) for x in args)) - sys.stdout.write("\n") - parser = OptionParser() platforms = ['linux', 'freebsd', 'solaris', 'mingw', 'windows'] profilers = ['gmon', 'pprof'] @@ -58,7 +52,7 @@ parser.add_option('--with-ninja', metavar='NAME', default="ninja") (options, args) = parser.parse_args() if args: - print_('ERROR: extra unparsed command-line arguments:', args) + print('ERROR: extra unparsed command-line arguments:', args) sys.exit(1) platform = options.platform @@ -264,7 +258,7 @@ if has_re2c(): n.build(src('depfile_parser.cc'), 're2c', src('depfile_parser.in.cc')) n.build(src('lexer.cc'), 're2c', src('lexer.in.cc')) else: - print_("warning: A compatible version of re2c (>= 0.11.3) was not found; " + print("warning: A compatible version of re2c (>= 0.11.3) was not found; " "changes to src/*.in.cc will not affect your build.") n.newline() @@ -444,4 +438,4 @@ if host == 'linux': n.build('all', 'phony', all_targets) -print_('wrote %s.' % BUILD_FILENAME) +print('wrote %s.' % BUILD_FILENAME) -- cgit v0.12 From 13b8aff3ca7d52db40cf2ac48194b0b3ef67e664 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Fri, 28 Sep 2012 10:05:34 -0700 Subject: bootstrap: fail more gracefully if gcc isn't available --- bootstrap.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bootstrap.py b/bootstrap.py index 7dfc543..291317f 100755 --- a/bootstrap.py +++ b/bootstrap.py @@ -108,7 +108,11 @@ else: if options.verbose: print(' '.join(args)) -run(args) +try: + run(args) +except: + print 'Failure running:', args + raise verbose = [] if options.verbose: -- cgit v0.12 From 4d62d11675e90d44dad02198461994d50655dc3f Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Mon, 8 Oct 2012 10:09:12 -0700 Subject: fix whitespace --- src/graph.cc | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/graph.cc b/src/graph.cc index 6ae324d..f6e0d0c 100644 --- a/src/graph.cc +++ b/src/graph.cc @@ -353,18 +353,18 @@ bool Edge::is_phony() const { } void Node::Dump(const char* prefix) const { - printf("%s <%s 0x%p> mtime: %d%s, (:%s), ", - prefix, path().c_str(), this, - mtime(), mtime()?"":" (:missing)", - dirty()?" dirty":" clean"); - if (in_edge()) { - in_edge()->Dump("in-edge: "); - }else{ - printf("no in-edge\n"); - } - printf(" out edges:\n"); - for (vector::const_iterator e = out_edges().begin(); - e != out_edges().end() && *e != NULL; ++e) { - (*e)->Dump(" +- "); - } + printf("%s <%s 0x%p> mtime: %d%s, (:%s), ", + prefix, path().c_str(), this, + mtime(), mtime() ? "" : " (:missing)", + dirty() ? " dirty" : " clean"); + if (in_edge()) { + in_edge()->Dump("in-edge: "); + } else { + printf("no in-edge\n"); + } + printf(" out edges:\n"); + for (vector::const_iterator e = out_edges().begin(); + e != out_edges().end() && *e != NULL; ++e) { + (*e)->Dump(" +- "); + } } -- cgit v0.12 From fae4d63821c96b954d47dd0520d82f21caa757ee Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Mon, 8 Oct 2012 16:30:27 -0700 Subject: update mingw docs for ubuntu precise --- HACKING.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/HACKING.md b/HACKING.md index a777b57..885d2d7 100644 --- a/HACKING.md +++ b/HACKING.md @@ -159,8 +159,15 @@ it's locked while in use. ### Via mingw on Linux (not well supported) +Setup on Ubuntu Lucid: * `sudo apt-get install gcc-mingw32 wine` * `export CC=i586-mingw32msvc-cc CXX=i586-mingw32msvc-c++ AR=i586-mingw32msvc-ar` + +Setup on Ubuntu Precise: +* `sudo apt-get install gcc-mingw-w64-i686 g++-mingw-w64-i686 wine` +* `export CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ AR=i686-w64-mingw32-ar` + +Then run: * `./configure.py --platform=mingw --host=linux` * Build `ninja.exe` using a Linux ninja binary: `/path/to/linux/ninja` * Run: `./ninja.exe` (implicitly runs through wine(!)) -- cgit v0.12 From e2709bd70462c25f0578408fdeda0d33ac42806e Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Mon, 8 Oct 2012 16:31:15 -0700 Subject: mingw helper: drop unused -r flag --- src/msvc_helper_main-win32.cc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/msvc_helper_main-win32.cc b/src/msvc_helper_main-win32.cc index 4a4b3c4..152450e 100644 --- a/src/msvc_helper_main-win32.cc +++ b/src/msvc_helper_main-win32.cc @@ -28,7 +28,6 @@ void Usage() { "usage: ninja -t msvc [options] -- cl.exe /showIncludes /otherArgs\n" "options:\n" " -e ENVFILE load environment block from ENVFILE as environment\n" -" -r BASE normalize paths and make relative to BASE before output\n" " -o FILE write output dependency information to FILE.d\n" ); } @@ -49,7 +48,6 @@ void PushPathIntoEnvironment(const string& env_block) { int MSVCHelperMain(int argc, char** argv) { const char* output_filename = NULL; - const char* relative_to = NULL; const char* envfile = NULL; const option kLongOptions[] = { @@ -57,7 +55,7 @@ int MSVCHelperMain(int argc, char** argv) { { NULL, 0, NULL, 0 } }; int opt; - while ((opt = getopt_long(argc, argv, "e:o:r:h", kLongOptions, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "e:o:h", kLongOptions, NULL)) != -1) { switch (opt) { case 'e': envfile = optarg; @@ -65,9 +63,6 @@ int MSVCHelperMain(int argc, char** argv) { case 'o': output_filename = optarg; break; - case 'r': - relative_to = optarg; - break; case 'h': default: Usage(); -- cgit v0.12 From 18d1b05431c0338d0bcb685e18f4d4d5120fc266 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Tue, 23 Oct 2012 09:37:55 -0700 Subject: fix bad merge --- bootstrap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap.py b/bootstrap.py index 291317f..12848ac 100755 --- a/bootstrap.py +++ b/bootstrap.py @@ -111,7 +111,7 @@ if options.verbose: try: run(args) except: - print 'Failure running:', args + print('Failure running:', args) raise verbose = [] -- cgit v0.12 From 13db3daadc2fb30320de90fe7667ab1cf9166799 Mon Sep 17 00:00:00 2001 From: Philip Puryear Date: Thu, 25 Oct 2012 22:41:20 -0500 Subject: browse.py: Python 3 support. Signed-off-by: Philip Puryear --- src/browse.py | 63 ++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/src/browse.py b/src/browse.py index 17e67cf..6bbf4de 100755 --- a/src/browse.py +++ b/src/browse.py @@ -20,7 +20,12 @@ This script is inlined into the final executable and spawned by it when needed. """ -import BaseHTTPServer +from __future__ import print_function + +try: + import http.server as httpserver +except ImportError: + import BaseHTTPServer as httpserver import subprocess import sys import webbrowser @@ -55,12 +60,12 @@ def parse(text): outputs = [] try: - target = lines.next()[:-1] # strip trailing colon + target = next(lines)[:-1] # strip trailing colon - line = lines.next() + line = next(lines) (match, rule) = match_strip(line, ' input: ') if match: - (match, line) = match_strip(lines.next(), ' ') + (match, line) = match_strip(next(lines), ' ') while match: type = None (match, line) = match_strip(line, '| ') @@ -70,21 +75,21 @@ def parse(text): if match: type = 'order-only' inputs.append((line, type)) - (match, line) = match_strip(lines.next(), ' ') + (match, line) = match_strip(next(lines), ' ') match, _ = match_strip(line, ' outputs:') if match: - (match, line) = match_strip(lines.next(), ' ') + (match, line) = match_strip(next(lines), ' ') while match: outputs.append(line) - (match, line) = match_strip(lines.next(), ' ') + (match, line) = match_strip(next(lines), ' ') except StopIteration: pass return Node(inputs, rule, target, outputs) def generate_html(node): - print ''' + document = [''' ''' +'''] - print '

%s

' % node.target + document.append('

%s

' % node.target) if node.inputs: - print '

target is built using rule %s of

' % node.rule + document.append('

target is built using rule %s of

' % + node.rule) if len(node.inputs) > 0: - print '
' + document.append('
') for input, type in sorted(node.inputs): extra = '' if type: extra = ' (%s)' % type - print '%s%s
' % (input, input, extra) - print '
' + document.append('%s%s
' % + (input, input, extra)) + document.append('
') if node.outputs: - print '

dependent edges build:

' - print '
' + document.append('

dependent edges build:

') + document.append('
') for output in sorted(node.outputs): - print '%s
' % (output, output) - print '
' + document.append('%s
' % + (output, output)) + document.append('
') + + return '\n'.join(document) def ninja_dump(target): proc = subprocess.Popen([sys.argv[1], '-t', 'query', target], - stdout=subprocess.PIPE) + stdout=subprocess.PIPE, universal_newlines=True) return proc.communicate()[0] -class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): +class RequestHandler(httpserver.BaseHTTPRequestHandler): def do_GET(self): assert self.path[0] == '/' target = self.path[1:] @@ -156,24 +166,19 @@ class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): self.send_response(200) self.end_headers() - stdout = sys.stdout - sys.stdout = self.wfile - try: - generate_html(parse(input.strip())) - finally: - sys.stdout = stdout + self.wfile.write(generate_html(parse(input.strip())).encode('utf-8')) def log_message(self, format, *args): pass # Swallow console spam. port = 8000 -httpd = BaseHTTPServer.HTTPServer(('',port), RequestHandler) +httpd = httpserver.HTTPServer(('',port), RequestHandler) try: - print 'Web server running on port %d, ctl-C to abort...' % port + print('Web server running on port %d, ctl-C to abort...' % port) webbrowser.open_new('http://localhost:%s' % port) httpd.serve_forever() except KeyboardInterrupt: - print + print() pass # Swallow console spam. -- cgit v0.12 From 10a0a7d9659591c44bba1ab74b069c9313a47dfa Mon Sep 17 00:00:00 2001 From: Philip Puryear Date: Thu, 25 Oct 2012 22:43:03 -0500 Subject: browse.py: Fix truncation with an unknown target. Signed-off-by: Philip Puryear --- src/browse.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/browse.py b/src/browse.py index 6bbf4de..bf4ff49 100755 --- a/src/browse.py +++ b/src/browse.py @@ -60,7 +60,9 @@ def parse(text): outputs = [] try: - target = next(lines)[:-1] # strip trailing colon + target = next(lines) + if target.endswith(':'): + target = target[:-1] # strip trailing colon line = next(lines) (match, rule) = match_strip(line, ' input: ') -- cgit v0.12 From 477b8b96aa5e4cee6cc85b9c629a986cd8469d07 Mon Sep 17 00:00:00 2001 From: Philip Puryear Date: Fri, 26 Oct 2012 16:56:48 -0500 Subject: browse.py: Don't truncate error message if ninja -t query fails. Signed-off-by: Philip Puryear --- src/browse.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/browse.py b/src/browse.py index bf4ff49..14d5edd 100755 --- a/src/browse.py +++ b/src/browse.py @@ -60,9 +60,7 @@ def parse(text): outputs = [] try: - target = next(lines) - if target.endswith(':'): - target = target[:-1] # strip trailing colon + target = next(lines)[:-1] # strip trailing colon line = next(lines) (match, rule) = match_strip(line, ' input: ') @@ -90,8 +88,8 @@ def parse(text): return Node(inputs, rule, target, outputs) -def generate_html(node): - document = [''' +def create_page(body): + return ''' '''] + +''' + body - document.append('

%s

' % node.target) +def generate_html(node): + document = ['

%s

' % node.target] if node.inputs: document.append('

target is built using rule %s of

' % @@ -145,7 +145,7 @@ tt { def ninja_dump(target): proc = subprocess.Popen([sys.argv[1], '-t', 'query', target], stdout=subprocess.PIPE, universal_newlines=True) - return proc.communicate()[0] + return (proc.communicate()[0], proc.returncode) class RequestHandler(httpserver.BaseHTTPRequestHandler): def do_GET(self): @@ -164,11 +164,16 @@ class RequestHandler(httpserver.BaseHTTPRequestHandler): return target = target[1:] - input = ninja_dump(target) + input, exit_code = ninja_dump(target) + if exit_code == 0: + page_body = generate_html(parse(input.strip())) + else: + # Relay ninja's error message. + page_body = '

%s

' % input self.send_response(200) self.end_headers() - self.wfile.write(generate_html(parse(input.strip())).encode('utf-8')) + self.wfile.write(create_page(page_body).encode('utf-8')) def log_message(self, format, *args): pass # Swallow console spam. -- cgit v0.12 From e6c67528e87f2a0899a3edb5e77432d8ec1aceb9 Mon Sep 17 00:00:00 2001 From: Tommy Nyquist Date: Tue, 30 Oct 2012 15:28:02 -0700 Subject: Add support for reading directory for bash completion. Currently ninja bash completion only works in the directory where the build.ninja files is located. This adds support for using the -C argument to ninja. --- misc/bash-completion | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/misc/bash-completion b/misc/bash-completion index ac4d051..b40136e 100644 --- a/misc/bash-completion +++ b/misc/bash-completion @@ -16,9 +16,17 @@ # . path/to/ninja/misc/bash-completion _ninja_target() { - local cur targets + local cur targets dir line targets_command OPTIND cur="${COMP_WORDS[COMP_CWORD]}" - targets=$((ninja -t targets all 2>/dev/null) | awk -F: '{print $1}') + dir="." + line=$(echo ${COMP_LINE} | cut -d" " -f 2-) + while getopts C: opt "${line[@]}"; do + case $opt in + C) dir="$OPTARG" ;; + esac + done; + targets_command="ninja -C ${dir} -t targets all" + targets=$((${targets_command} 2>/dev/null) | awk -F: '{print $1}') COMPREPLY=($(compgen -W "$targets" -- "$cur")) return 0 } -- cgit v0.12 From eaf33ee5579bbc7ed884a2c3daaf6cb2e9c0d37d Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Sat, 27 Oct 2012 12:52:46 -0700 Subject: trailing whitespace --- src/graph.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graph.cc b/src/graph.cc index f6e0d0c..0174e61 100644 --- a/src/graph.cc +++ b/src/graph.cc @@ -241,7 +241,7 @@ string EdgeEnv::MakePathList(vector::iterator begin, string Edge::EvaluateCommand(bool incl_rsp_file) { EdgeEnv env(this); string command = rule_->command().Evaluate(&env); - if (incl_rsp_file && HasRspFile()) + if (incl_rsp_file && HasRspFile()) command += ";rspfile=" + GetRspFileContent(); return command; } -- cgit v0.12 From 599d716fc78441436aebcde948886791d1988651 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Sat, 27 Oct 2012 12:56:31 -0700 Subject: delete some obsolete TODOs --- src/graph.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/graph.cc b/src/graph.cc index 0174e61..25b5baf 100644 --- a/src/graph.cc +++ b/src/graph.cc @@ -192,7 +192,7 @@ struct EdgeEnv : public Env { virtual string LookupVariable(const string& var); /// Given a span of Nodes, construct a list of paths suitable for a command - /// line. XXX here is where shell-escaping of e.g spaces should happen. + /// line. string MakePathList(vector::iterator begin, vector::iterator end, char sep); @@ -214,7 +214,6 @@ string EdgeEnv::LookupVariable(const string& var) { } else if (edge_->env_) { return edge_->env_->LookupVariable(var); } else { - // XXX should we warn here? return string(); } } -- cgit v0.12 From 93e509469953a90f31afc838536b82568da397b2 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Mon, 29 Oct 2012 09:53:30 -0700 Subject: refactor repeated code in cleaner --- src/clean.cc | 28 ++++++++++++++-------------- src/clean.h | 4 ++++ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/clean.cc b/src/clean.cc index c9d4cbd..0b8476b 100644 --- a/src/clean.cc +++ b/src/clean.cc @@ -82,6 +82,16 @@ bool Cleaner::IsAlreadyRemoved(const string& path) { return (i != removed_.end()); } +void Cleaner::RemoveEdgeFiles(Edge* edge) { + string depfile = edge->EvaluateDepFile(); + if (!depfile.empty()) + Remove(depfile); + + string rspfile = edge->GetRspFile(); + if (!rspfile.empty()) + Remove(rspfile); +} + void Cleaner::PrintHeader() { if (config_.verbosity == BuildConfig::QUIET) return; @@ -113,12 +123,8 @@ int Cleaner::CleanAll(bool generator) { out_node != (*e)->outputs_.end(); ++out_node) { Remove((*out_node)->path()); } - // Remove the depfile - if (!(*e)->rule().depfile().empty()) - Remove((*e)->EvaluateDepFile()); - // Remove the response file - if ((*e)->HasRspFile()) - Remove((*e)->GetRspFile()); + + RemoveEdgeFiles(*e); } PrintFooter(); return status_; @@ -129,10 +135,7 @@ void Cleaner::DoCleanTarget(Node* target) { // Do not try to remove phony targets if (!e->is_phony()) { Remove(target->path()); - if (!target->in_edge()->rule().depfile().empty()) - Remove(target->in_edge()->EvaluateDepFile()); - if (e->HasRspFile()) - Remove(e->GetRspFile()); + RemoveEdgeFiles(e); } for (vector::iterator n = e->inputs_.begin(); n != e->inputs_.end(); ++n) { @@ -200,10 +203,7 @@ void Cleaner::DoCleanRule(const Rule* rule) { for (vector::iterator out_node = (*e)->outputs_.begin(); out_node != (*e)->outputs_.end(); ++out_node) { Remove((*out_node)->path()); - if (!(*e)->rule().depfile().empty()) - Remove((*e)->EvaluateDepFile()); - if ((*e)->HasRspFile()) - Remove((*e)->GetRspFile()); + RemoveEdgeFiles(*e); } } } diff --git a/src/clean.h b/src/clean.h index 8359901..5a23283 100644 --- a/src/clean.h +++ b/src/clean.h @@ -81,10 +81,14 @@ class Cleaner { /// @returns whether the file @a path exists. bool FileExists(const string& path); void Report(const string& path); + /// Remove the given @a path file only if it has not been already removed. void Remove(const string& path); /// @return whether the given @a path has already been removed. bool IsAlreadyRemoved(const string& path); + /// Remove the depfile and rspfile for an Edge. + void RemoveEdgeFiles(Edge* edge); + /// Helper recursive method for CleanTarget(). void DoCleanTarget(Node* target); void PrintHeader(); -- cgit v0.12 From f5ce01ae885f7a93b5f1a4ac30e4abeaaa233f7e Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Thu, 8 Nov 2012 10:42:28 -0800 Subject: Fix a doxygen bug found by clang's -Wdocumentation. (That found one more issue, but I think that might be a bug in -Wdocumentation, http://llvm.org/PR14295.) --- src/build.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/build.h b/src/build.h index 8876d88..5b05601 100644 --- a/src/build.h +++ b/src/build.h @@ -175,7 +175,7 @@ struct BuildStatus { /// Format the progress status string by replacing the placeholders. /// See the user manual for more information about the available /// placeholders. - /// @param progress_status_format_ The format of the progress status. + /// @param progress_status_format The format of the progress status. string FormatProgressStatus(const char* progress_status_format) const; private: -- cgit v0.12 From 8775c4d1e3630230dcea202f2d1647e56a81c20f Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Mon, 1 Oct 2012 13:51:00 -0700 Subject: Pull out base changes to state --- src/state.cc | 17 ++++++++++++++++- src/state.h | 25 ++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/state.cc b/src/state.cc index 4c7168b..0697e48 100644 --- a/src/state.cc +++ b/src/state.cc @@ -22,10 +22,12 @@ #include "metrics.h" #include "util.h" +const Pool State::kDefaultPool("", 0); const Rule State::kPhonyRule("phony"); State::State() { AddRule(&kPhonyRule); + AddPool(&kDefaultPool); } void State::AddRule(const Rule* rule) { @@ -40,7 +42,20 @@ const Rule* State::LookupRule(const string& rule_name) { return i->second; } -Edge* State::AddEdge(const Rule* rule) { +void State::AddPool(const Pool* pool) { + assert(LookupPool(pool->name()) == NULL); + pools_[pool->name()] = pool; +} + +const Pool* State::LookupPool(const string& pool_name) { + map::iterator i = pools_.find(pool_name); + if (i == pools_.end()) + return NULL; + return i->second; +} + +Edge* State::AddEdge(const Rule* rule, const Pool* pool) { + // FIXME(iannucci): do something with pool Edge* edge = new Edge(); edge->rule_ = rule; edge->env_ = &bindings_; diff --git a/src/state.h b/src/state.h index 026acf3..38fc74f 100644 --- a/src/state.h +++ b/src/state.h @@ -27,16 +27,36 @@ struct Edge; struct Node; struct Rule; +/// A pool for delayed edges +struct Pool { + explicit Pool(const string& name, int depth) + : name_(name), depth_(depth) { } + + // A depth of 0 is infinite + bool isValid() const { return depth_ >= 0; } + int depth() const { return depth_; } + string name() const { return name_; } + +private: + string name_; + + int depth_; +}; + /// Global state (file status, loaded rules) for a single run. struct State { static const Rule kPhonyRule; + static const Pool kDefaultPool; State(); void AddRule(const Rule* rule); const Rule* LookupRule(const string& rule_name); - Edge* AddEdge(const Rule* rule); + void AddPool(const Pool* pool); + const Pool* LookupPool(const string& pool_name); + + Edge* AddEdge(const Rule* rule, const Pool* pool); Node* GetNode(StringPiece path); Node* LookupNode(StringPiece path); @@ -65,6 +85,9 @@ struct State { /// All the rules used in the graph. map rules_; + /// All the pools used in the graph. + map pools_; + /// All the edges of the graph. vector edges_; -- cgit v0.12 From d1c32c9f40e22ac1f1a8a5b4fd42fc3304b6ba78 Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Mon, 1 Oct 2012 13:58:30 -0700 Subject: Pull graph.cc too --- src/graph.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/graph.cc b/src/graph.cc index 25b5baf..3550d48 100644 --- a/src/graph.cc +++ b/src/graph.cc @@ -316,7 +316,8 @@ bool DependencyScan::LoadDepFile(Edge* edge, string* err) { // create one; this makes us not abort if the input is missing, // but instead will rebuild in that circumstance. if (!node->in_edge()) { - Edge* phony_edge = state_->AddEdge(&State::kPhonyRule); + Edge* phony_edge = state_->AddEdge(&State::kPhonyRule, + &State::kDefaultPool); node->set_in_edge(phony_edge); phony_edge->outputs_.push_back(node); -- cgit v0.12 From a4220cdcd298cb43133c241497f2edb9e5cbc8d9 Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Mon, 1 Oct 2012 14:12:59 -0700 Subject: Const ref FTW --- src/state.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state.h b/src/state.h index 38fc74f..e678df9 100644 --- a/src/state.h +++ b/src/state.h @@ -35,7 +35,7 @@ struct Pool { // A depth of 0 is infinite bool isValid() const { return depth_ >= 0; } int depth() const { return depth_; } - string name() const { return name_; } + const string& name() const { return name_; } private: string name_; -- cgit v0.12 From aca4ec54656057e58ca7a9ae5de7f94c869b2ccb Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Wed, 3 Oct 2012 16:27:27 -0700 Subject: stub out an api and de-constify Pool --- src/build.cc | 24 ++++++++++++++++++++++-- src/build.h | 8 +++++--- src/graph.h | 2 ++ src/manifest_parser.cc | 2 +- src/state.cc | 12 ++++++------ src/state.h | 32 +++++++++++++++++++++++++++----- 6 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/build.cc b/src/build.cc index 19775e7..0710e51 100644 --- a/src/build.cc +++ b/src/build.cc @@ -359,7 +359,7 @@ bool Plan::AddSubTarget(Node* node, vector* stack, string* err) { want = true; ++wanted_edges_; if (edge->AllInputsReady()) - ready_.insert(edge); + ScheduleWork(edge); if (!edge->is_phony()) ++command_edges_; } @@ -408,6 +408,22 @@ Edge* Plan::FindWork() { return edge; } +void Plan::ScheduleWork(Edge* edge) { + // TODO(iannucci): See if this should get delayed instead + // if edge has pool + // create pool if DNE + // pool.InsertEdge(edge) + // ready_.insert(pool.GetAvailableEdges()) + // else + ready_.insert(edge); +} + +void Plan::ResumeDelayedJobs(Edge* edge) { + // if edge has pool + // pool.ReturnUnits(edge) + // ready_.insert(pool.GetAvailableEdges()) +} + void Plan::EdgeFinished(Edge* edge) { map::iterator i = want_.find(edge); assert(i != want_.end()); @@ -416,6 +432,9 @@ void Plan::EdgeFinished(Edge* edge) { want_.erase(i); edge->outputs_ready_ = true; + // See if this job frees up any delayed jobs + ResumeDelayedJobs(edge); + // Check off any nodes we were waiting for with this edge. for (vector::iterator i = edge->outputs_.begin(); i != edge->outputs_.end(); ++i) { @@ -434,7 +453,7 @@ void Plan::NodeFinished(Node* node) { // See if the edge is now ready. if ((*i)->AllInputsReady()) { if (want_i->second) { - ready_.insert(*i); + ScheduleWork(*i); } else { // We do not need to build this edge, but we might need to build one of // its dependents. @@ -501,6 +520,7 @@ void Plan::Dump() { printf("want "); i->first->Dump(); } + // TODO(iannucci): Dump pending pools too printf("ready: %d\n", (int)ready_.size()); } diff --git a/src/build.h b/src/build.h index 8876d88..6bcfc07 100644 --- a/src/build.h +++ b/src/build.h @@ -15,13 +15,13 @@ #ifndef NINJA_BUILD_H_ #define NINJA_BUILD_H_ +#include #include +#include +#include #include #include -#include #include -#include -#include #include "graph.h" // XXX needed for DependencyScan; should rearrange. #include "exit_status.h" @@ -69,6 +69,8 @@ private: bool AddSubTarget(Node* node, vector* stack, string* err); bool CheckDependencyCycle(Node* node, vector* stack, string* err); void NodeFinished(Node* node); + void ScheduleWork(Edge* edge); + void ResumeDelayedJobs(Edge* edge); /// Keep track of which edges we want to build in this plan. If this map does /// not contain an entry for an edge, we do not want to build the entry or its diff --git a/src/graph.h b/src/graph.h index 272fcb9..a35ab65 100644 --- a/src/graph.h +++ b/src/graph.h @@ -138,6 +138,7 @@ struct Rule { struct BuildLog; struct Node; struct State; +struct Pool; /// An edge in the dependency graph; links between Nodes using Rules. struct Edge { @@ -166,6 +167,7 @@ struct Edge { void Dump(const char* prefix="") const; const Rule* rule_; + Pool* pool_; vector inputs_; vector outputs_; Env* env_; diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc index 405e244..8a010dd 100644 --- a/src/manifest_parser.cc +++ b/src/manifest_parser.cc @@ -266,7 +266,7 @@ bool ManifestParser::ParseEdge(string* err) { } while (lexer_.PeekToken(Lexer::INDENT)); } - Edge* edge = state_->AddEdge(rule); + Edge* edge = state_->AddEdge(rule, NULL); edge->env_ = env; for (vector::iterator i = ins.begin(); i != ins.end(); ++i) { string path = i->Evaluate(env); diff --git a/src/state.cc b/src/state.cc index 0697e48..e8e6dc3 100644 --- a/src/state.cc +++ b/src/state.cc @@ -22,7 +22,7 @@ #include "metrics.h" #include "util.h" -const Pool State::kDefaultPool("", 0); +Pool State::kDefaultPool("", 0); const Rule State::kPhonyRule("phony"); State::State() { @@ -42,22 +42,22 @@ const Rule* State::LookupRule(const string& rule_name) { return i->second; } -void State::AddPool(const Pool* pool) { +void State::AddPool(Pool* pool) { assert(LookupPool(pool->name()) == NULL); pools_[pool->name()] = pool; } -const Pool* State::LookupPool(const string& pool_name) { - map::iterator i = pools_.find(pool_name); +Pool* State::LookupPool(const string& pool_name) { + map::iterator i = pools_.find(pool_name); if (i == pools_.end()) return NULL; return i->second; } -Edge* State::AddEdge(const Rule* rule, const Pool* pool) { - // FIXME(iannucci): do something with pool +Edge* State::AddEdge(const Rule* rule, Pool* pool) { Edge* edge = new Edge(); edge->rule_ = rule; + edge->pool_ = pool; edge->env_ = &bindings_; edges_.push_back(edge); return edge; diff --git a/src/state.h b/src/state.h index e678df9..3e569cf 100644 --- a/src/state.h +++ b/src/state.h @@ -16,6 +16,8 @@ #define NINJA_STATE_H_ #include +#include +#include #include #include using namespace std; @@ -37,26 +39,46 @@ struct Pool { int depth() const { return depth_; } const string& name() const { return name_; } + /// true if the Pool would delay this edge + bool ShouldDelayEdge(Edge* edge); + + /// informs this Pool that the given edge is committed to be run. + /// Pool will count this edge as using resources from this pool. + void EdgeScheduled(Edge* edge); + + /// informs this Pool that the given edge is no longer runnable, and should + /// relinquish it's resources back to the pool + void EdgeFinished(Edge* edge); + + /// adds the given edge to this Pool to be delayed. + void DelayEdge(Edge* edge); + + /// Pool will add zero or more edges to the ready_queue + void RetrieveReadyEdges(Edge* edge, set* ready_queue); + private: string name_; + int current_use_; int depth_; + + queue delayed_; }; /// Global state (file status, loaded rules) for a single run. struct State { + static Pool kDefaultPool; static const Rule kPhonyRule; - static const Pool kDefaultPool; State(); void AddRule(const Rule* rule); const Rule* LookupRule(const string& rule_name); - void AddPool(const Pool* pool); - const Pool* LookupPool(const string& pool_name); + void AddPool(Pool* pool); + Pool* LookupPool(const string& pool_name); - Edge* AddEdge(const Rule* rule, const Pool* pool); + Edge* AddEdge(const Rule* rule, Pool* pool); Node* GetNode(StringPiece path); Node* LookupNode(StringPiece path); @@ -86,7 +108,7 @@ struct State { map rules_; /// All the pools used in the graph. - map pools_; + map pools_; /// All the edges of the graph. vector edges_; -- cgit v0.12 From 307f0bbd13d881cc2883c3e5f7f385b8f7914480 Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Wed, 3 Oct 2012 17:25:23 -0700 Subject: and some basic implementation --- src/build.cc | 20 ++++++++++---------- src/graph.h | 2 ++ src/state.cc | 27 +++++++++++++++++++++++++++ src/state.h | 12 +++++++----- 4 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/build.cc b/src/build.cc index 0710e51..2794c6c 100644 --- a/src/build.cc +++ b/src/build.cc @@ -409,19 +409,19 @@ Edge* Plan::FindWork() { } void Plan::ScheduleWork(Edge* edge) { - // TODO(iannucci): See if this should get delayed instead - // if edge has pool - // create pool if DNE - // pool.InsertEdge(edge) - // ready_.insert(pool.GetAvailableEdges()) - // else - ready_.insert(edge); + Pool& pool = edge->pool(); + if (pool.ShouldDelayEdge(*edge)) { + pool.DelayEdge(edge); + pool.RetrieveReadyEdges(&ready_); + } else { + pool.EdgeScheduled(*edge); + ready_.insert(edge); + } } void Plan::ResumeDelayedJobs(Edge* edge) { - // if edge has pool - // pool.ReturnUnits(edge) - // ready_.insert(pool.GetAvailableEdges()) + edge->pool().EdgeFinished(*edge); + edge->pool().RetrieveReadyEdges(&ready_); } void Plan::EdgeFinished(Edge* edge) { diff --git a/src/graph.h b/src/graph.h index a35ab65..9e924f7 100644 --- a/src/graph.h +++ b/src/graph.h @@ -174,6 +174,8 @@ struct Edge { bool outputs_ready_; const Rule& rule() const { return *rule_; } + Pool& pool() const { return *pool_; } + int weight() const { return 1; } bool outputs_ready() const { return outputs_ready_; } // XXX There are three types of inputs. diff --git a/src/state.cc b/src/state.cc index e8e6dc3..b34b938 100644 --- a/src/state.cc +++ b/src/state.cc @@ -22,6 +22,33 @@ #include "metrics.h" #include "util.h" + +void Pool::EdgeScheduled(const Edge& edge) { + if (depth_ != 0) + current_use_ += edge.weight(); +} + +void Pool::EdgeFinished(const Edge& edge) { + if (depth_ != 0) + current_use_ -= edge.weight(); +} + +void Pool::DelayEdge(Edge* edge) { + assert(depth_ != 0); + delayed_.push(edge); +} + +void Pool::RetrieveReadyEdges(set* ready_queue) { + while(!delayed_.empty()) { + Edge* edge = delayed_.front(); + if(current_use_ + edge->weight() > depth_) + break; + delayed_.pop(); + ready_queue->insert(edge); + EdgeScheduled(*edge); + } +} + Pool State::kDefaultPool("", 0); const Rule State::kPhonyRule("phony"); diff --git a/src/state.h b/src/state.h index 3e569cf..e3137c4 100644 --- a/src/state.h +++ b/src/state.h @@ -39,24 +39,26 @@ struct Pool { int depth() const { return depth_; } const string& name() const { return name_; } - /// true if the Pool would delay this edge - bool ShouldDelayEdge(Edge* edge); + /// true if the Pool might delay this edge + bool ShouldDelayEdge(const Edge& edge) const { return depth_ == 0; } /// informs this Pool that the given edge is committed to be run. /// Pool will count this edge as using resources from this pool. - void EdgeScheduled(Edge* edge); + void EdgeScheduled(const Edge& edge); /// informs this Pool that the given edge is no longer runnable, and should /// relinquish it's resources back to the pool - void EdgeFinished(Edge* edge); + void EdgeFinished(const Edge& edge); /// adds the given edge to this Pool to be delayed. void DelayEdge(Edge* edge); /// Pool will add zero or more edges to the ready_queue - void RetrieveReadyEdges(Edge* edge, set* ready_queue); + void RetrieveReadyEdges(set* ready_queue); private: + int UnitsWaiting() { return delayed_.size(); } + string name_; int current_use_; -- cgit v0.12 From 09515ebc7fbf0aa9b8d037f6c43a2e95b2899f4a Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Thu, 4 Oct 2012 16:50:24 -0700 Subject: Fix a bug... now chrome build works O_O --- src/state.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state.h b/src/state.h index e3137c4..349679a 100644 --- a/src/state.h +++ b/src/state.h @@ -40,7 +40,7 @@ struct Pool { const string& name() const { return name_; } /// true if the Pool might delay this edge - bool ShouldDelayEdge(const Edge& edge) const { return depth_ == 0; } + bool ShouldDelayEdge(const Edge& edge) const { return depth_ != 0; } /// informs this Pool that the given edge is committed to be run. /// Pool will count this edge as using resources from this pool. -- cgit v0.12 From 489cc06d0fa49e55137109d1b94f7b116fa59f38 Mon Sep 17 00:00:00 2001 From: Hannu Koivisto Date: Wed, 10 Oct 2012 14:17:42 +0300 Subject: Add --windows option to bootstrap.py Makes it possible to make a native Windows build when using Cygwin Python. --- bootstrap.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/bootstrap.py b/bootstrap.py index 12848ac..7463d34 100755 --- a/bootstrap.py +++ b/bootstrap.py @@ -31,6 +31,9 @@ parser.add_option('--verbose', action='store_true', help='enable verbose build',) parser.add_option('--x64', action='store_true', help='force 64-bit build (Windows)',) +parser.add_option('--windows', action='store_true', + help='force native Windows build (when using Cygwin Python)', + default=sys.platform.startswith('win32')) (options, conf_args) = parser.parse_args() def run(*args, **kwargs): @@ -66,7 +69,7 @@ for src in glob.glob('src/*.cc'): if filename == 'browse.cc': # Depends on generated header. continue - if sys.platform.startswith('win32'): + if options.windows: if src.endswith('-posix.cc'): continue else: @@ -75,7 +78,7 @@ for src in glob.glob('src/*.cc'): sources.append(src) -if sys.platform.startswith('win32'): +if options.windows: sources.append('src/getopt.c') vcdir = os.environ.get('VCINSTALLDIR') @@ -90,14 +93,14 @@ else: cflags.extend(['-Wno-deprecated', '-DNINJA_PYTHON="' + sys.executable + '"', '-DNINJA_BOOTSTRAP']) - if sys.platform.startswith('win32'): + if options.windows: cflags.append('-D_WIN32_WINNT=0x0501') if options.x64: cflags.append('-m64') args.extend(cflags) args.extend(ldflags) binary = 'ninja.bootstrap' -if sys.platform.startswith('win32'): +if options.windows: binary = 'ninja.bootstrap.exe' args.extend(sources) if vcdir: @@ -118,7 +121,7 @@ verbose = [] if options.verbose: verbose = ['-v'] -if sys.platform.startswith('win32'): +if options.windows: print('Building ninja using itself...') run([sys.executable, 'configure.py', '--with-ninja=%s' % binary] + conf_args) -- cgit v0.12 From 7ef52dcf45cea23044a53ea273b4a18899eddcda Mon Sep 17 00:00:00 2001 From: Richard Geary Date: Fri, 14 Sep 2012 05:04:19 -0400 Subject: .gitignore Eclipse project files Change-Id: I7cfe7cee92e1800284829f099fe6a44f212b527f --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 19a08ee..3cee921 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,8 @@ TAGS /doc/manual.html /doc/doxygen /gtest-1.6.0 + +# Eclipse project files +.project +.cproject + -- cgit v0.12 From c302134756b7afdf1fb7f2375d2d4597d311b03e Mon Sep 17 00:00:00 2001 From: Richard Geary Date: Fri, 14 Sep 2012 05:02:06 -0400 Subject: Improved error message with more information Change-Id: Idb1ce67a320a9819de262d83b498ee10eb362ed2 --- src/util.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.cc b/src/util.cc index 0feb99d..4b2900f 100644 --- a/src/util.cc +++ b/src/util.cc @@ -155,7 +155,7 @@ bool CanonicalizePath(char* path, size_t* len, string* err) { } if (component_count == kMaxPathComponents) - Fatal("path has too many components"); + Fatal("path has too many components : %s", path); components[component_count] = dst; ++component_count; -- cgit v0.12 From d06e3c12ffef42ad845dbd2883e7921a5f89f64d Mon Sep 17 00:00:00 2001 From: Richard Geary Date: Wed, 26 Sep 2012 04:40:33 -0400 Subject: Exit status = 2 if user presses ctrl-c Change-Id: I7be958e18eb2e434e78afb6e03b332281a651957 --- src/ninja.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ninja.cc b/src/ninja.cc index ad56f1c..a5e8d81 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -682,6 +682,9 @@ int RunBuild(Builder* builder, int argc, char** argv) { if (!builder->Build(&err)) { printf("ninja: build stopped: %s.\n", err.c_str()); + if (err.find("interrupted by user") != string::npos) { + return 2; + } return 1; } -- cgit v0.12 From ca1f213f63013902f931b98b4c428abb6722d8d8 Mon Sep 17 00:00:00 2001 From: Maxim Kalaev Date: Fri, 9 Nov 2012 09:20:05 +0200 Subject: refactoring: decompose CollectTargetsFromArgs --- src/ninja.cc | 100 ++++++++++++++++++++++++++++++----------------------------- 1 file changed, 51 insertions(+), 49 deletions(-) diff --git a/src/ninja.cc b/src/ninja.cc index e408ce1..7869774 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -168,6 +168,50 @@ bool RebuildManifest(Builder* builder, const char* input_file, string* err) { return node->dirty(); } +Node* CollectTarget(State* state, const char* cpath, string* err) { + string path = cpath; + if (!CanonicalizePath(&path, err)) + return NULL; + + // Special syntax: "foo.cc^" means "the first output of foo.cc". + bool first_dependent = false; + if (!path.empty() && path[path.size() - 1] == '^') { + path.resize(path.size() - 1); + first_dependent = true; + } + + Node* node = state->LookupNode(path); + if (node) { + if (first_dependent) { + if (node->out_edges().empty()) { + *err = "'" + path + "' has no out edge"; + return false; + } + Edge* edge = node->out_edges()[0]; + if (edge->outputs_.empty()) { + edge->Dump(); + Fatal("edge has no outputs"); + } + node = edge->outputs_[0]; + } + return node; + } else { + *err = "unknown target '" + path + "'"; + + if (path == "clean") { + *err += ", did you mean 'ninja -t clean'?"; + } else if (path == "help") { + *err += ", did you mean 'ninja -h'?"; + } else { + Node* suggestion = state->SpellcheckNode(path); + if (suggestion) { + *err += ", did you mean '" + suggestion->path() + "'?"; + } + } + return NULL; + } +} + bool CollectTargetsFromArgs(State* state, int argc, char* argv[], vector* targets, string* err) { if (argc == 0) { @@ -176,47 +220,10 @@ bool CollectTargetsFromArgs(State* state, int argc, char* argv[], } for (int i = 0; i < argc; ++i) { - string path = argv[i]; - if (!CanonicalizePath(&path, err)) - return false; - - // Special syntax: "foo.cc^" means "the first output of foo.cc". - bool first_dependent = false; - if (!path.empty() && path[path.size() - 1] == '^') { - path.resize(path.size() - 1); - first_dependent = true; - } - - Node* node = state->LookupNode(path); - if (node) { - if (first_dependent) { - if (node->out_edges().empty()) { - *err = "'" + path + "' has no out edge"; - return false; - } - Edge* edge = node->out_edges()[0]; - if (edge->outputs_.empty()) { - edge->Dump(); - Fatal("edge has no outputs"); - } - node = edge->outputs_[0]; - } - targets->push_back(node); - } else { - *err = "unknown target '" + path + "'"; - - if (path == "clean") { - *err += ", did you mean 'ninja -t clean'?"; - } else if (path == "help") { - *err += ", did you mean 'ninja -h'?"; - } else { - Node* suggestion = state->SpellcheckNode(path); - if (suggestion) { - *err += ", did you mean '" + suggestion->path() + "'?"; - } - } + Node* node = CollectTarget(state, argv[i], err); + if (node == NULL) return false; - } + targets->push_back(node); } return true; } @@ -244,19 +251,14 @@ int ToolQuery(Globals* globals, int argc, char* argv[]) { return 1; } for (int i = 0; i < argc; ++i) { - Node* node = globals->state->LookupNode(argv[i]); + string err; + Node* node = CollectTarget(globals->state, argv[i], &err); if (!node) { - Node* suggestion = globals->state->SpellcheckNode(argv[i]); - if (suggestion) { - printf("%s unknown, did you mean %s?\n", - argv[i], suggestion->path().c_str()); - } else { - printf("%s unknown\n", argv[i]); - } + Error("%s", err.c_str()); return 1; } - printf("%s:\n", argv[i]); + printf("%s:\n", node->path().c_str()); if (Edge* edge = node->in_edge()) { printf(" input: %s\n", edge->rule_->name().c_str()); for (int in = 0; in < (int)edge->inputs_.size(); in++) { -- cgit v0.12 From a5bf183fd9ae7745effbce5dee1e76432c865d5a Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Fri, 9 Nov 2012 12:07:12 -0800 Subject: Dump pools with State --- src/build.cc | 1 - src/state.cc | 22 ++++++++++++++++++++-- src/state.h | 9 ++++++--- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/build.cc b/src/build.cc index 2794c6c..cadf7dc 100644 --- a/src/build.cc +++ b/src/build.cc @@ -520,7 +520,6 @@ void Plan::Dump() { printf("want "); i->first->Dump(); } - // TODO(iannucci): Dump pending pools too printf("ready: %d\n", (int)ready_.size()); } diff --git a/src/state.cc b/src/state.cc index b34b938..b0da350 100644 --- a/src/state.cc +++ b/src/state.cc @@ -35,7 +35,7 @@ void Pool::EdgeFinished(const Edge& edge) { void Pool::DelayEdge(Edge* edge) { assert(depth_ != 0); - delayed_.push(edge); + delayed_.push_back(edge); } void Pool::RetrieveReadyEdges(set* ready_queue) { @@ -43,12 +43,22 @@ void Pool::RetrieveReadyEdges(set* ready_queue) { Edge* edge = delayed_.front(); if(current_use_ + edge->weight() > depth_) break; - delayed_.pop(); + delayed_.pop_front(); ready_queue->insert(edge); EdgeScheduled(*edge); } } +void Pool::Dump() const { + printf("%s (%d/%d) ->\n", name_.c_str(), current_use_, depth_); + for (deque::const_iterator it = delayed_.begin(); + it != delayed_.end(); ++it) + { + printf("\t"); + (*it)->Dump(); + } +} + Pool State::kDefaultPool("", 0); const Rule State::kPhonyRule("phony"); @@ -188,4 +198,12 @@ void State::Dump() { node->status_known() ? (node->dirty() ? "dirty" : "clean") : "unknown"); } + if(!pools_.empty()) { + printf("resource_pools:\n"); + for (map::const_iterator it = pools_.begin(); + it != pools_.end(); ++it) + { + it->second->Dump(); + } + } } diff --git a/src/state.h b/src/state.h index 349679a..c28407f 100644 --- a/src/state.h +++ b/src/state.h @@ -16,7 +16,7 @@ #define NINJA_STATE_H_ #include -#include +#include #include #include #include @@ -56,6 +56,9 @@ struct Pool { /// Pool will add zero or more edges to the ready_queue void RetrieveReadyEdges(set* ready_queue); + /// Dump the Pool and it's edges (useful for debugging). + void Dump() const; + private: int UnitsWaiting() { return delayed_.size(); } @@ -64,7 +67,7 @@ private: int current_use_; int depth_; - queue delayed_; + deque delayed_; }; /// Global state (file status, loaded rules) for a single run. @@ -94,7 +97,7 @@ struct State { /// state where we haven't yet examined the disk for dirty state. void Reset(); - /// Dump the nodes (useful for debugging). + /// Dump the nodes and Pools (useful for debugging). void Dump(); /// @return the root node(s) of the graph. (Root nodes have no output edges). -- cgit v0.12 From bd0ad99e7549386a8bb51e8f445312fe5c7a6679 Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Fri, 9 Nov 2012 15:47:06 -0800 Subject: all building and tests passing --- src/manifest_parser.cc | 2 +- src/state_test.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc index 8a010dd..d2c7f1c 100644 --- a/src/manifest_parser.cc +++ b/src/manifest_parser.cc @@ -266,7 +266,7 @@ bool ManifestParser::ParseEdge(string* err) { } while (lexer_.PeekToken(Lexer::INDENT)); } - Edge* edge = state_->AddEdge(rule, NULL); + Edge* edge = state_->AddEdge(rule, &State::kDefaultPool); edge->env_ = env; for (vector::iterator i = ins.begin(); i != ins.end(); ++i) { string path = i->Evaluate(env); diff --git a/src/state_test.cc b/src/state_test.cc index bc24edd..26177ff 100644 --- a/src/state_test.cc +++ b/src/state_test.cc @@ -32,7 +32,7 @@ TEST(State, Basic) { rule->set_command(command); state.AddRule(rule); - Edge* edge = state.AddEdge(rule); + Edge* edge = state.AddEdge(rule, &State::kDefaultPool); state.AddIn(edge, "in1"); state.AddIn(edge, "in2"); state.AddOut(edge, "out"); -- cgit v0.12 From a84d93cde00febb44be7918f6a44dc658d83d155 Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Thu, 27 Sep 2012 16:37:37 -0700 Subject: block parse method done --- src/graph.h | 1 + src/lexer.cc | 389 ++++++++++++++++++++++++++----------------------- src/lexer.h | 1 + src/lexer.in.cc | 2 + src/manifest_parser.cc | 68 ++++++++- src/manifest_parser.h | 1 + 6 files changed, 277 insertions(+), 185 deletions(-) diff --git a/src/graph.h b/src/graph.h index 9e924f7..4d24c72 100644 --- a/src/graph.h +++ b/src/graph.h @@ -131,6 +131,7 @@ struct Rule { EvalString command_; EvalString description_; EvalString depfile_; + EvalString pool_; EvalString rspfile_; EvalString rspfile_content_; }; diff --git a/src/lexer.cc b/src/lexer.cc index 5d7d185..685fe81 100644 --- a/src/lexer.cc +++ b/src/lexer.cc @@ -83,6 +83,7 @@ const char* Lexer::TokenName(Token t) { case NEWLINE: return "newline"; case PIPE2: return "'||'"; case PIPE: return "'|'"; + case POOL: return "'pool'"; case RULE: return "'rule'"; case SUBNINJA: return "'subninja'"; case TEOF: return "eof"; @@ -162,63 +163,71 @@ Lexer::Token Lexer::ReadToken() { }; yych = *p; - if (yych <= 'Z') { + if (yych <= '^') { if (yych <= ',') { if (yych <= 0x1F) { - if (yych <= 0x00) goto yy21; + if (yych <= 0x00) goto yy22; if (yych == '\n') goto yy6; - goto yy23; + goto yy24; } else { if (yych <= ' ') goto yy2; if (yych == '#') goto yy4; - goto yy23; + goto yy24; } } else { if (yych <= ':') { - if (yych == '/') goto yy23; - if (yych <= '9') goto yy20; - goto yy14; + if (yych == '/') goto yy24; + if (yych <= '9') goto yy21; + goto yy15; } else { - if (yych == '=') goto yy12; - if (yych <= '@') goto yy23; - goto yy20; + if (yych <= '=') { + if (yych <= '<') goto yy24; + goto yy13; + } else { + if (yych <= '@') goto yy24; + if (yych <= 'Z') goto yy21; + goto yy24; + } } } } else { - if (yych <= 'h') { - if (yych <= 'a') { - if (yych == '_') goto yy20; - if (yych <= '`') goto yy23; - goto yy20; + if (yych <= 'i') { + if (yych <= 'b') { + if (yych == '`') goto yy24; + if (yych <= 'a') goto yy21; + goto yy8; } else { - if (yych <= 'b') goto yy8; - if (yych == 'd') goto yy11; - goto yy20; + if (yych == 'd') goto yy12; + if (yych <= 'h') goto yy21; + goto yy19; } } else { - if (yych <= 's') { - if (yych <= 'i') goto yy18; - if (yych <= 'q') goto yy20; - if (yych <= 'r') goto yy10; - goto yy19; + if (yych <= 'r') { + if (yych == 'p') goto yy10; + if (yych <= 'q') goto yy21; + goto yy11; } else { - if (yych <= 'z') goto yy20; - if (yych == '|') goto yy16; - goto yy23; + if (yych <= 'z') { + if (yych <= 's') goto yy20; + goto yy21; + } else { + if (yych == '|') goto yy17; + goto yy24; + } } } } yy2: yyaccept = 0; yych = *(q = ++p); - goto yy65; + goto yy70; yy3: { token = INDENT; break; } yy4: yyaccept = 1; yych = *(q = ++p); if (yych <= 0x00) goto yy5; - if (yych != '\r') goto yy60; + if (yych != '\r') goto yy65; yy5: { token = ERROR; break; } yy6: @@ -227,159 +236,173 @@ yy7: { token = NEWLINE; break; } yy8: ++p; - if ((yych = *p) == 'u') goto yy54; - goto yy25; + if ((yych = *p) == 'u') goto yy59; + goto yy26; yy9: { token = IDENT; break; } yy10: yych = *++p; - if (yych == 'u') goto yy50; - goto yy25; + if (yych == 'o') goto yy55; + goto yy26; yy11: yych = *++p; - if (yych == 'e') goto yy43; - goto yy25; + if (yych == 'u') goto yy51; + goto yy26; yy12: + yych = *++p; + if (yych == 'e') goto yy44; + goto yy26; +yy13: ++p; { token = EQUALS; break; } -yy14: +yy15: ++p; { token = COLON; break; } -yy16: +yy17: ++p; - if ((yych = *p) == '|') goto yy41; + if ((yych = *p) == '|') goto yy42; { token = PIPE; break; } -yy18: - yych = *++p; - if (yych == 'n') goto yy34; - goto yy25; yy19: yych = *++p; - if (yych == 'u') goto yy26; - goto yy25; + if (yych == 'n') goto yy35; + goto yy26; yy20: yych = *++p; - goto yy25; + if (yych == 'u') goto yy27; + goto yy26; yy21: + yych = *++p; + goto yy26; +yy22: ++p; { token = TEOF; break; } -yy23: +yy24: yych = *++p; goto yy5; -yy24: +yy25: ++p; yych = *p; -yy25: +yy26: if (yybm[0+yych] & 32) { - goto yy24; + goto yy25; } goto yy9; -yy26: +yy27: yych = *++p; - if (yych != 'b') goto yy25; + if (yych != 'b') goto yy26; yych = *++p; - if (yych != 'n') goto yy25; + if (yych != 'n') goto yy26; yych = *++p; - if (yych != 'i') goto yy25; + if (yych != 'i') goto yy26; yych = *++p; - if (yych != 'n') goto yy25; + if (yych != 'n') goto yy26; yych = *++p; - if (yych != 'j') goto yy25; + if (yych != 'j') goto yy26; yych = *++p; - if (yych != 'a') goto yy25; + if (yych != 'a') goto yy26; ++p; if (yybm[0+(yych = *p)] & 32) { - goto yy24; + goto yy25; } { token = SUBNINJA; break; } -yy34: +yy35: yych = *++p; - if (yych != 'c') goto yy25; + if (yych != 'c') goto yy26; yych = *++p; - if (yych != 'l') goto yy25; + if (yych != 'l') goto yy26; yych = *++p; - if (yych != 'u') goto yy25; + if (yych != 'u') goto yy26; yych = *++p; - if (yych != 'd') goto yy25; + if (yych != 'd') goto yy26; yych = *++p; - if (yych != 'e') goto yy25; + if (yych != 'e') goto yy26; ++p; if (yybm[0+(yych = *p)] & 32) { - goto yy24; + goto yy25; } { token = INCLUDE; break; } -yy41: +yy42: ++p; { token = PIPE2; break; } -yy43: +yy44: yych = *++p; - if (yych != 'f') goto yy25; + if (yych != 'f') goto yy26; yych = *++p; - if (yych != 'a') goto yy25; + if (yych != 'a') goto yy26; yych = *++p; - if (yych != 'u') goto yy25; + if (yych != 'u') goto yy26; yych = *++p; - if (yych != 'l') goto yy25; + if (yych != 'l') goto yy26; yych = *++p; - if (yych != 't') goto yy25; + if (yych != 't') goto yy26; ++p; if (yybm[0+(yych = *p)] & 32) { - goto yy24; + goto yy25; } { token = DEFAULT; break; } -yy50: +yy51: yych = *++p; - if (yych != 'l') goto yy25; + if (yych != 'l') goto yy26; yych = *++p; - if (yych != 'e') goto yy25; + if (yych != 'e') goto yy26; ++p; if (yybm[0+(yych = *p)] & 32) { - goto yy24; + goto yy25; } { token = RULE; break; } -yy54: +yy55: yych = *++p; - if (yych != 'i') goto yy25; + if (yych != 'o') goto yy26; yych = *++p; - if (yych != 'l') goto yy25; + if (yych != 'l') goto yy26; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy25; + } + { token = POOL; break; } +yy59: + yych = *++p; + if (yych != 'i') goto yy26; yych = *++p; - if (yych != 'd') goto yy25; + if (yych != 'l') goto yy26; + yych = *++p; + if (yych != 'd') goto yy26; ++p; if (yybm[0+(yych = *p)] & 32) { - goto yy24; + goto yy25; } { token = BUILD; break; } -yy59: +yy64: ++p; yych = *p; -yy60: +yy65: if (yybm[0+yych] & 64) { - goto yy59; + goto yy64; } - if (yych <= 0x00) goto yy61; - if (yych <= '\f') goto yy62; -yy61: + if (yych <= 0x00) goto yy66; + if (yych <= '\f') goto yy67; +yy66: p = q; if (yyaccept <= 0) { goto yy3; } else { goto yy5; } -yy62: +yy67: ++p; { continue; } -yy64: +yy69: yyaccept = 0; q = ++p; yych = *p; -yy65: +yy70: if (yybm[0+yych] & 128) { - goto yy64; + goto yy69; } - if (yych == '\n') goto yy66; - if (yych == '#') goto yy59; + if (yych == '\n') goto yy71; + if (yych == '#') goto yy64; goto yy3; -yy66: +yy71: ++p; yych = *p; goto yy7; @@ -445,39 +468,39 @@ void Lexer::EatWhitespace() { }; yych = *p; if (yych <= ' ') { - if (yych <= 0x00) goto yy73; - if (yych <= 0x1F) goto yy75; + if (yych <= 0x00) goto yy78; + if (yych <= 0x1F) goto yy80; } else { - if (yych == '$') goto yy71; - goto yy75; + if (yych == '$') goto yy76; + goto yy80; } ++p; yych = *p; - goto yy79; -yy70: + goto yy84; +yy75: { continue; } -yy71: +yy76: ++p; - if ((yych = *p) == '\n') goto yy76; -yy72: + if ((yych = *p) == '\n') goto yy81; +yy77: { break; } -yy73: +yy78: ++p; { break; } -yy75: +yy80: yych = *++p; - goto yy72; -yy76: + goto yy77; +yy81: ++p; { continue; } -yy78: +yy83: ++p; yych = *p; -yy79: +yy84: if (yybm[0+yych] & 128) { - goto yy78; + goto yy83; } - goto yy70; + goto yy75; } } @@ -527,40 +550,40 @@ bool Lexer::ReadIdent(string* out) { yych = *p; if (yych <= '@') { if (yych <= '.') { - if (yych <= ',') goto yy84; + if (yych <= ',') goto yy89; } else { - if (yych <= '/') goto yy84; - if (yych >= ':') goto yy84; + if (yych <= '/') goto yy89; + if (yych >= ':') goto yy89; } } else { if (yych <= '_') { - if (yych <= 'Z') goto yy82; - if (yych <= '^') goto yy84; + if (yych <= 'Z') goto yy87; + if (yych <= '^') goto yy89; } else { - if (yych <= '`') goto yy84; - if (yych >= '{') goto yy84; + if (yych <= '`') goto yy89; + if (yych >= '{') goto yy89; } } -yy82: +yy87: ++p; yych = *p; - goto yy87; -yy83: + goto yy92; +yy88: { out->assign(start, p - start); break; } -yy84: +yy89: ++p; { return false; } -yy86: +yy91: ++p; yych = *p; -yy87: +yy92: if (yybm[0+yych] & 128) { - goto yy86; + goto yy91; } - goto yy83; + goto yy88; } } @@ -615,29 +638,29 @@ bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) { yych = *p; if (yych <= ' ') { if (yych <= '\n') { - if (yych <= 0x00) goto yy96; - if (yych >= '\n') goto yy92; + if (yych <= 0x00) goto yy101; + if (yych >= '\n') goto yy97; } else { - if (yych == '\r') goto yy98; - if (yych >= ' ') goto yy92; + if (yych == '\r') goto yy103; + if (yych >= ' ') goto yy97; } } else { if (yych <= '9') { - if (yych == '$') goto yy94; + if (yych == '$') goto yy99; } else { - if (yych <= ':') goto yy92; - if (yych == '|') goto yy92; + if (yych <= ':') goto yy97; + if (yych == '|') goto yy97; } } ++p; yych = *p; - goto yy121; -yy91: + goto yy126; +yy96: { eval->AddText(StringPiece(start, p - start)); continue; } -yy92: +yy97: ++p; { if (path) { @@ -650,137 +673,137 @@ yy92: continue; } } -yy94: +yy99: ++p; if ((yych = *p) <= '/') { if (yych <= ' ') { - if (yych == '\n') goto yy110; - if (yych <= 0x1F) goto yy99; - goto yy101; + if (yych == '\n') goto yy115; + if (yych <= 0x1F) goto yy104; + goto yy106; } else { if (yych <= '$') { - if (yych <= '#') goto yy99; - goto yy103; + if (yych <= '#') goto yy104; + goto yy108; } else { - if (yych == '-') goto yy105; - goto yy99; + if (yych == '-') goto yy110; + goto yy104; } } } else { if (yych <= '^') { if (yych <= ':') { - if (yych <= '9') goto yy105; - goto yy107; + if (yych <= '9') goto yy110; + goto yy112; } else { - if (yych <= '@') goto yy99; - if (yych <= 'Z') goto yy105; - goto yy99; + if (yych <= '@') goto yy104; + if (yych <= 'Z') goto yy110; + goto yy104; } } else { if (yych <= '`') { - if (yych <= '_') goto yy105; - goto yy99; + if (yych <= '_') goto yy110; + goto yy104; } else { - if (yych <= 'z') goto yy105; - if (yych <= '{') goto yy109; - goto yy99; + if (yych <= 'z') goto yy110; + if (yych <= '{') goto yy114; + goto yy104; } } } -yy95: +yy100: { last_token_ = start; return Error(DescribeLastError(), err); } -yy96: +yy101: ++p; { last_token_ = start; return Error("unexpected EOF", err); } -yy98: +yy103: yych = *++p; - goto yy95; -yy99: + goto yy100; +yy104: ++p; -yy100: +yy105: { last_token_ = start; return Error("bad $-escape (literal $ must be written as $$)", err); } -yy101: +yy106: ++p; { eval->AddText(StringPiece(" ", 1)); continue; } -yy103: +yy108: ++p; { eval->AddText(StringPiece("$", 1)); continue; } -yy105: +yy110: ++p; yych = *p; - goto yy119; -yy106: + goto yy124; +yy111: { eval->AddSpecial(StringPiece(start + 1, p - start - 1)); continue; } -yy107: +yy112: ++p; { eval->AddText(StringPiece(":", 1)); continue; } -yy109: +yy114: yych = *(q = ++p); if (yybm[0+yych] & 32) { - goto yy113; + goto yy118; } - goto yy100; -yy110: + goto yy105; +yy115: ++p; yych = *p; if (yybm[0+yych] & 16) { - goto yy110; + goto yy115; } { continue; } -yy113: +yy118: ++p; yych = *p; if (yybm[0+yych] & 32) { - goto yy113; + goto yy118; } - if (yych == '}') goto yy116; + if (yych == '}') goto yy121; p = q; - goto yy100; -yy116: + goto yy105; +yy121: ++p; { eval->AddSpecial(StringPiece(start + 2, p - start - 3)); continue; } -yy118: +yy123: ++p; yych = *p; -yy119: +yy124: if (yybm[0+yych] & 64) { - goto yy118; + goto yy123; } - goto yy106; -yy120: + goto yy111; +yy125: ++p; yych = *p; -yy121: +yy126: if (yybm[0+yych] & 128) { - goto yy120; + goto yy125; } - goto yy91; + goto yy96; } } diff --git a/src/lexer.h b/src/lexer.h index 03c59f2..f366556 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -41,6 +41,7 @@ struct Lexer { NEWLINE, PIPE, PIPE2, + POOL, RULE, SUBNINJA, TEOF, diff --git a/src/lexer.in.cc b/src/lexer.in.cc index 7ae9c61..93d5540 100644 --- a/src/lexer.in.cc +++ b/src/lexer.in.cc @@ -82,6 +82,7 @@ const char* Lexer::TokenName(Token t) { case NEWLINE: return "newline"; case PIPE2: return "'||'"; case PIPE: return "'|'"; + case POOL: return "'pool'"; case RULE: return "'rule'"; case SUBNINJA: return "'subninja'"; case TEOF: return "eof"; @@ -135,6 +136,7 @@ Lexer::Token Lexer::ReadToken() { [ ]*[\n] { token = NEWLINE; break; } [ ]+ { token = INDENT; break; } "build" { token = BUILD; break; } + "pool" { token = POOL; break; } "rule" { token = RULE; break; } "default" { token = DEFAULT; break; } "=" { token = EQUALS; break; } diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc index d2c7f1c..9ab973f 100644 --- a/src/manifest_parser.cc +++ b/src/manifest_parser.cc @@ -47,6 +47,10 @@ bool ManifestParser::Parse(const string& filename, const string& input, for (;;) { Lexer::Token token = lexer_.ReadToken(); switch (token) { + case Lexer::POOL: + if (!ParsePool(err)) + return false; + break; case Lexer::BUILD: if (!ParseEdge(err)) return false; @@ -91,6 +95,46 @@ bool ManifestParser::Parse(const string& filename, const string& input, return false; // not reached } + +bool ManifestParser::ParsePool(string* err) { + string name; + if (!lexer_.ReadIdent(&name)) + return lexer_.Error("expected pool name", err); + + if (!ExpectToken(Lexer::NEWLINE, err)) + return false; + + if (state_->LookupPool(name) != NULL) + return lexer_.Error("duplicate pool '" + name + "'", err); + + Pool* pool = new Pool(name); + bool set_depth = false; + + while (lexer_.PeekToken(Lexer::INDENT)) { + string key; + EvalString value; + if (!ParseLet(&key, &value, err)) + return false; + + if (key == "depth") { + string depth_string = value.Evaluate(env_); + pool->depth_ = atol(depth_string.c_str()); + if (pool->depth() <= 0) + return lexer_.Error("invalid pool depth", err); + set_depth = true; + } else { + return lexer_.Error("unexpected variable '" + key + "'", err); + } + } + + if (!set_depth) + return lexer_.Error("expected 'depth =' line", err); + + state_->AddPool(pool); + return true; +} + + bool ManifestParser::ParseRule(string* err) { string name; if (!lexer_.ReadIdent(&name)) @@ -126,6 +170,8 @@ bool ManifestParser::ParseRule(string* err) { rule->rspfile_ = value; } else if (key == "rspfile_content") { rule->rspfile_content_ = value; + } else if (key == "pool") { + rule->pool_ = value; } else { // Die on other keyvals for now; revisit if we want to add a // scope here. @@ -252,6 +298,7 @@ bool ManifestParser::ParseEdge(string* err) { // Default to using outer env. BindingEnv* env = env_; + Pool* pool = NULL; // But create and fill a nested env if there are variables in scope. if (lexer_.PeekToken(Lexer::INDENT)) { @@ -262,11 +309,28 @@ bool ManifestParser::ParseEdge(string* err) { EvalString val; if (!ParseLet(&key, &val, err)) return false; - env->AddBinding(key, val.Evaluate(env_)); + if (key == "pool") { + string pool_name = val.Evaluate(env_); + pool = state_->LookupPool(pool_name); + if (pool == NULL) + return lexer_.Error("undefined pool '" + pool_name + "'", err); + } else { + env->AddBinding(key, val.Evaluate(env_)); + } } while (lexer_.PeekToken(Lexer::INDENT)); } - Edge* edge = state_->AddEdge(rule, &State::kDefaultPool); + if (pool == NULL) { + if (!rule->pool_.empty()) { + pool = state_->LookupPool(rule->pool_.Evaluate(env_)); + if (pool == NULL) + return lexer_.Error("cannot resolve pool for this edge.", err); + } else { + pool = &State::kDefaultPool; + } + } + + Edge* edge = state_->AddEdge(rule, pool); edge->env_ = env; for (vector::iterator i = ins.begin(); i != ins.end(); ++i) { string path = i->Evaluate(env); diff --git a/src/manifest_parser.h b/src/manifest_parser.h index a2c6c93..a08e5af 100644 --- a/src/manifest_parser.h +++ b/src/manifest_parser.h @@ -50,6 +50,7 @@ private: bool Parse(const string& filename, const string& input, string* err); /// Parse various statement types. + bool ParsePool(string* err); bool ParseRule(string* err); bool ParseLet(string* key, EvalString* val, string* err); bool ParseEdge(string* err); -- cgit v0.12 From 6a2f66d3ec8d8f2827c36b61dc64402c6ee5c8c2 Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Mon, 1 Oct 2012 13:07:42 -0700 Subject: begin rationalizing platform for both parsers --- src/manifest_parser.cc | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc index 9ab973f..271b841 100644 --- a/src/manifest_parser.cc +++ b/src/manifest_parser.cc @@ -107,8 +107,7 @@ bool ManifestParser::ParsePool(string* err) { if (state_->LookupPool(name) != NULL) return lexer_.Error("duplicate pool '" + name + "'", err); - Pool* pool = new Pool(name); - bool set_depth = false; + int depth = -1; while (lexer_.PeekToken(Lexer::INDENT)) { string key; @@ -118,19 +117,18 @@ bool ManifestParser::ParsePool(string* err) { if (key == "depth") { string depth_string = value.Evaluate(env_); - pool->depth_ = atol(depth_string.c_str()); - if (pool->depth() <= 0) + depth = atol(depth_string.c_str()); + if (depth < 0) return lexer_.Error("invalid pool depth", err); - set_depth = true; } else { return lexer_.Error("unexpected variable '" + key + "'", err); } } - if (!set_depth) + if (depth < 0) return lexer_.Error("expected 'depth =' line", err); - state_->AddPool(pool); + state_->AddPool(new Pool(name, depth)); return true; } -- cgit v0.12 From 9615853c249514050514ec04cb9bc1c76a660d1d Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Fri, 9 Nov 2012 21:12:48 -0800 Subject: Add some tests! --- src/build_test.cc | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 129 insertions(+), 3 deletions(-) diff --git a/src/build_test.cc b/src/build_test.cc index c208463..345878f 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -176,6 +176,131 @@ TEST_F(PlanTest, DependencyCycle) { ASSERT_EQ("dependency cycle: out -> mid -> in -> pre -> out", err); } +TEST_F(PlanTest, PoolWithDepthOne) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"pool foobar\n" +" depth = 1\n" +"rule poolcat\n" +" command = cat $in > $out\n" +" pool = foobar\n" +"build out1: poolcat in\n" +"build out2: poolcat in\n")); + GetNode("out1")->MarkDirty(); + GetNode("out2")->MarkDirty(); + string err; + EXPECT_TRUE(plan_.AddTarget(GetNode("out1"), &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(plan_.AddTarget(GetNode("out2"), &err)); + ASSERT_EQ("", err); + ASSERT_TRUE(plan_.more_to_do()); + + Edge* edge = plan_.FindWork(); + ASSERT_TRUE(edge); + ASSERT_EQ("in", edge->inputs_[0]->path()); + ASSERT_EQ("out1", edge->outputs_[0]->path()); + + // This will be false since poolcat is serialized + ASSERT_FALSE(plan_.FindWork()); + + plan_.EdgeFinished(edge); + + edge = plan_.FindWork(); + ASSERT_TRUE(edge); + ASSERT_EQ("in", edge->inputs_[0]->path()); + ASSERT_EQ("out2", edge->outputs_[0]->path()); + + ASSERT_FALSE(plan_.FindWork()); + + plan_.EdgeFinished(edge); + + ASSERT_FALSE(plan_.more_to_do()); + edge = plan_.FindWork(); + ASSERT_EQ(0, edge); +} + +TEST_F(PlanTest, PoolsWithDepthTwo) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"pool foobar\n" +" depth = 2\n" +"pool bazbin\n" +" depth = 2\n" +"rule foocat\n" +" command = cat $in > $out\n" +" pool = foobar\n" +"rule bazcat\n" +" command = cat $in > $out\n" +" pool = bazbin\n" +"build out1: foocat in\n" +"build out2: foocat in\n" +"build out3: foocat in\n" +"build outb1: bazcat in\n" +"build outb2: bazcat in\n" +"build outb3: bazcat in\n" +"build allTheThings: cat out1 out2 out3 outb1 outb2 outb3\n" +)); + // Mark all the out* nodes dirty + for(int i = 0; i < 3; ++i) { + GetNode("out"+string(1, '1'+i))->MarkDirty(); + GetNode("outb"+string(1, '1'+i))->MarkDirty(); + } + GetNode("allTheThings")->MarkDirty(); + + string err; + EXPECT_TRUE(plan_.AddTarget(GetNode("allTheThings"), &err)); + ASSERT_EQ("", err); + + // Grab the first 4 edges, out1 out2 outb1 outb2 + vector edges; + for(int i = 0; i < 4; ++i) { + ASSERT_TRUE(plan_.more_to_do()); + Edge* edge = plan_.FindWork(); + ASSERT_TRUE(edge); + ASSERT_EQ("in", edge->inputs_[0]->path()); + string base_name(i < 2 ? "out" : "outb"); + ASSERT_EQ(base_name+string(1, '1'+(i%2)), edge->outputs_[0]->path()); + edges.push_back(edge); + } + + ASSERT_FALSE(plan_.FindWork()); + + // finish outb2 + plan_.EdgeFinished(edges.back()); + edges.pop_back(); + + // outb3 should be available + Edge* outb3 = plan_.FindWork(); + ASSERT_TRUE(outb3); + ASSERT_EQ("in", outb3->inputs_[0]->path()); + ASSERT_EQ("outb3", outb3->outputs_[0]->path()); + + ASSERT_FALSE(plan_.FindWork()); + + plan_.EdgeFinished(outb3); + + ASSERT_FALSE(plan_.FindWork()); + + for(vector::iterator it = edges.begin(); it != edges.end(); ++it) { + plan_.EdgeFinished(*it); + } + + Edge* out3 = plan_.FindWork(); + ASSERT_TRUE(out3); + ASSERT_EQ("in", out3->inputs_[0]->path()); + ASSERT_EQ("out3", out3->outputs_[0]->path()); + + ASSERT_FALSE(plan_.FindWork()); + plan_.EdgeFinished(out3); + + Edge* final = plan_.FindWork(); + ASSERT_TRUE(final); + ASSERT_EQ("allTheThings", final->outputs_[0]->path()); + + plan_.EdgeFinished(final); + + ASSERT_FALSE(plan_.more_to_do()); + ASSERT_FALSE(plan_.FindWork()); +} + struct BuildTest : public StateTestWithBuiltinRules, public CommandRunner { BuildTest() : config_(MakeConfig()), @@ -869,7 +994,7 @@ TEST_F(BuildWithLogTest, RestatMissingInput) { // Create all necessary files fs_.Create("in", now_, ""); - // The implicit dependencies and the depfile itself + // The implicit dependencies and the depfile itself // are newer than the output TimeStamp restat_mtime = ++now_; fs_.Create("out1.d", now_, "out1: will.be.deleted restat.file\n"); @@ -889,10 +1014,10 @@ TEST_F(BuildWithLogTest, RestatMissingInput) { ASSERT_TRUE(NULL != log_entry); ASSERT_EQ(restat_mtime, log_entry->restat_mtime); - // Now remove a file, referenced from depfile, so that target becomes + // Now remove a file, referenced from depfile, so that target becomes // dirty, but the output does not change fs_.RemoveFile("will.be.deleted"); - + // Trigger the build again - only out1 gets built commands_ran_.clear(); state_.Reset(); @@ -1135,3 +1260,4 @@ TEST_F(BuildTest, StatusFormatReplacePlaceholder) { EXPECT_EQ("[%/s0/t0/r0/u0/f0]", status_.FormatProgressStatus("[%%/s%s/t%t/r%r/u%u/f%f]")); } + -- cgit v0.12 From f2eeca20f4a495d7c477bf5c7254095cf3b75c5d Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Fri, 9 Nov 2012 21:37:54 -0800 Subject: add docs --- doc/manual.asciidoc | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc index 03d27df..b01cdad 100644 --- a/doc/manual.asciidoc +++ b/doc/manual.asciidoc @@ -420,6 +420,57 @@ If the top-level Ninja file is specified as an output of any build statement and it is out of date, Ninja will rebuild and reload it before building the targets requested by the user. +Pools +~~~~~ + +Pools allow you to allocate one or more rules or edges a finite number +of concurrent jobs which is more tightly restricted than the total job +number that you specify to ninja on the command line. + +This can be useful, for example, to restrict a particular expensive rule +(like link steps for huge executables), or to restrict particular build +statements which you know preform poorly when run concurrently. + +Each pool has a `depth` variable which is specified in the build file. +The pool is then referred to with the `pool` variable on either a rule +or a build statement. + +The jobs amount specified on the command line is always respected, no +matter what pools you've set up. + +---------------- +# No more than 4 links at a time +pool link_pool + depth = 4 + +# No more than 1 heavy object at a time +pool heavy_object_pool + depth = 1 + +rule link + ... + pool = link_pool + +rule cc + ... + +# The link_pool is used here. Only 4 links will run concurrently. +build foo.exe: link input.obj + +# A build statement can be exempted from it's rule's pool by setting an +# empty pool +build other.exe: link input.obj + pool = + +# A build statement can specify a pool even if its rule does not +# Only one of these builds will run at a time. +build heavy_object1.obj: cc heavy_obj1.cc + pool = heavy_object_pool +build heavy_object2.obj: cc heavy_obj2.cc + pool = heavy_object_pool + +---------------- + Generating Ninja files from code ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- cgit v0.12 From 0daed0f865fc5fca6b83610db438edefe21dbce1 Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Fri, 9 Nov 2012 21:46:45 -0800 Subject: cover the nulled pool case --- src/build_test.cc | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/build_test.cc b/src/build_test.cc index 345878f..34db237 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -236,6 +236,7 @@ TEST_F(PlanTest, PoolsWithDepthTwo) { "build outb1: bazcat in\n" "build outb2: bazcat in\n" "build outb3: bazcat in\n" +" pool =\n" "build allTheThings: cat out1 out2 out3 outb1 outb2 outb3\n" )); // Mark all the out* nodes dirty @@ -250,7 +251,7 @@ TEST_F(PlanTest, PoolsWithDepthTwo) { ASSERT_EQ("", err); // Grab the first 4 edges, out1 out2 outb1 outb2 - vector edges; + deque edges; for(int i = 0; i < 4; ++i) { ASSERT_TRUE(plan_.more_to_do()); Edge* edge = plan_.FindWork(); @@ -261,36 +262,36 @@ TEST_F(PlanTest, PoolsWithDepthTwo) { edges.push_back(edge); } - ASSERT_FALSE(plan_.FindWork()); - - // finish outb2 - plan_.EdgeFinished(edges.back()); - edges.pop_back(); - - // outb3 should be available - Edge* outb3 = plan_.FindWork(); - ASSERT_TRUE(outb3); - ASSERT_EQ("in", outb3->inputs_[0]->path()); - ASSERT_EQ("outb3", outb3->outputs_[0]->path()); - - ASSERT_FALSE(plan_.FindWork()); - - plan_.EdgeFinished(outb3); + // outb3 is exempt because it has an empty pool + ASSERT_TRUE(plan_.more_to_do()); + Edge* edge = plan_.FindWork(); + ASSERT_TRUE(edge); + ASSERT_EQ("in", edge->inputs_[0]->path()); + ASSERT_EQ("outb3", edge->outputs_[0]->path()); + edges.push_back(edge); ASSERT_FALSE(plan_.FindWork()); - for(vector::iterator it = edges.begin(); it != edges.end(); ++it) { - plan_.EdgeFinished(*it); - } + // finish out1 + plan_.EdgeFinished(edges.front()); + edges.pop_front(); + // out3 should be available Edge* out3 = plan_.FindWork(); ASSERT_TRUE(out3); ASSERT_EQ("in", out3->inputs_[0]->path()); ASSERT_EQ("out3", out3->outputs_[0]->path()); ASSERT_FALSE(plan_.FindWork()); + plan_.EdgeFinished(out3); + ASSERT_FALSE(plan_.FindWork()); + + for(deque::iterator it = edges.begin(); it != edges.end(); ++it) { + plan_.EdgeFinished(*it); + } + Edge* final = plan_.FindWork(); ASSERT_TRUE(final); ASSERT_EQ("allTheThings", final->outputs_[0]->path()); -- cgit v0.12 From c068fcc4fd3fc9d83b58fceb7a59497c86e10e11 Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Sat, 10 Nov 2012 11:35:02 -0800 Subject: Make edge dump pool name, and skip default pool --- src/graph.cc | 7 +++++++ src/state.cc | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/graph.cc b/src/graph.cc index 3550d48..f8ceda9 100644 --- a/src/graph.cc +++ b/src/graph.cc @@ -345,6 +345,13 @@ void Edge::Dump(const char* prefix) const { i != outputs_.end() && *i != NULL; ++i) { printf("%s ", (*i)->path().c_str()); } + if (pool_) { + if (!pool_->name().empty()) { + printf("(in pool '%s')", pool_->name().c_str()); + } + } else { + printf("(null pool?)"); + } printf("] 0x%p\n", this); } diff --git a/src/state.cc b/src/state.cc index b0da350..0fb696e 100644 --- a/src/state.cc +++ b/src/state.cc @@ -203,7 +203,9 @@ void State::Dump() { for (map::const_iterator it = pools_.begin(); it != pools_.end(); ++it) { - it->second->Dump(); + if (!it->second->name().empty()) { + it->second->Dump(); + } } } } -- cgit v0.12 From 9b196dc806e57cefd88bbbacd12286447dbf9ad9 Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Sat, 10 Nov 2012 11:45:54 -0800 Subject: Dump state for debugging --- src/build_test.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/build_test.cc b/src/build_test.cc index 34db237..2d1d1a2 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -194,6 +194,8 @@ TEST_F(PlanTest, PoolWithDepthOne) { ASSERT_EQ("", err); ASSERT_TRUE(plan_.more_to_do()); + plan_.Dump(); + state_.Dump(); Edge* edge = plan_.FindWork(); ASSERT_TRUE(edge); ASSERT_EQ("in", edge->inputs_[0]->path()); @@ -250,6 +252,8 @@ TEST_F(PlanTest, PoolsWithDepthTwo) { EXPECT_TRUE(plan_.AddTarget(GetNode("allTheThings"), &err)); ASSERT_EQ("", err); + plan_.Dump(); + state_.Dump(); // Grab the first 4 edges, out1 out2 outb1 outb2 deque edges; for(int i = 0; i < 4; ++i) { -- cgit v0.12 From 170066f8c169068c49203d91f2033837e517685e Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Sat, 10 Nov 2012 11:54:13 -0800 Subject: Uninitialized variable! There is always one... --- src/state.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state.h b/src/state.h index c28407f..170a5fc 100644 --- a/src/state.h +++ b/src/state.h @@ -32,7 +32,7 @@ struct Rule; /// A pool for delayed edges struct Pool { explicit Pool(const string& name, int depth) - : name_(name), depth_(depth) { } + : name_(name), current_use_(0), depth_(depth) { } // A depth of 0 is infinite bool isValid() const { return depth_ >= 0; } -- cgit v0.12 From e3959306762b544a0a01fd2161c52f3bd68e76a2 Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Sat, 10 Nov 2012 11:58:04 -0800 Subject: Revert "Dump state for debugging" This reverts commit 9b196dc806e57cefd88bbbacd12286447dbf9ad9. --- src/build_test.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/build_test.cc b/src/build_test.cc index 2d1d1a2..34db237 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -194,8 +194,6 @@ TEST_F(PlanTest, PoolWithDepthOne) { ASSERT_EQ("", err); ASSERT_TRUE(plan_.more_to_do()); - plan_.Dump(); - state_.Dump(); Edge* edge = plan_.FindWork(); ASSERT_TRUE(edge); ASSERT_EQ("in", edge->inputs_[0]->path()); @@ -252,8 +250,6 @@ TEST_F(PlanTest, PoolsWithDepthTwo) { EXPECT_TRUE(plan_.AddTarget(GetNode("allTheThings"), &err)); ASSERT_EQ("", err); - plan_.Dump(); - state_.Dump(); // Grab the first 4 edges, out1 out2 outb1 outb2 deque edges; for(int i = 0; i < 4; ++i) { -- cgit v0.12 From f93f1309b451f4c00fd0d68b4736130135f6586d Mon Sep 17 00:00:00 2001 From: Thiago Farina Date: Tue, 27 Nov 2012 15:59:08 -0200 Subject: Fix clang warning. The return type of CollectTarget() is Node, so we should return NULL in the failure case instead of false. src/ninja.cc:188:16: warning: initialization of pointer of type 'Node *' to null from a constant boolean expression [-Wbool-conversion] return false; ^~~~~ Signed-off-by: Thiago Farina --- src/ninja.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ninja.cc b/src/ninja.cc index 560e7d4..38f1d78 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -185,7 +185,7 @@ Node* CollectTarget(State* state, const char* cpath, string* err) { if (first_dependent) { if (node->out_edges().empty()) { *err = "'" + path + "' has no out edge"; - return false; + return NULL; } Edge* edge = node->out_edges()[0]; if (edge->outputs_.empty()) { -- cgit v0.12 From 3311c32f3e5b405a84af7decfa168f33a250f11b Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Thu, 29 Nov 2012 16:28:44 -0800 Subject: Improve the manual documentation --- doc/manual.asciidoc | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc index b01cdad..666f0a5 100644 --- a/doc/manual.asciidoc +++ b/doc/manual.asciidoc @@ -424,26 +424,27 @@ Pools ~~~~~ Pools allow you to allocate one or more rules or edges a finite number -of concurrent jobs which is more tightly restricted than the total job -number that you specify to ninja on the command line. +of concurrent jobs which is more tightly restricted than the default +parallelism. This can be useful, for example, to restrict a particular expensive rule (like link steps for huge executables), or to restrict particular build -statements which you know preform poorly when run concurrently. +statements which you know perform poorly when run concurrently. Each pool has a `depth` variable which is specified in the build file. The pool is then referred to with the `pool` variable on either a rule or a build statement. -The jobs amount specified on the command line is always respected, no -matter what pools you've set up. +No matter what pools you specify, ninja will never run more concurrent jobs +than the default parallelism, or the number of jobs specified on the command +line (with -j). ---------------- -# No more than 4 links at a time +# No more than 4 links at a time. pool link_pool depth = 4 -# No more than 1 heavy object at a time +# No more than 1 heavy object at a time. pool heavy_object_pool depth = 1 @@ -457,12 +458,13 @@ rule cc # The link_pool is used here. Only 4 links will run concurrently. build foo.exe: link input.obj -# A build statement can be exempted from it's rule's pool by setting an -# empty pool +# A build statement can be exempted from its rule's pool by setting an +# empty pool. This effectively puts the build statement back into the default +# pool, which has infinite depth. build other.exe: link input.obj pool = -# A build statement can specify a pool even if its rule does not +# A build statement can specify a pool directly. # Only one of these builds will run at a time. build heavy_object1.obj: cc heavy_obj1.cc pool = heavy_object_pool -- cgit v0.12 From 02b9cbaea9d41bb9228aebbc1fe8c53ccdb4744c Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Thu, 29 Nov 2012 16:36:16 -0800 Subject: Make Edge->pool() a pointer like it should have been --- src/build.cc | 14 +++++++------- src/graph.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/build.cc b/src/build.cc index cadf7dc..eacac50 100644 --- a/src/build.cc +++ b/src/build.cc @@ -409,19 +409,19 @@ Edge* Plan::FindWork() { } void Plan::ScheduleWork(Edge* edge) { - Pool& pool = edge->pool(); - if (pool.ShouldDelayEdge(*edge)) { - pool.DelayEdge(edge); - pool.RetrieveReadyEdges(&ready_); + Pool* pool = edge->pool(); + if (pool->ShouldDelayEdge(*edge)) { + pool->DelayEdge(edge); + pool->RetrieveReadyEdges(&ready_); } else { - pool.EdgeScheduled(*edge); + pool->EdgeScheduled(*edge); ready_.insert(edge); } } void Plan::ResumeDelayedJobs(Edge* edge) { - edge->pool().EdgeFinished(*edge); - edge->pool().RetrieveReadyEdges(&ready_); + edge->pool()->EdgeFinished(*edge); + edge->pool()->RetrieveReadyEdges(&ready_); } void Plan::EdgeFinished(Edge* edge) { diff --git a/src/graph.h b/src/graph.h index 4d24c72..ce92679 100644 --- a/src/graph.h +++ b/src/graph.h @@ -175,7 +175,7 @@ struct Edge { bool outputs_ready_; const Rule& rule() const { return *rule_; } - Pool& pool() const { return *pool_; } + Pool* pool() const { return pool_; } int weight() const { return 1; } bool outputs_ready() const { return outputs_ready_; } -- cgit v0.12 From d21f528e0a91412a4a6d0f64722e7f437e0ee501 Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Thu, 29 Nov 2012 16:49:04 -0800 Subject: Doc improvements --- src/build.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/build.h b/src/build.h index 6bcfc07..c60b893 100644 --- a/src/build.h +++ b/src/build.h @@ -69,7 +69,15 @@ private: bool AddSubTarget(Node* node, vector* stack, string* err); bool CheckDependencyCycle(Node* node, vector* stack, string* err); void NodeFinished(Node* node); + + /// Submits a ready edge as a candidate for execution. + /// The edge may be delayed from running, for example if it's a member of a + /// currently-full pool. void ScheduleWork(Edge* edge); + + /// Allows jobs blocking on |edge| to potentially resume. + /// For example, if |edge| is a member of a pool, calling this may schedule + /// previously pending jobs in that pool. void ResumeDelayedJobs(Edge* edge); /// Keep track of which edges we want to build in this plan. If this map does -- cgit v0.12 From 14b3e108edc4a5476a1c2a6b8ae69f6416bca0de Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Thu, 29 Nov 2012 17:07:50 -0800 Subject: Improve comments for src/state.h --- src/state.h | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/state.h b/src/state.h index 170a5fc..4b644db 100644 --- a/src/state.h +++ b/src/state.h @@ -29,7 +29,14 @@ struct Edge; struct Node; struct Rule; -/// A pool for delayed edges +/// A pool for delayed edges. +/// Pools are scoped to a State. Edges within a State will share Pools. A Pool +/// will keep a count of the total 'weight' of the currently scheduled edges. If +/// a Plan attempts to schedule an Edge which would cause the total weight to +/// exceed the depth of the Pool, the Pool will enque the Edge instead of +/// allowing the Plan to schedule it. The Pool will relinquish queued Edges when +/// the total scheduled weight diminishes enough (i.e. when a scheduled edge +/// completes). struct Pool { explicit Pool(const string& name, int depth) : name_(name), current_use_(0), depth_(depth) { } @@ -47,7 +54,7 @@ struct Pool { void EdgeScheduled(const Edge& edge); /// informs this Pool that the given edge is no longer runnable, and should - /// relinquish it's resources back to the pool + /// relinquish its resources back to the pool void EdgeFinished(const Edge& edge); /// adds the given edge to this Pool to be delayed. @@ -56,7 +63,7 @@ struct Pool { /// Pool will add zero or more edges to the ready_queue void RetrieveReadyEdges(set* ready_queue); - /// Dump the Pool and it's edges (useful for debugging). + /// Dump the Pool and its edges (useful for debugging). void Dump() const; private: @@ -64,6 +71,8 @@ private: string name_; + /// |current_use_| is the total of the weights of the edges which are + /// currently scheduled in the Plan (i.e. the edges in Plan::ready_). int current_use_; int depth_; -- cgit v0.12 From cc531200853eea49b4b7e70cbabf394a9c864c79 Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Thu, 29 Nov 2012 17:47:22 -0800 Subject: Fix formatting --- src/build_log.cc | 4 ++-- src/build_test.cc | 12 ++++++------ src/hash_map.h | 4 ++-- src/state.cc | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/build_log.cc b/src/build_log.cc index 235951f..f53ccdc 100644 --- a/src/build_log.cc +++ b/src/build_log.cc @@ -56,7 +56,7 @@ uint64_t MurmurHash64A(const void* key, size_t len) { uint64_t h = seed ^ (len * m); const uint64_t * data = (const uint64_t *)key; const uint64_t * end = data + (len/8); - while(data != end) { + while (data != end) { uint64_t k = *data++; k *= m; k ^= k >> r; @@ -65,7 +65,7 @@ uint64_t MurmurHash64A(const void* key, size_t len) { h *= m; } const unsigned char* data2 = (const unsigned char*)data; - switch(len & 7) + switch (len & 7) { case 7: h ^= uint64_t(data2[6]) << 48; case 6: h ^= uint64_t(data2[5]) << 40; diff --git a/src/build_test.cc b/src/build_test.cc index 34db237..17e433b 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -240,9 +240,9 @@ TEST_F(PlanTest, PoolsWithDepthTwo) { "build allTheThings: cat out1 out2 out3 outb1 outb2 outb3\n" )); // Mark all the out* nodes dirty - for(int i = 0; i < 3; ++i) { - GetNode("out"+string(1, '1'+i))->MarkDirty(); - GetNode("outb"+string(1, '1'+i))->MarkDirty(); + for (int i = 0; i < 3; ++i) { + GetNode("out" + string(1, '1' + i))->MarkDirty(); + GetNode("outb" + string(1, '1' + i))->MarkDirty(); } GetNode("allTheThings")->MarkDirty(); @@ -252,13 +252,13 @@ TEST_F(PlanTest, PoolsWithDepthTwo) { // Grab the first 4 edges, out1 out2 outb1 outb2 deque edges; - for(int i = 0; i < 4; ++i) { + for (int i = 0; i < 4; ++i) { ASSERT_TRUE(plan_.more_to_do()); Edge* edge = plan_.FindWork(); ASSERT_TRUE(edge); ASSERT_EQ("in", edge->inputs_[0]->path()); string base_name(i < 2 ? "out" : "outb"); - ASSERT_EQ(base_name+string(1, '1'+(i%2)), edge->outputs_[0]->path()); + ASSERT_EQ(base_name + string(1, '1' + (i % 2)), edge->outputs_[0]->path()); edges.push_back(edge); } @@ -288,7 +288,7 @@ TEST_F(PlanTest, PoolsWithDepthTwo) { ASSERT_FALSE(plan_.FindWork()); - for(deque::iterator it = edges.begin(); it != edges.end(); ++it) { + for (deque::iterator it = edges.begin(); it != edges.end(); ++it) { plan_.EdgeFinished(*it); } diff --git a/src/hash_map.h b/src/hash_map.h index 9904fb8..076f6c0 100644 --- a/src/hash_map.h +++ b/src/hash_map.h @@ -25,7 +25,7 @@ unsigned int MurmurHash2(const void* key, size_t len) { const int r = 24; unsigned int h = seed ^ len; const unsigned char * data = (const unsigned char *)key; - while(len >= 4) { + while (len >= 4) { unsigned int k = *(unsigned int *)data; k *= m; k ^= k >> r; @@ -35,7 +35,7 @@ unsigned int MurmurHash2(const void* key, size_t len) { data += 4; len -= 4; } - switch(len) { + switch (len) { case 3: h ^= data[2] << 16; case 2: h ^= data[1] << 8; case 1: h ^= data[0]; diff --git a/src/state.cc b/src/state.cc index 0fb696e..bb0cc15 100644 --- a/src/state.cc +++ b/src/state.cc @@ -39,9 +39,9 @@ void Pool::DelayEdge(Edge* edge) { } void Pool::RetrieveReadyEdges(set* ready_queue) { - while(!delayed_.empty()) { + while (!delayed_.empty()) { Edge* edge = delayed_.front(); - if(current_use_ + edge->weight() > depth_) + if (current_use_ + edge->weight() > depth_) break; delayed_.pop_front(); ready_queue->insert(edge); @@ -198,7 +198,7 @@ void State::Dump() { node->status_known() ? (node->dirty() ? "dirty" : "clean") : "unknown"); } - if(!pools_.empty()) { + if (!pools_.empty()) { printf("resource_pools:\n"); for (map::const_iterator it = pools_.begin(); it != pools_.end(); ++it) -- cgit v0.12 From 57086d5d99fa7e6e9415a84cd77358246a691a59 Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Thu, 29 Nov 2012 17:51:30 -0800 Subject: Rename isValid --- src/state.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state.h b/src/state.h index 4b644db..f1cc4c7 100644 --- a/src/state.h +++ b/src/state.h @@ -42,7 +42,7 @@ struct Pool { : name_(name), current_use_(0), depth_(depth) { } // A depth of 0 is infinite - bool isValid() const { return depth_ >= 0; } + bool is_valid() const { return depth_ >= 0; } int depth() const { return depth_; } const string& name() const { return name_; } -- cgit v0.12 From c18b15da63e7d759ba9c1b89825c625ac42ee248 Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Thu, 29 Nov 2012 17:53:30 -0800 Subject: Remove unnecessary parameter from ShouldDelayEdge --- src/build.cc | 2 +- src/state.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/build.cc b/src/build.cc index eacac50..93ab10d 100644 --- a/src/build.cc +++ b/src/build.cc @@ -410,7 +410,7 @@ Edge* Plan::FindWork() { void Plan::ScheduleWork(Edge* edge) { Pool* pool = edge->pool(); - if (pool->ShouldDelayEdge(*edge)) { + if (pool->ShouldDelayEdge()) { pool->DelayEdge(edge); pool->RetrieveReadyEdges(&ready_); } else { diff --git a/src/state.h b/src/state.h index f1cc4c7..918fe09 100644 --- a/src/state.h +++ b/src/state.h @@ -47,7 +47,7 @@ struct Pool { const string& name() const { return name_; } /// true if the Pool might delay this edge - bool ShouldDelayEdge(const Edge& edge) const { return depth_ != 0; } + bool ShouldDelayEdge() const { return depth_ != 0; } /// informs this Pool that the given edge is committed to be run. /// Pool will count this edge as using resources from this pool. -- cgit v0.12 From 2ea3638cb2e75197764584f7dd715cc97f68c8c5 Mon Sep 17 00:00:00 2001 From: Philip Puryear Date: Fri, 7 Dec 2012 18:22:19 -0600 Subject: browse: Read ninja's error text from stderr. --- src/browse.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/browse.py b/src/browse.py index 14d5edd..7f15e50 100755 --- a/src/browse.py +++ b/src/browse.py @@ -144,8 +144,9 @@ def generate_html(node): def ninja_dump(target): proc = subprocess.Popen([sys.argv[1], '-t', 'query', target], - stdout=subprocess.PIPE, universal_newlines=True) - return (proc.communicate()[0], proc.returncode) + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True) + return proc.communicate() + (proc.returncode,) class RequestHandler(httpserver.BaseHTTPRequestHandler): def do_GET(self): @@ -164,12 +165,12 @@ class RequestHandler(httpserver.BaseHTTPRequestHandler): return target = target[1:] - input, exit_code = ninja_dump(target) + ninja_output, ninja_error, exit_code = ninja_dump(target) if exit_code == 0: - page_body = generate_html(parse(input.strip())) + page_body = generate_html(parse(ninja_output.strip())) else: # Relay ninja's error message. - page_body = '

%s

' % input + page_body = '

%s

' % ninja_error self.send_response(200) self.end_headers() -- cgit v0.12 From f5129c8174a85b8f4abd2fedd219980b1f760410 Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Thu, 13 Dec 2012 23:11:37 -0800 Subject: Add python ninja_syntax.py support for pool --- misc/ninja_syntax.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/misc/ninja_syntax.py b/misc/ninja_syntax.py index 3572dd9..b0a1561 100644 --- a/misc/ninja_syntax.py +++ b/misc/ninja_syntax.py @@ -32,8 +32,13 @@ class Writer(object): value = ' '.join(filter(None, value)) # Filter out empty strings. self._line('%s = %s' % (key, value), indent) + def pool(self, name, depth): + self._line('pool %s' % name) + self.variable('depth', depth, indent=1) + def rule(self, name, command, description=None, depfile=None, - generator=False, restat=False, rspfile=None, rspfile_content=None): + generator=False, pool=None, restat=False, rspfile=None, + rspfile_content=None): self._line('rule %s' % name) self.variable('command', command, indent=1) if description: @@ -42,6 +47,8 @@ class Writer(object): self.variable('depfile', depfile, indent=1) if generator: self.variable('generator', '1', indent=1) + if pool: + self.variable('pool', pool, indent=1) if restat: self.variable('restat', '1', indent=1) if rspfile: -- cgit v0.12 From e735510fcf1dccc3ea0e7fafeb3dd3c6e37c1650 Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Thu, 13 Dec 2012 23:10:52 -0800 Subject: Add ninja.vim syntax for pool --- misc/ninja.vim | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/misc/ninja.vim b/misc/ninja.vim index 6f0e48d..6ec7c8b 100644 --- a/misc/ninja.vim +++ b/misc/ninja.vim @@ -25,6 +25,7 @@ syn match ninjaComment /#.*/ contains=@Spell " lexer.in.cc, ReadToken() and manifest_parser.cc, Parse() syn match ninjaKeyword "^build\>" syn match ninjaKeyword "^rule\>" +syn match ninjaKeyword "^pool\>" syn match ninjaKeyword "^default\>" syn match ninjaKeyword "^include\>" syn match ninjaKeyword "^subninja\>" @@ -35,7 +36,11 @@ syn match ninjaKeyword "^subninja\>" " let assignments. " manifest_parser.cc, ParseRule() syn region ninjaRule start="^rule" end="^\ze\S" contains=ALL transparent -syn keyword ninjaRuleCommand contained command depfile description generator restat +syn keyword ninjaRuleCommand contained command depfile description generator + \ pool restat + +syn region ninjaPool start="^pool" end="^\ze\S" contains=ALL transparent +syn keyword ninjaPoolCommand contained depth " Strings are parsed as follows: " lexer.in.cc, ReadEvalString() @@ -61,6 +66,7 @@ syn match ninjaOperator "\(=\|:\||\|||\)\ze\s" hi def link ninjaComment Comment hi def link ninjaKeyword Keyword hi def link ninjaRuleCommand Statement +hi def link ninjaPoolCommand Statement hi def link ninjaWrapLineOperator ninjaOperator hi def link ninjaOperator Operator hi def link ninjaSimpleVar ninjaVar -- cgit v0.12 From a7e4d6affbd6bb05dc443928f82c7a1a90f97c65 Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Thu, 13 Dec 2012 23:11:12 -0800 Subject: Add missing rspfile and rspfile_content --- misc/ninja.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/ninja.vim b/misc/ninja.vim index 6ec7c8b..7787c6e 100644 --- a/misc/ninja.vim +++ b/misc/ninja.vim @@ -37,7 +37,7 @@ syn match ninjaKeyword "^subninja\>" " manifest_parser.cc, ParseRule() syn region ninjaRule start="^rule" end="^\ze\S" contains=ALL transparent syn keyword ninjaRuleCommand contained command depfile description generator - \ pool restat + \ pool restat rspfile rspfile_content syn region ninjaPool start="^pool" end="^\ze\S" contains=ALL transparent syn keyword ninjaPoolCommand contained depth -- cgit v0.12 From 16a7f86f0f19e72ab0919112b3b8a206696b2df6 Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Thu, 13 Dec 2012 23:20:50 -0800 Subject: Add pool --- misc/ninja-mode.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/misc/ninja-mode.el b/misc/ninja-mode.el index 44fc82b..01d305e 100644 --- a/misc/ninja-mode.el +++ b/misc/ninja-mode.el @@ -18,7 +18,8 @@ (setq ninja-keywords (list '("^#.*" . font-lock-comment-face) - (cons (concat "^" (regexp-opt '("rule" "build" "subninja" "include") + (cons (concat "^" (regexp-opt '("rule" "build" "subninja" "include" + "pool") 'words)) font-lock-keyword-face) '("\\([[:alnum:]_]+\\) =" . (1 font-lock-variable-name-face)) -- cgit v0.12 From 53ba2ca746fbe3d9ce56eff1fa71f92b5dfba4da Mon Sep 17 00:00:00 2001 From: Robert Iannucci Date: Thu, 13 Dec 2012 23:21:02 -0800 Subject: Add missing default keyword --- misc/ninja-mode.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/ninja-mode.el b/misc/ninja-mode.el index 01d305e..d939206 100644 --- a/misc/ninja-mode.el +++ b/misc/ninja-mode.el @@ -19,7 +19,7 @@ (list '("^#.*" . font-lock-comment-face) (cons (concat "^" (regexp-opt '("rule" "build" "subninja" "include" - "pool") + "pool" "default") 'words)) font-lock-keyword-face) '("\\([[:alnum:]_]+\\) =" . (1 font-lock-variable-name-face)) -- cgit v0.12 From 6943f0537299e0d572b38b7d1e0562f0c13cb68e Mon Sep 17 00:00:00 2001 From: yannicklm Date: Fri, 14 Dec 2012 22:25:51 +0100 Subject: NINJA_STATUS: add support of `%p` for percentage --- doc/manual.asciidoc | 1 + src/build.cc | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc index 666f0a5..42e5452 100644 --- a/doc/manual.asciidoc +++ b/doc/manual.asciidoc @@ -215,6 +215,7 @@ Ninja supports one environment variable to control its behavior. Several placeholders are available: * `%s`: The number of started edges. * `%t`: The total number of edges that must be run to complete the build. +* `%p`: The percentage of started edges. * `%r`: The number of currently running edges. * `%u`: The number of remaining edges to start. * `%f`: The number of finished edges. diff --git a/src/build.cc b/src/build.cc index 93ab10d..e5429cf 100644 --- a/src/build.cc +++ b/src/build.cc @@ -179,6 +179,7 @@ string BuildStatus::FormatProgressStatus( const char* progress_status_format) const { string out; char buf[32]; + int percent; for (const char* s = progress_status_format; *s != '\0'; ++s) { if (*s == '%') { ++s; @@ -231,6 +232,13 @@ string BuildStatus::FormatProgressStatus( out += buf; break; + // Percentage + case 'p': + percent = (100 * started_edges_) / total_edges_; + snprintf(buf, sizeof(buf), "%3i%%", percent); + out += buf; + break; + default: Fatal("unknown placeholder '%%%c' in $NINJA_STATUS", *s); return ""; -- cgit v0.12 From 1b91f76bbaad5141d052782341bfc4f054f82159 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Fri, 14 Dec 2012 21:18:31 -0800 Subject: Update version number in ninja.vim after recent changes. --- misc/ninja.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/ninja.vim b/misc/ninja.vim index 7787c6e..841902f 100644 --- a/misc/ninja.vim +++ b/misc/ninja.vim @@ -1,8 +1,8 @@ " ninja build file syntax. " Language: ninja build file as described at " http://martine.github.com/ninja/manual.html -" Version: 1.2 -" Last Change: 2012/06/01 +" Version: 1.3 +" Last Change: 2012/12/14 " Maintainer: Nicolas Weber " Version 1.2 of this script is in the upstream vim repository and will be " included in the next vim release. If you change this, please send your change -- cgit v0.12 From be847b38264adce9a5309c6138e5c94d4785db5a Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Tue, 27 Nov 2012 08:30:45 -0800 Subject: update a comment --- src/graph.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/graph.h b/src/graph.h index ce92679..4a8198e 100644 --- a/src/graph.h +++ b/src/graph.h @@ -179,17 +179,14 @@ struct Edge { int weight() const { return 1; } bool outputs_ready() const { return outputs_ready_; } - // XXX There are three types of inputs. + // There are three types of inputs. // 1) explicit deps, which show up as $in on the command line; // 2) implicit deps, which the target depends on implicitly (e.g. C headers), // and changes in them cause the target to rebuild; // 3) order-only deps, which are needed before the target builds but which // don't cause the target to rebuild. - // Currently we stuff all of these into inputs_ and keep counts of #2 and #3 - // when we need to compute subsets. This is suboptimal; should think of a - // better representation. (Could make each pointer into a pair of a pointer - // and a type of input, or if memory matters could use the low bits of the - // pointer...) + // These are stored in inputs_ in that order, and we keep counts of + // #2 and #3 when we need to access the various subsets. int implicit_deps_; int order_only_deps_; bool is_implicit(size_t index) { -- cgit v0.12 From d8d3b2f0bdee4cd3d801bcd19bf65f91fb6760e0 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Thu, 20 Dec 2012 17:49:55 -0800 Subject: correctly open /dev/null in subprocesses Fixes issue #468. --- src/subprocess-posix.cc | 2 +- src/subprocess_test.cc | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/subprocess-posix.cc b/src/subprocess-posix.cc index 1c47fd1..8f1a04e 100644 --- a/src/subprocess-posix.cc +++ b/src/subprocess-posix.cc @@ -76,7 +76,7 @@ bool Subprocess::Start(SubprocessSet* set, const string& command) { break; // Open /dev/null over stdin. - int devnull = open("/dev/null", O_WRONLY); + int devnull = open("/dev/null", O_RDONLY); if (devnull < 0) break; if (dup2(devnull, 0) < 0) diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc index d89525e..313b227 100644 --- a/src/subprocess_test.cc +++ b/src/subprocess_test.cc @@ -179,3 +179,18 @@ TEST_F(SubprocessTest, SetWithLots) { ASSERT_EQ(kNumProcs, subprocs_.finished_.size()); } #endif // linux + +// TODO: this test could work on Windows, just not sure how to simply +// read stdin. +#ifndef _WIN32 +// Verify that a command that attempts to read stdin correctly thinks +// that stdin is closed. +TEST_F(SubprocessTest, ReadStdin) { + Subprocess* subproc = subprocs_.Add("cat -"); + while (!subproc->Done()) { + subprocs_.DoWork(); + } + ASSERT_EQ(ExitSuccess, subproc->Finish()); + ASSERT_EQ(1u, subprocs_.finished_.size()); +} +#endif // _WIN32 -- cgit v0.12 From b4e6932b81727bdd1d2266aa220b7c74432c4027 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Fri, 28 Dec 2012 13:47:59 -0800 Subject: wrap test in anon namespace --- src/build_log_test.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/build_log_test.cc b/src/build_log_test.cc index 5275f25..787a709 100644 --- a/src/build_log_test.cc +++ b/src/build_log_test.cc @@ -26,6 +26,8 @@ #include #endif +namespace { + const char kTestFilename[] = "BuildLogTest-tempfile"; struct BuildLogTest : public StateTestWithBuiltinRules { @@ -265,3 +267,5 @@ TEST_F(BuildLogTest, MultiTargetEdge) { ASSERT_EQ(22, e2->end_time); ASSERT_EQ(22, e2->end_time); } + +} // anonymous namespace -- cgit v0.12 From a29686ea9c9300ecc32f21b7ae9d2a0a962bddfc Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Sat, 29 Dec 2012 00:35:53 -0800 Subject: fix test build under clang/system gtest Tests always need GTEST_HAS_RTTI=0 set, but the code was only setting it in the --with-gtest branch. Instead always use the test-specific cflags when compiling test code. --- configure.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/configure.py b/configure.py index e41cf4e..b153f15 100755 --- a/configure.py +++ b/configure.py @@ -313,7 +313,7 @@ all_targets += ninja n.comment('Tests all build into ninja_test executable.') variables = [] -test_cflags = None +test_cflags = cflags[:] test_ldflags = None test_libs = libs objs = [] @@ -332,13 +332,17 @@ if options.with_gtest: os.path.join(path, 'src', 'gtest_main.cc'), variables=[('cflags', gtest_cflags)]) - test_cflags = cflags + ['-DGTEST_HAS_RTTI=0', - '-I%s' % os.path.join(path, 'include')] + test_cflags.append('-I%s' % os.path.join(path, 'include')) elif platform == 'windows': test_libs.extend(['gtest_main.lib', 'gtest.lib']) else: + test_cflags.append('-DGTEST_HAS_RTTI=0') test_libs.extend(['-lgtest_main', '-lgtest']) +if test_cflags == cflags: + test_cflags = None + +n.variable('test_cflags', test_cflags) for name in ['build_log_test', 'build_test', 'clean_test', @@ -352,7 +356,7 @@ for name in ['build_log_test', 'subprocess_test', 'test', 'util_test']: - objs += cxx(name, variables=[('cflags', test_cflags)]) + objs += cxx(name, variables=[('cflags', '$test_cflags')]) if platform in ('windows', 'mingw'): for name in ['includes_normalize_test', 'msvc_helper_test']: objs += cxx(name, variables=[('cflags', test_cflags)]) -- cgit v0.12 From b883c291c1c7b66c736f762ed6280ba56fcd7b43 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Sat, 29 Dec 2012 00:39:38 -0800 Subject: ninja_syntax.py: don't add trailing space when build has no inputs Seen in Ninja's configure.py where it creates an "rpmbuild" command. --- misc/ninja_syntax.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/misc/ninja_syntax.py b/misc/ninja_syntax.py index b0a1561..ece7eb5 100644 --- a/misc/ninja_syntax.py +++ b/misc/ninja_syntax.py @@ -72,9 +72,8 @@ class Writer(object): all_inputs.append('||') all_inputs.extend(order_only) - self._line('build %s: %s %s' % (' '.join(out_outputs), - rule, - ' '.join(all_inputs))) + self._line('build %s: %s' % (' '.join(out_outputs), + ' '.join([rule] + all_inputs))) if variables: if isinstance(variables, dict): -- cgit v0.12 From 35e955cf93c89145a33837d6dded611c18176c9c Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Sat, 29 Dec 2012 12:33:02 -0800 Subject: add a TODO from a pull request --- bootstrap.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bootstrap.py b/bootstrap.py index 7463d34..a847df9 100755 --- a/bootstrap.py +++ b/bootstrap.py @@ -31,6 +31,7 @@ parser.add_option('--verbose', action='store_true', help='enable verbose build',) parser.add_option('--x64', action='store_true', help='force 64-bit build (Windows)',) +# TODO: make this --platform to match configure.py. parser.add_option('--windows', action='store_true', help='force native Windows build (when using Cygwin Python)', default=sys.platform.startswith('win32')) -- cgit v0.12 From 7d41c2f521e27a3c2891e6f1c8da42f0f6f3c266 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Sat, 29 Dec 2012 11:57:14 -0800 Subject: fix all "class" -> "struct" --- src/build.cc | 3 +-- src/build_log.cc | 3 +-- src/clean.h | 3 +-- src/disk_interface_test.cc | 3 +-- src/metrics.h | 8 +++++--- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/build.cc b/src/build.cc index e5429cf..dcb4f45 100644 --- a/src/build.cc +++ b/src/build.cc @@ -41,8 +41,7 @@ namespace { /// A CommandRunner that doesn't actually run the commands. -class DryRunCommandRunner : public CommandRunner { - public: +struct DryRunCommandRunner : public CommandRunner { virtual ~DryRunCommandRunner() {} // Overridden from CommandRunner: diff --git a/src/build_log.cc b/src/build_log.cc index f53ccdc..6b73002 100644 --- a/src/build_log.cc +++ b/src/build_log.cc @@ -167,8 +167,7 @@ void BuildLog::Close() { log_file_ = NULL; } -class LineReader { - public: +struct LineReader { explicit LineReader(FILE* file) : file_(file), buf_end_(buf_), line_start_(buf_), line_end_(NULL) { memset(buf_, 0, sizeof(buf_)); diff --git a/src/clean.h b/src/clean.h index 5a23283..19432ab 100644 --- a/src/clean.h +++ b/src/clean.h @@ -27,8 +27,7 @@ struct Node; struct Rule; struct DiskInterface; -class Cleaner { - public: +struct Cleaner { /// Build a cleaner object with a real disk interface. Cleaner(State* state, const BuildConfig& config); diff --git a/src/disk_interface_test.cc b/src/disk_interface_test.cc index 32fe9cb..c2315c7 100644 --- a/src/disk_interface_test.cc +++ b/src/disk_interface_test.cc @@ -25,8 +25,7 @@ namespace { -class DiskInterfaceTest : public testing::Test { - public: +struct DiskInterfaceTest : public testing::Test { virtual void SetUp() { // These tests do real disk accesses, so create a temp dir. temp_dir_.CreateAndEnter("Ninja-DiskInterfaceTest"); diff --git a/src/metrics.h b/src/metrics.h index 044011d..b6da859 100644 --- a/src/metrics.h +++ b/src/metrics.h @@ -64,16 +64,18 @@ int64_t GetTimeMillis(); /// A simple stopwatch which returns the time /// in seconds since Restart() was called. -class Stopwatch { +struct Stopwatch { public: Stopwatch() : started_(0) {} /// Seconds since Restart() call. - double Elapsed() const { return 1e-6 * static_cast(Now() - started_); } + double Elapsed() const { + return 1e-6 * static_cast(Now() - started_); + } void Restart() { started_ = Now(); } -private: + private: uint64_t started_; uint64_t Now() const; }; -- cgit v0.12 From 3249938cdf574058a066436aea06b0541ded6958 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Sat, 29 Dec 2012 12:02:16 -0800 Subject: wrap some overlong lines --- configure.py | 4 +++- src/build.cc | 12 ++++++++---- src/build_log.h | 3 ++- src/build_log_test.cc | 3 ++- src/build_test.cc | 6 ++++-- src/disk_interface.cc | 12 ++++++++---- src/graph.cc | 7 ++++--- src/graph.h | 2 +- src/includes_normalize_test.cc | 16 +++++++++++----- src/manifest_parser.cc | 6 ++++-- src/subprocess-win32.cc | 7 +++++-- src/subprocess_test.cc | 3 ++- src/util.h | 3 ++- 13 files changed, 56 insertions(+), 28 deletions(-) diff --git a/configure.py b/configure.py index b153f15..9391a68 100755 --- a/configure.py +++ b/configure.py @@ -171,7 +171,9 @@ else: libs.append('-lprofiler') def shell_escape(str): - """Escape str such that it's interpreted as a single argument by the shell.""" + """Escape str such that it's interpreted as a single argument by + the shell.""" + # This isn't complete, but it's just enough to make NINJA_PYTHON work. if platform in ('windows', 'mingw'): return str diff --git a/src/build.cc b/src/build.cc index dcb4f45..b4229c4 100644 --- a/src/build.cc +++ b/src/build.cc @@ -482,8 +482,9 @@ void Plan::CleanNode(DependencyScan* scan, Node* node) { // If all non-order-only inputs for this edge are now clean, // we might have changed the dirty state of the outputs. - vector::iterator begin = (*ei)->inputs_.begin(), - end = (*ei)->inputs_.end() - (*ei)->order_only_deps_; + vector::iterator + begin = (*ei)->inputs_.begin(), + end = (*ei)->inputs_.end() - (*ei)->order_only_deps_; if (find_if(begin, end, mem_fun(&Node::dirty)) == end) { // Recompute most_recent_input and command. Node* most_recent_input = NULL; @@ -771,8 +772,10 @@ bool Builder::StartEdge(Edge* edge, string* err) { // Create response file, if needed // XXX: this may also block; do we care? if (edge->HasRspFile()) { - if (!disk_interface_->WriteFile(edge->GetRspFile(), edge->GetRspFileContent())) + if (!disk_interface_->WriteFile(edge->GetRspFile(), + edge->GetRspFileContent())) { return false; + } } // start command computing and run it @@ -815,7 +818,8 @@ void Builder::FinishEdge(Edge* edge, bool success, const string& output) { } if (restat_mtime != 0 && !edge->rule().depfile().empty()) { - TimeStamp depfile_mtime = disk_interface_->Stat(edge->EvaluateDepFile()); + TimeStamp depfile_mtime = + disk_interface_->Stat(edge->EvaluateDepFile()); if (depfile_mtime > restat_mtime) restat_mtime = depfile_mtime; } diff --git a/src/build_log.h b/src/build_log.h index 5a3b516..231bfd9 100644 --- a/src/build_log.h +++ b/src/build_log.h @@ -62,7 +62,8 @@ struct BuildLog { } explicit LogEntry(const string& output); - LogEntry(const string& output, uint64_t command_hash, int start_time, int end_time, TimeStamp restat_mtime); + LogEntry(const string& output, uint64_t command_hash, + int start_time, int end_time, TimeStamp restat_mtime); }; /// Lookup a previously-run command by its output path. diff --git a/src/build_log_test.cc b/src/build_log_test.cc index 787a709..2dd6500 100644 --- a/src/build_log_test.cc +++ b/src/build_log_test.cc @@ -147,7 +147,8 @@ TEST_F(BuildLogTest, Truncate) { ASSERT_EQ(0, truncate(kTestFilename, size)); #else int fh; - fh = _sopen(kTestFilename, _O_RDWR | _O_CREAT, _SH_DENYNO, _S_IREAD | _S_IWRITE); + fh = _sopen(kTestFilename, _O_RDWR | _O_CREAT, _SH_DENYNO, + _S_IREAD | _S_IWRITE); ASSERT_EQ(0, _chsize(fh, size)); _close(fh); #endif diff --git a/src/build_test.cc b/src/build_test.cc index 17e433b..59c4c53 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -573,10 +573,12 @@ TEST_F(BuildTest, MakeDirs) { string err; #ifdef _WIN32 - ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, "build subdir\\dir2\\file: cat in1\n")); + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, + "build subdir\\dir2\\file: cat in1\n")); EXPECT_TRUE(builder_.AddTarget("subdir\\dir2\\file", &err)); #else - ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, "build subdir/dir2/file: cat in1\n")); + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, + "build subdir/dir2/file: cat in1\n")); EXPECT_TRUE(builder_.AddTarget("subdir/dir2/file", &err)); #endif diff --git a/src/disk_interface.cc b/src/disk_interface.cc index 515ff59..7c557cd 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -80,7 +80,8 @@ TimeStamp RealDiskInterface::Stat(const string& path) { // MSDN: "Naming Files, Paths, and Namespaces" // http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx if (!path.empty() && path[0] != '\\' && path.size() > MAX_PATH) { - Error("Stat(%s): Filename longer than %i characters", path.c_str(), MAX_PATH); + Error("Stat(%s): Filename longer than %i characters", + path.c_str(), MAX_PATH); return -1; } WIN32_FILE_ATTRIBUTE_DATA attrs; @@ -116,18 +117,21 @@ TimeStamp RealDiskInterface::Stat(const string& path) { bool RealDiskInterface::WriteFile(const string& path, const string& contents) { FILE * fp = fopen(path.c_str(), "w"); if (fp == NULL) { - Error("WriteFile(%s): Unable to create file. %s", path.c_str(), strerror(errno)); + Error("WriteFile(%s): Unable to create file. %s", + path.c_str(), strerror(errno)); return false; } if (fwrite(contents.data(), 1, contents.length(), fp) < contents.length()) { - Error("WriteFile(%s): Unable to write to the file. %s", path.c_str(), strerror(errno)); + Error("WriteFile(%s): Unable to write to the file. %s", + path.c_str(), strerror(errno)); fclose(fp); return false; } if (fclose(fp) == EOF) { - Error("WriteFile(%s): Unable to close the file. %s", path.c_str(), strerror(errno)); + Error("WriteFile(%s): Unable to close the file. %s", + path.c_str(), strerror(errno)); return false; } diff --git a/src/graph.cc b/src/graph.cc index f8ceda9..f9b9c6f 100644 --- a/src/graph.cc +++ b/src/graph.cc @@ -145,9 +145,10 @@ bool DependencyScan::RecomputeOutputDirty(Edge* edge, if (edge->rule_->restat() && build_log() && (entry = build_log()->LookupByOutput(output->path()))) { if (entry->restat_mtime < most_recent_stamp) { - EXPLAIN("restat of output %s older than most recent input %s (%d vs %d)", - output->path().c_str(), most_recent_input->path().c_str(), - entry->restat_mtime, most_recent_stamp); + EXPLAIN("restat of output %s older than most recent input %s " + "(%d vs %d)", + output->path().c_str(), most_recent_input->path().c_str(), + entry->restat_mtime, most_recent_stamp); return true; } } else { diff --git a/src/graph.h b/src/graph.h index 4a8198e..3c31e19 100644 --- a/src/graph.h +++ b/src/graph.h @@ -152,7 +152,7 @@ struct Edge { /// Expand all variables in a command and return it as a string. /// If incl_rsp_file is enabled, the string will also contain the /// full contents of a response file (if applicable) - string EvaluateCommand(bool incl_rsp_file = false); // XXX move to env, take env ptr + string EvaluateCommand(bool incl_rsp_file = false); string EvaluateDepFile(); string GetDescription(); diff --git a/src/includes_normalize_test.cc b/src/includes_normalize_test.cc index 77b5b3b..29e6755 100644 --- a/src/includes_normalize_test.cc +++ b/src/includes_normalize_test.cc @@ -40,7 +40,8 @@ string GetCurDir() { TEST(IncludesNormalize, WithRelative) { string currentdir = IncludesNormalize::ToLower(GetCurDir()); EXPECT_EQ("c", IncludesNormalize::Normalize("a/b/c", "a/b")); - EXPECT_EQ("a", IncludesNormalize::Normalize(IncludesNormalize::AbsPath("a"), NULL)); + EXPECT_EQ("a", IncludesNormalize::Normalize(IncludesNormalize::AbsPath("a"), + NULL)); EXPECT_EQ(string("..\\") + currentdir + string("\\a"), IncludesNormalize::Normalize("a", "../b")); EXPECT_EQ(string("..\\") + currentdir + string("\\a\\b"), @@ -69,16 +70,21 @@ TEST(IncludesNormalize, Join) { } TEST(IncludesNormalize, Split) { - EXPECT_EQ("", IncludesNormalize::Join(IncludesNormalize::Split("", '/'), ':')); - EXPECT_EQ("a", IncludesNormalize::Join(IncludesNormalize::Split("a", '/'), ':')); - EXPECT_EQ("a:b:c", IncludesNormalize::Join(IncludesNormalize::Split("a/b/c", '/'), ':')); + EXPECT_EQ("", IncludesNormalize::Join(IncludesNormalize::Split("", '/'), + ':')); + EXPECT_EQ("a", IncludesNormalize::Join(IncludesNormalize::Split("a", '/'), + ':')); + EXPECT_EQ("a:b:c", + IncludesNormalize::Join( + IncludesNormalize::Split("a/b/c", '/'), ':')); } TEST(IncludesNormalize, ToLower) { EXPECT_EQ("", IncludesNormalize::ToLower("")); EXPECT_EQ("stuff", IncludesNormalize::ToLower("Stuff")); EXPECT_EQ("stuff and things", IncludesNormalize::ToLower("Stuff AND thINGS")); - EXPECT_EQ("stuff 3and thin43gs", IncludesNormalize::ToLower("Stuff 3AND thIN43GS")); + EXPECT_EQ("stuff 3and thin43gs", + IncludesNormalize::ToLower("Stuff 3AND thIN43GS")); } TEST(IncludesNormalize, DifferentDrive) { diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc index 271b841..2d052b5 100644 --- a/src/manifest_parser.cc +++ b/src/manifest_parser.cc @@ -177,8 +177,10 @@ bool ManifestParser::ParseRule(string* err) { } } - if (rule->rspfile_.empty() != rule->rspfile_content_.empty()) - return lexer_.Error("rspfile and rspfile_content need to be both specified", err); + if (rule->rspfile_.empty() != rule->rspfile_content_.empty()) { + return lexer_.Error("rspfile and rspfile_content need to be both specified", + err); + } if (rule->command_.empty()) return lexer_.Error("expected 'command =' line", err); diff --git a/src/subprocess-win32.cc b/src/subprocess-win32.cc index 4b103a5..1b230b6 100644 --- a/src/subprocess-win32.cc +++ b/src/subprocess-win32.cc @@ -101,14 +101,17 @@ bool Subprocess::Start(SubprocessSet* set, const string& command) { NULL, NULL, &startup_info, &process_info)) { DWORD error = GetLastError(); - if (error == ERROR_FILE_NOT_FOUND) { // file (program) not found error is treated as a normal build action failure + if (error == ERROR_FILE_NOT_FOUND) { + // File (program) not found error is treated as a normal build + // action failure. if (child_pipe) CloseHandle(child_pipe); CloseHandle(pipe_); CloseHandle(nul); pipe_ = NULL; // child_ is already NULL; - buf_ = "CreateProcess failed: The system cannot find the file specified.\n"; + buf_ = "CreateProcess failed: The system cannot find the file " + "specified.\n"; return true; } else { Win32Fatal("CreateProcess"); // pass all other errors to Win32Fatal diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc index 313b227..c3175da 100644 --- a/src/subprocess_test.cc +++ b/src/subprocess_test.cc @@ -64,7 +64,8 @@ TEST_F(SubprocessTest, NoSuchCommand) { EXPECT_EQ(ExitFailure, subproc->Finish()); EXPECT_NE("", subproc->GetOutput()); #ifdef _WIN32 - ASSERT_EQ("CreateProcess failed: The system cannot find the file specified.\n", subproc->GetOutput()); + ASSERT_EQ("CreateProcess failed: The system cannot find the file " + "specified.\n", subproc->GetOutput()); #endif } diff --git a/src/util.h b/src/util.h index 6c142c6..2b59283 100644 --- a/src/util.h +++ b/src/util.h @@ -49,7 +49,8 @@ void SetCloseOnExec(int fd); /// Given a misspelled string and a list of correct spellings, returns /// the closest match or NULL if there is no close enough match. -const char* SpellcheckStringV(const string& text, const vector& words); +const char* SpellcheckStringV(const string& text, + const vector& words); /// Like SpellcheckStringV, but takes a NULL-terminated list. const char* SpellcheckString(const string& text, ...); -- cgit v0.12