From b063d8e882cecd5a4efc59b7182909a918500f12 Mon Sep 17 00:00:00 2001 From: Demetri Obenour Date: Thu, 8 May 2014 10:29:27 -0400 Subject: Added support for international characters in makefile dependency names --- src/depfile_parser.in.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/depfile_parser.in.cc b/src/depfile_parser.in.cc index b59baf0..24744c7 100644 --- a/src/depfile_parser.in.cc +++ b/src/depfile_parser.in.cc @@ -45,7 +45,7 @@ bool DepfileParser::Parse(string* content, string* err) { // start: beginning of the current parsed span. const char* start = in; /*!re2c - re2c:define:YYCTYPE = "char"; + re2c:define:YYCTYPE = "unsigned char"; re2c:define:YYCURSOR = in; re2c:define:YYLIMIT = end; @@ -73,7 +73,7 @@ bool DepfileParser::Parse(string* content, string* err) { *out++ = yych; continue; } - [a-zA-Z0-9+,/_:.~()}{@=!-]+ { + [a-zA-Z0-9+,/_:.~()}{@=!\x80-\xFF-]+ { // Got a span of plain text. int len = (int)(in - start); // Need to shift it over if we're overwriting backslashes. -- cgit v0.12 From 6143187ac6f44995b9cd82d17cd4f13147286fb9 Mon Sep 17 00:00:00 2001 From: Demetri Obenour Date: Thu, 8 May 2014 11:01:07 -0400 Subject: Added test of international character support --- src/depfile_parser.cc | 58 +++++++++++++++++++++++++--------------------- src/depfile_parser_test.cc | 7 ++++-- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/depfile_parser.cc b/src/depfile_parser.cc index 4ca3943..dc53d4f 100644 --- a/src/depfile_parser.cc +++ b/src/depfile_parser.cc @@ -47,7 +47,7 @@ bool DepfileParser::Parse(string* content, string* err) { const char* start = in; { - char yych; + unsigned char yych; static const unsigned char yybm[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -65,22 +65,22 @@ bool DepfileParser::Parse(string* content, string* err) { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 0, 128, 128, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, }; yych = *in; @@ -106,24 +106,28 @@ bool DepfileParser::Parse(string* content, string* err) { } } } else { - if (yych <= '^') { - if (yych <= 'Z') { + if (yych <= '_') { + if (yych <= '[') { if (yych <= '?') goto yy9; - goto yy5; + if (yych <= 'Z') goto yy5; + goto yy9; } else { - if (yych != '\\') goto yy9; + if (yych <= '\\') goto yy2; + if (yych <= '^') goto yy9; + goto yy5; } } else { - if (yych <= '{') { - if (yych == '`') goto yy9; - goto yy5; - } else { - if (yych <= '|') goto yy9; - if (yych <= '~') goto yy5; + if (yych <= '|') { + if (yych <= '`') goto yy9; + if (yych <= '{') goto yy5; goto yy9; + } else { + if (yych == 0x7F) goto yy9; + goto yy5; } } } +yy2: ++in; if ((yych = *in) <= '"') { if (yych <= '\f') { diff --git a/src/depfile_parser_test.cc b/src/depfile_parser_test.cc index a5f3321..c7a163c 100644 --- a/src/depfile_parser_test.cc +++ b/src/depfile_parser_test.cc @@ -121,18 +121,21 @@ TEST_F(DepfileParserTest, SpecialChars) { EXPECT_TRUE(Parse( "C:/Program\\ Files\\ (x86)/Microsoft\\ crtdefs.h: \n" " en@quot.header~ t+t-x!=1 \n" -" openldap/slapd.d/cn=config/cn=schema/cn={0}core.ldif", +" openldap/slapd.d/cn=config/cn=schema/cn={0}core.ldif\n" +" Fu\303\244ball", &err)); ASSERT_EQ("", err); EXPECT_EQ("C:/Program Files (x86)/Microsoft crtdefs.h", parser_.out_.AsString()); - ASSERT_EQ(3u, parser_.ins_.size()); + ASSERT_EQ(4u, parser_.ins_.size()); EXPECT_EQ("en@quot.header~", parser_.ins_[0].AsString()); EXPECT_EQ("t+t-x!=1", parser_.ins_[1].AsString()); EXPECT_EQ("openldap/slapd.d/cn=config/cn=schema/cn={0}core.ldif", parser_.ins_[2].AsString()); + EXPECT_EQ("Fu\303\244ball", + parser_.ins_[3].AsString()); } TEST_F(DepfileParserTest, UnifyMultipleOutputs) { -- cgit v0.12 From a2d1cf2f26bdd7a326a3ef6186ee507fd87868d0 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 8 Jun 2015 13:21:55 -0700 Subject: Fix crash in attempting to canonicalize paths longer than _MAX_PATH --- src/includes_normalize-win32.cc | 6 +++++- src/includes_normalize_test.cc | 47 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/includes_normalize-win32.cc b/src/includes_normalize-win32.cc index 1e88a0a..066f512 100644 --- a/src/includes_normalize-win32.cc +++ b/src/includes_normalize-win32.cc @@ -96,8 +96,12 @@ string IncludesNormalize::Relativize(StringPiece path, const string& start) { string IncludesNormalize::Normalize(const string& input, const char* relative_to) { - char copy[_MAX_PATH]; + char copy[_MAX_PATH + 1]; size_t len = input.size(); + if (len > _MAX_PATH) { + Warning("path too long '%s'\n", input.c_str()); + return input; + } strncpy(copy, input.c_str(), input.size() + 1); string err; unsigned int slash_bits; diff --git a/src/includes_normalize_test.cc b/src/includes_normalize_test.cc index c4c2476..b3519e2 100644 --- a/src/includes_normalize_test.cc +++ b/src/includes_normalize_test.cc @@ -14,6 +14,8 @@ #include "includes_normalize.h" +#include + #include #include "test.h" @@ -102,3 +104,48 @@ TEST(IncludesNormalize, DifferentDrive) { IncludesNormalize::Normalize("P:/vs08\\../wee\\stuff.h", "D:\\stuff/things")); } + +TEST(IncludesNormalize, LongInvalidPath) { + const char kLongInputString[] = + "C:\\Program Files (x86)\\Microsoft Visual Studio " + "12.0\\VC\\INCLUDEwarning #31001: The dll for reading and writing the " + "pdb (for example, mspdb110.dll) could not be found on your path. This " + "is usually a configuration error. Compilation will continue using /Z7 " + "instead of /Zi, but expect a similar error when you link your program."; + // Too long, won't be canonicalized, but just don't crash. + EXPECT_EQ(kLongInputString, + IncludesNormalize::Normalize(kLongInputString, NULL)); + + const char kExactlyMaxPath[] = + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "012345678\\" + "0123456789"; + std::string forward_slashes(kExactlyMaxPath); + std::replace(forward_slashes.begin(), forward_slashes.end(), '\\', '/'); + // Make sure a path that's exactly _MAX_PATH long is canonicalized. + EXPECT_EQ(forward_slashes, + IncludesNormalize::Normalize(kExactlyMaxPath, NULL)); +} -- cgit v0.12 From 1493c6a0b0f779294b870ae8ee4a8515087ac64e Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 8 Jun 2015 17:17:55 -0700 Subject: push error to caller and abort on failure --- src/includes_normalize-win32.cc | 22 ++++++----- src/includes_normalize.h | 3 +- src/includes_normalize_test.cc | 83 +++++++++++++++++++++++++---------------- src/msvc_helper-win32.cc | 12 ++++-- 4 files changed, 73 insertions(+), 47 deletions(-) diff --git a/src/includes_normalize-win32.cc b/src/includes_normalize-win32.cc index 066f512..ca35012 100644 --- a/src/includes_normalize-win32.cc +++ b/src/includes_normalize-win32.cc @@ -94,19 +94,18 @@ string IncludesNormalize::Relativize(StringPiece path, const string& start) { return Join(rel_list, '/'); } -string IncludesNormalize::Normalize(const string& input, - const char* relative_to) { +bool IncludesNormalize::Normalize(const string& input, const char* relative_to, + string* result, string* err) { char copy[_MAX_PATH + 1]; size_t len = input.size(); if (len > _MAX_PATH) { - Warning("path too long '%s'\n", input.c_str()); - return input; + *err = "path too long"; + return false; } strncpy(copy, input.c_str(), input.size() + 1); - string err; unsigned int slash_bits; - if (!CanonicalizePath(copy, &len, &slash_bits, &err)) - Warning("couldn't canonicalize '%s': %s\n", input.c_str(), err.c_str()); + if (!CanonicalizePath(copy, &len, &slash_bits, err)) + return false; StringPiece partially_fixed(copy, len); string curdir; @@ -114,7 +113,10 @@ string IncludesNormalize::Normalize(const string& input, curdir = AbsPath("."); relative_to = curdir.c_str(); } - if (!SameDrive(partially_fixed, relative_to)) - return partially_fixed.AsString(); - return Relativize(partially_fixed, relative_to); + if (!SameDrive(partially_fixed, relative_to)) { + *result = partially_fixed.AsString(); + return true; + } + *result = Relativize(partially_fixed, relative_to); + return true; } diff --git a/src/includes_normalize.h b/src/includes_normalize.h index 634fef3..98e912f 100644 --- a/src/includes_normalize.h +++ b/src/includes_normalize.h @@ -30,5 +30,6 @@ struct IncludesNormalize { /// Normalize by fixing slashes style, fixing redundant .. and . and makes the /// path relative to |relative_to|. - static string Normalize(const string& input, const char* relative_to); + static bool Normalize(const string& input, const char* relative_to, + string* result, string* err); }; diff --git a/src/includes_normalize_test.cc b/src/includes_normalize_test.cc index b3519e2..aba25d0 100644 --- a/src/includes_normalize_test.cc +++ b/src/includes_normalize_test.cc @@ -21,13 +21,6 @@ #include "test.h" #include "util.h" -TEST(IncludesNormalize, Simple) { - EXPECT_EQ("b", IncludesNormalize::Normalize("a\\..\\b", NULL)); - EXPECT_EQ("b", IncludesNormalize::Normalize("a\\../b", NULL)); - EXPECT_EQ("a/b", IncludesNormalize::Normalize("a\\.\\b", NULL)); - EXPECT_EQ("a/b", IncludesNormalize::Normalize("a\\./b", NULL)); -} - namespace { string GetCurDir() { @@ -37,28 +30,50 @@ string GetCurDir() { return parts[parts.size() - 1]; } +string NormalizeAndCheckNoError(const std::string& input) { + string result, err; + EXPECT_TRUE(IncludesNormalize::Normalize(input.c_str(), NULL, &result, &err)); + EXPECT_EQ("", err); + return result; +} + +string NormalizeRelativeAndCheckNoError(const std::string& input, + const std::string& relative_to) { + string result, err; + EXPECT_TRUE(IncludesNormalize::Normalize(input.c_str(), relative_to.c_str(), + &result, &err)); + EXPECT_EQ("", err); + return result; +} + } // namespace +TEST(IncludesNormalize, Simple) { + EXPECT_EQ("b", NormalizeAndCheckNoError("a\\..\\b")); + EXPECT_EQ("b", NormalizeAndCheckNoError("a\\../b")); + EXPECT_EQ("a/b", NormalizeAndCheckNoError("a\\.\\b")); + EXPECT_EQ("a/b", NormalizeAndCheckNoError("a\\./b")); +} + TEST(IncludesNormalize, WithRelative) { string currentdir = GetCurDir(); - EXPECT_EQ("c", IncludesNormalize::Normalize("a/b/c", "a/b")); - EXPECT_EQ("a", IncludesNormalize::Normalize(IncludesNormalize::AbsPath("a"), - NULL)); + EXPECT_EQ("c", NormalizeRelativeAndCheckNoError("a/b/c", "a/b")); + EXPECT_EQ("a", NormalizeAndCheckNoError(IncludesNormalize::AbsPath("a"))); EXPECT_EQ(string("../") + currentdir + string("/a"), - IncludesNormalize::Normalize("a", "../b")); + NormalizeRelativeAndCheckNoError("a", "../b")); EXPECT_EQ(string("../") + currentdir + string("/a/b"), - IncludesNormalize::Normalize("a/b", "../c")); - EXPECT_EQ("../../a", IncludesNormalize::Normalize("a", "b/c")); - EXPECT_EQ(".", IncludesNormalize::Normalize("a", "a")); + NormalizeRelativeAndCheckNoError("a/b", "../c")); + EXPECT_EQ("../../a", NormalizeRelativeAndCheckNoError("a", "b/c")); + EXPECT_EQ(".", NormalizeRelativeAndCheckNoError("a", "a")); } TEST(IncludesNormalize, Case) { - EXPECT_EQ("b", IncludesNormalize::Normalize("Abc\\..\\b", NULL)); - EXPECT_EQ("BdEf", IncludesNormalize::Normalize("Abc\\..\\BdEf", NULL)); - EXPECT_EQ("A/b", IncludesNormalize::Normalize("A\\.\\b", NULL)); - EXPECT_EQ("a/b", IncludesNormalize::Normalize("a\\./b", NULL)); - EXPECT_EQ("A/B", IncludesNormalize::Normalize("A\\.\\B", NULL)); - EXPECT_EQ("A/B", IncludesNormalize::Normalize("A\\./B", NULL)); + EXPECT_EQ("b", NormalizeAndCheckNoError("Abc\\..\\b")); + EXPECT_EQ("BdEf", NormalizeAndCheckNoError("Abc\\..\\BdEf")); + EXPECT_EQ("A/b", NormalizeAndCheckNoError("A\\.\\b")); + EXPECT_EQ("a/b", NormalizeAndCheckNoError("a\\./b")); + EXPECT_EQ("A/B", NormalizeAndCheckNoError("A\\.\\B")); + EXPECT_EQ("A/B", NormalizeAndCheckNoError("A\\./B")); } TEST(IncludesNormalize, Join) { @@ -91,18 +106,18 @@ TEST(IncludesNormalize, ToLower) { TEST(IncludesNormalize, DifferentDrive) { EXPECT_EQ("stuff.h", - IncludesNormalize::Normalize("p:\\vs08\\stuff.h", "p:\\vs08")); + NormalizeRelativeAndCheckNoError("p:\\vs08\\stuff.h", "p:\\vs08")); EXPECT_EQ("stuff.h", - IncludesNormalize::Normalize("P:\\Vs08\\stuff.h", "p:\\vs08")); + NormalizeRelativeAndCheckNoError("P:\\Vs08\\stuff.h", "p:\\vs08")); EXPECT_EQ("p:/vs08/stuff.h", - IncludesNormalize::Normalize("p:\\vs08\\stuff.h", "c:\\vs08")); - EXPECT_EQ("P:/vs08/stufF.h", - IncludesNormalize::Normalize("P:\\vs08\\stufF.h", "D:\\stuff/things")); - EXPECT_EQ("P:/vs08/stuff.h", - IncludesNormalize::Normalize("P:/vs08\\stuff.h", "D:\\stuff/things")); + NormalizeRelativeAndCheckNoError("p:\\vs08\\stuff.h", "c:\\vs08")); + EXPECT_EQ("P:/vs08/stufF.h", NormalizeRelativeAndCheckNoError( + "P:\\vs08\\stufF.h", "D:\\stuff/things")); + EXPECT_EQ("P:/vs08/stuff.h", NormalizeRelativeAndCheckNoError( + "P:/vs08\\stuff.h", "D:\\stuff/things")); EXPECT_EQ("P:/wee/stuff.h", - IncludesNormalize::Normalize("P:/vs08\\../wee\\stuff.h", - "D:\\stuff/things")); + NormalizeRelativeAndCheckNoError("P:/vs08\\../wee\\stuff.h", + "D:\\stuff/things")); } TEST(IncludesNormalize, LongInvalidPath) { @@ -112,9 +127,11 @@ TEST(IncludesNormalize, LongInvalidPath) { "pdb (for example, mspdb110.dll) could not be found on your path. This " "is usually a configuration error. Compilation will continue using /Z7 " "instead of /Zi, but expect a similar error when you link your program."; - // Too long, won't be canonicalized, but just don't crash. - EXPECT_EQ(kLongInputString, - IncludesNormalize::Normalize(kLongInputString, NULL)); + // Too long, won't be canonicalized. Ensure doesn't crash. + string result, err; + EXPECT_FALSE( + IncludesNormalize::Normalize(kLongInputString, NULL, &result, &err)); + EXPECT_EQ("path too long", err); const char kExactlyMaxPath[] = "012345678\\" @@ -147,5 +164,5 @@ TEST(IncludesNormalize, LongInvalidPath) { std::replace(forward_slashes.begin(), forward_slashes.end(), '\\', '/'); // Make sure a path that's exactly _MAX_PATH long is canonicalized. EXPECT_EQ(forward_slashes, - IncludesNormalize::Normalize(kExactlyMaxPath, NULL)); + NormalizeAndCheckNoError(kExactlyMaxPath)); } diff --git a/src/msvc_helper-win32.cc b/src/msvc_helper-win32.cc index e465279..4b22c7a 100644 --- a/src/msvc_helper-win32.cc +++ b/src/msvc_helper-win32.cc @@ -95,9 +95,15 @@ string CLParser::Parse(const string& output, const string& deps_prefix) { string include = FilterShowIncludes(line, deps_prefix); if (!include.empty()) { - include = IncludesNormalize::Normalize(include, NULL); - if (!IsSystemInclude(include)) - includes_.insert(include); + string normalized; + string err; + if (!IncludesNormalize::Normalize(include, NULL, &normalized, &err)) { + printf("failed to normalize path: %s: %s\n", include.c_str(), + err.c_str()); + abort(); + } + if (!IsSystemInclude(normalized)) + includes_.insert(normalized); } else if (FilterInputFilename(line)) { // Drop it. // TODO: if we support compiling multiple output files in a single -- cgit v0.12 From 32774e9ba88a3d4e3decdad0fa01620cb16d6300 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 9 Jun 2015 10:15:11 -0700 Subject: don't abort() --- src/msvc_helper-win32.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/msvc_helper-win32.cc b/src/msvc_helper-win32.cc index 4b22c7a..f875633 100644 --- a/src/msvc_helper-win32.cc +++ b/src/msvc_helper-win32.cc @@ -98,9 +98,9 @@ string CLParser::Parse(const string& output, const string& deps_prefix) { string normalized; string err; if (!IncludesNormalize::Normalize(include, NULL, &normalized, &err)) { - printf("failed to normalize path: %s: %s\n", include.c_str(), - err.c_str()); - abort(); + Error("failed to normalize path: %s: %s\n", include.c_str(), + err.c_str()); + exit(1); } if (!IsSystemInclude(normalized)) includes_.insert(normalized); -- cgit v0.12 From 38aba9dbe68b800038f1cefc827362c126c7b212 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 18 Jun 2015 15:06:42 -0700 Subject: Error+exit -> Fatal --- src/msvc_helper-win32.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/msvc_helper-win32.cc b/src/msvc_helper-win32.cc index f875633..a52dd38 100644 --- a/src/msvc_helper-win32.cc +++ b/src/msvc_helper-win32.cc @@ -98,9 +98,8 @@ string CLParser::Parse(const string& output, const string& deps_prefix) { string normalized; string err; if (!IncludesNormalize::Normalize(include, NULL, &normalized, &err)) { - Error("failed to normalize path: %s: %s\n", include.c_str(), + Fatal("failed to normalize path: %s: %s\n", include.c_str(), err.c_str()); - exit(1); } if (!IsSystemInclude(normalized)) includes_.insert(normalized); -- cgit v0.12 From fd4b140467887be6cb96154d28b9636fe387c69d Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 18 Jun 2015 15:41:47 -0700 Subject: propagate include normalization failure to caller instead --- src/build.cc | 3 ++- src/msvc_helper-win32.cc | 18 +++++++----------- src/msvc_helper.h | 5 +++-- src/msvc_helper_main-win32.cc | 4 +++- src/msvc_helper_test.cc | 25 +++++++++++++++---------- 5 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/build.cc b/src/build.cc index cdb8e0a..fcde626 100644 --- a/src/build.cc +++ b/src/build.cc @@ -849,7 +849,8 @@ bool Builder::ExtractDeps(CommandRunner::Result* result, #ifdef _WIN32 if (deps_type == "msvc") { CLParser parser; - result->output = parser.Parse(result->output, deps_prefix); + if (!parser.Parse(result->output, deps_prefix, &result->output, err)) + return false; for (set::iterator i = parser.includes_.begin(); i != parser.includes_.end(); ++i) { // ~0 is assuming that with MSVC-parsed headers, it's ok to always make diff --git a/src/msvc_helper-win32.cc b/src/msvc_helper-win32.cc index a52dd38..78b79b0 100644 --- a/src/msvc_helper-win32.cc +++ b/src/msvc_helper-win32.cc @@ -82,9 +82,8 @@ bool CLParser::FilterInputFilename(string line) { EndsWith(line, ".cpp"); } -string CLParser::Parse(const string& output, const string& deps_prefix) { - string filtered_output; - +bool CLParser::Parse(const string& output, const string& deps_prefix, + string* filtered_output, string* err) { // Loop over all lines in the output to process them. size_t start = 0; while (start < output.size()) { @@ -96,11 +95,8 @@ string CLParser::Parse(const string& output, const string& deps_prefix) { string include = FilterShowIncludes(line, deps_prefix); if (!include.empty()) { string normalized; - string err; - if (!IncludesNormalize::Normalize(include, NULL, &normalized, &err)) { - Fatal("failed to normalize path: %s: %s\n", include.c_str(), - err.c_str()); - } + if (!IncludesNormalize::Normalize(include, NULL, &normalized, err)) + return false; if (!IsSystemInclude(normalized)) includes_.insert(normalized); } else if (FilterInputFilename(line)) { @@ -108,8 +104,8 @@ string CLParser::Parse(const string& output, const string& deps_prefix) { // TODO: if we support compiling multiple output files in a single // cl.exe invocation, we should stash the filename. } else { - filtered_output.append(line); - filtered_output.append("\n"); + filtered_output->append(line); + filtered_output->append("\n"); } if (end < output.size() && output[end] == '\r') @@ -119,7 +115,7 @@ string CLParser::Parse(const string& output, const string& deps_prefix) { start = end; } - return filtered_output; + return true; } int CLWrapper::Run(const string& command, string* output) { diff --git a/src/msvc_helper.h b/src/msvc_helper.h index 5d7dcb0..08c0ad5 100644 --- a/src/msvc_helper.h +++ b/src/msvc_helper.h @@ -41,8 +41,9 @@ struct CLParser { static bool FilterInputFilename(string line); /// Parse the full output of cl, returning the output (if any) that - /// should printed. - string Parse(const string& output, const string& deps_prefix); + /// should printed. Returns true on success, or false with err filled. + bool Parse(const string& output, const string& deps_prefix, + string* filtered_output, string* err); set includes_; }; diff --git a/src/msvc_helper_main-win32.cc b/src/msvc_helper_main-win32.cc index 58bc797..680aaad 100644 --- a/src/msvc_helper_main-win32.cc +++ b/src/msvc_helper_main-win32.cc @@ -127,7 +127,9 @@ int MSVCHelperMain(int argc, char** argv) { if (output_filename) { CLParser parser; - output = parser.Parse(output, deps_prefix); + string err; + if (!parser.Parse(output, deps_prefix, &output, &err)) + Fatal("%s\n", err.c_str()); WriteDepFileOrDie(output_filename, parser); } diff --git a/src/msvc_helper_test.cc b/src/msvc_helper_test.cc index 29db650..49d27c1 100644 --- a/src/msvc_helper_test.cc +++ b/src/msvc_helper_test.cc @@ -46,11 +46,12 @@ TEST(CLParserTest, FilterInputFilename) { TEST(CLParserTest, ParseSimple) { CLParser parser; - string output = parser.Parse( + string output, err; + ASSERT_TRUE(parser.Parse( "foo\r\n" "Note: inc file prefix: foo.h\r\n" "bar\r\n", - "Note: inc file prefix:"); + "Note: inc file prefix:", &output, &err)); ASSERT_EQ("foo\nbar\n", output); ASSERT_EQ(1u, parser.includes_.size()); @@ -59,20 +60,22 @@ TEST(CLParserTest, ParseSimple) { TEST(CLParserTest, ParseFilenameFilter) { CLParser parser; - string output = parser.Parse( + string output, err; + ASSERT_TRUE(parser.Parse( "foo.cc\r\n" "cl: warning\r\n", - ""); + "", &output, &err)); ASSERT_EQ("cl: warning\n", output); } TEST(CLParserTest, ParseSystemInclude) { CLParser parser; - string output = parser.Parse( + string output, err; + ASSERT_TRUE(parser.Parse( "Note: including file: c:\\Program Files\\foo.h\r\n" "Note: including file: d:\\Microsoft Visual Studio\\bar.h\r\n" "Note: including file: path.h\r\n", - ""); + "", &output, &err)); // We should have dropped the first two includes because they look like // system headers. ASSERT_EQ("", output); @@ -82,11 +85,12 @@ TEST(CLParserTest, ParseSystemInclude) { TEST(CLParserTest, DuplicatedHeader) { CLParser parser; - string output = parser.Parse( + string output, err; + ASSERT_TRUE(parser.Parse( "Note: including file: foo.h\r\n" "Note: including file: bar.h\r\n" "Note: including file: foo.h\r\n", - ""); + "", &output, &err)); // We should have dropped one copy of foo.h. ASSERT_EQ("", output); ASSERT_EQ(2u, parser.includes_.size()); @@ -94,11 +98,12 @@ TEST(CLParserTest, DuplicatedHeader) { TEST(CLParserTest, DuplicatedHeaderPathConverted) { CLParser parser; - string output = parser.Parse( + string output, err; + ASSERT_TRUE(parser.Parse( "Note: including file: sub/foo.h\r\n" "Note: including file: bar.h\r\n" "Note: including file: sub\\foo.h\r\n", - ""); + "", &output, &err)); // We should have dropped one copy of foo.h. ASSERT_EQ("", output); ASSERT_EQ(2u, parser.includes_.size()); -- cgit v0.12 From 312c6aa131943408e9a0268c11806369f84063e3 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 18 Jun 2015 15:52:25 -0700 Subject: don't alias input/output in ExtractDeps (i.e. actually works now) --- src/build.cc | 4 +++- src/msvc_helper-win32.cc | 2 ++ src/msvc_helper.h | 5 +++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/build.cc b/src/build.cc index fcde626..e4820d0 100644 --- a/src/build.cc +++ b/src/build.cc @@ -849,8 +849,10 @@ bool Builder::ExtractDeps(CommandRunner::Result* result, #ifdef _WIN32 if (deps_type == "msvc") { CLParser parser; - if (!parser.Parse(result->output, deps_prefix, &result->output, err)) + string output; + if (!parser.Parse(result->output, deps_prefix, &output, err)) return false; + result->output = output; for (set::iterator i = parser.includes_.begin(); i != parser.includes_.end(); ++i) { // ~0 is assuming that with MSVC-parsed headers, it's ok to always make diff --git a/src/msvc_helper-win32.cc b/src/msvc_helper-win32.cc index 78b79b0..d516240 100644 --- a/src/msvc_helper-win32.cc +++ b/src/msvc_helper-win32.cc @@ -15,6 +15,7 @@ #include "msvc_helper.h" #include +#include #include #include #include @@ -85,6 +86,7 @@ bool CLParser::FilterInputFilename(string line) { bool CLParser::Parse(const string& output, const string& deps_prefix, string* filtered_output, string* err) { // Loop over all lines in the output to process them. + assert(&output != filtered_output); size_t start = 0; while (start < output.size()) { size_t end = output.find_first_of("\r\n", start); diff --git a/src/msvc_helper.h b/src/msvc_helper.h index 08c0ad5..30f87f3 100644 --- a/src/msvc_helper.h +++ b/src/msvc_helper.h @@ -40,8 +40,9 @@ struct CLParser { /// Exposed for testing. static bool FilterInputFilename(string line); - /// Parse the full output of cl, returning the output (if any) that - /// should printed. Returns true on success, or false with err filled. + /// Parse the full output of cl, filling filtered_output with the text that + /// should be printed (if any). Returns true on success, or false with err + /// filled. output must not be the same object as filtered_object. bool Parse(const string& output, const string& deps_prefix, string* filtered_output, string* err); -- cgit v0.12 From 1beea932bc1dcf5a4684cc948c8eae6957ee119f Mon Sep 17 00:00:00 2001 From: Jason Haslam Date: Tue, 26 May 2015 15:19:28 -0600 Subject: Allow configure script to bootstrap out of source. --- configure.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/configure.py b/configure.py index 2eacbfe..69f6075 100755 --- a/configure.py +++ b/configure.py @@ -28,7 +28,8 @@ import string import subprocess import sys -sys.path.insert(0, 'misc') +sourcedir = os.path.dirname(os.path.realpath(__file__)) +sys.path.insert(0, os.path.join(sourcedir, 'misc')) import ninja_syntax @@ -251,11 +252,11 @@ if platform.is_msvc(): objext = '.obj' def src(filename): - return os.path.join('src', filename) + return os.path.join('$sourcedir', 'src', filename) def built(filename): return os.path.join('$builddir', filename) def doc(filename): - return os.path.join('doc', filename) + return os.path.join('$sourcedir', 'doc', filename) def cc(name, **kwargs): return n.build(built(name + objext), 'cxx', src(name + '.c'), **kwargs) def cxx(name, **kwargs): @@ -267,6 +268,7 @@ def binary(name): return exe return name +n.variable('sourcedir', sourcedir) n.variable('builddir', 'build') n.variable('cxx', CXX) if platform.is_msvc(): @@ -415,10 +417,10 @@ objs = [] if platform.supports_ninja_browse(): n.comment('browse_py.h is used to inline browse.py.') n.rule('inline', - command='src/inline.sh $varname < $in > $out', + command=src('inline.sh') + ' $varname < $in > $out', description='INLINE $out') n.build(built('browse_py.h'), 'inline', src('browse.py'), - implicit='src/inline.sh', + implicit=src('inline.sh'), variables=[('varname', 'kBrowsePy')]) n.newline() @@ -591,11 +593,12 @@ n.newline() if not host.is_mingw(): n.comment('Regenerate build files if build script changes.') n.rule('configure', - command='${configure_env}%s configure.py $configure_args' % + command='${configure_env}%s $sourcedir/configure.py $configure_args' % options.with_python, generator=True) n.build('build.ninja', 'configure', - implicit=['configure.py', os.path.normpath('misc/ninja_syntax.py')]) + implicit=['$sourcedir/configure.py', + os.path.normpath('$sourcedir/misc/ninja_syntax.py')]) n.newline() n.default(ninja) -- cgit v0.12 From d3441a03386e0d1766c1a4b79d5fa5098786860f Mon Sep 17 00:00:00 2001 From: Jason Haslam Date: Tue, 26 May 2015 16:47:18 -0600 Subject: Search for generated headers relative to build dir. --- configure.py | 3 +++ src/browse.cc | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/configure.py b/configure.py index 69f6075..27516b4 100755 --- a/configure.py +++ b/configure.py @@ -355,6 +355,9 @@ if platform.supports_ppoll() and not options.force_pselect: if platform.supports_ninja_browse(): cflags.append('-DNINJA_HAVE_BROWSE') +# Search for generated headers relative to build dir. +cflags.append('-I.') + def shell_escape(str): """Escape str such that it's interpreted as a single argument by the shell.""" diff --git a/src/browse.cc b/src/browse.cc index 83bfe43..8673919 100644 --- a/src/browse.cc +++ b/src/browse.cc @@ -18,7 +18,7 @@ #include #include -#include "../build/browse_py.h" +#include "build/browse_py.h" void RunBrowsePython(State* state, const char* ninja_command, const char* initial_target) { -- cgit v0.12 From 5d4473d548ec839ee2c5acc006db682afad0bd15 Mon Sep 17 00:00:00 2001 From: Jason Haslam Date: Mon, 22 Jun 2015 16:28:04 -0600 Subject: Fix bootstrap from a source path containing spaces. --- configure.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/configure.py b/configure.py index 27516b4..7413659 100755 --- a/configure.py +++ b/configure.py @@ -156,12 +156,16 @@ class Bootstrap: def _expand_paths(self, paths): """Expand $vars in an array of paths, e.g. from a 'build' block.""" paths = ninja_syntax.as_list(paths) - return ' '.join(map(self._expand, paths)) + return ' '.join(map(self._shell_escape, (map(self._expand, paths)))) def _expand(self, str, local_vars={}): """Expand $vars in a string.""" return ninja_syntax.expand(str, self.vars, local_vars) + def _shell_escape(self, path): + """Quote paths containing spaces.""" + return '"%s"' % path if ' ' in path else path + def _run_command(self, cmdline): """Run a subcommand, quietly. Prints the full command on error.""" try: @@ -420,7 +424,7 @@ objs = [] if platform.supports_ninja_browse(): n.comment('browse_py.h is used to inline browse.py.') n.rule('inline', - command=src('inline.sh') + ' $varname < $in > $out', + command='"%s"' % src('inline.sh') + ' $varname < $in > $out', description='INLINE $out') n.build(built('browse_py.h'), 'inline', src('browse.py'), implicit=src('inline.sh'), -- cgit v0.12 From 53e5aed022b09d38e219b31cc9af4574cb948327 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Tue, 30 Jun 2015 15:26:20 -0700 Subject: point people at more detailed build docs if they're on Windows It's a hard balance between "most people want to run configure --bootstrap" and "here's a massive wall of text of every detail about building". I think the balance in this change is better than it was before -- most people on Windows don't want to build their own binary. Fixes #983. --- HACKING.md | 20 ++++++++++++++++++-- README | 11 ++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/HACKING.md b/HACKING.md index 9c6830f..1ba520c 100644 --- a/HACKING.md +++ b/HACKING.md @@ -2,10 +2,26 @@ `./configure.py` generates the `build.ninja` files used to build ninja. It accepts various flags to adjust build parameters. +Run './configure.py --help' for more configuration options. The primary build target of interest is `ninja`, but when hacking on -Ninja your changes should be testable so it's more useful to build -and run `ninja_test` when developing. +Ninja your changes should be testable so it's more useful to build and +run `ninja_test` when developing. + +### Bootstrapping + +Ninja is built using itself. To bootstrap the first binary, run the +configure script as `./configure.py --bootstrap`. It This first +compiles all non-test source files together, then re-builds Ninja +using itself. You should end up with a `ninja' binary (or +`ninja.exe`) in the source root. + +#### Windows + +On Windows, you'll need to install Python to run `configure.py`, and +run everything under a Visual Studio Tools Command Prompt (or after +running `vcvarsall` in a normal command prompt). See below if you +want to use mingw or some other compiler instead using Visual Studio. ### Adjusting build flags diff --git a/README b/README index 41ecdda..66a6516 100644 --- a/README +++ b/README @@ -5,13 +5,14 @@ See the manual -- http://martine.github.com/ninja/manual.html or doc/manual.asciidoc included in the distribution -- for background and more details. -To build, run ./configure.py --bootstrap. It first compiles all non-test -source files together, then re-builds Ninja using itself. You should -end up with a 'ninja' binary in the source root. - -Run './configure.py --help' for more configuration options. +Binaries for Linux, Mac, and Windows are available at + https://github.com/martine/ninja/releases Run './ninja -h' for Ninja help. +To build your own binary, on many platforms it should be sufficient to +just run `./configure.py --bootstrap`; for more details see HACKING.md. +(Also read that before making changes to Ninja, as it has advice.) + Installation is not necessary because the only required file is is the resulting ninja binary. However, to enable features like Bash completion and Emacs and Vim editing modes, some files in misc/ must be -- cgit v0.12 From c81c127a0c2a20d6c6e05ed5710fc6002565e401 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Wed, 1 Jul 2015 17:52:16 -0700 Subject: fix typos, punctuation, redundant words in HACKING.md --- HACKING.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/HACKING.md b/HACKING.md index 1ba520c..7fdb152 100644 --- a/HACKING.md +++ b/HACKING.md @@ -11,10 +11,9 @@ run `ninja_test` when developing. ### Bootstrapping Ninja is built using itself. To bootstrap the first binary, run the -configure script as `./configure.py --bootstrap`. It This first -compiles all non-test source files together, then re-builds Ninja -using itself. You should end up with a `ninja' binary (or -`ninja.exe`) in the source root. +configure script as `./configure.py --bootstrap`. This first compiles +all non-test source files together, then re-builds Ninja using itself. +You should end up with a `ninja` binary (or `ninja.exe`) in the source root. #### Windows @@ -192,7 +191,7 @@ root directory: ./ninja_test gcov build/*.o -Look at the generated `.gcov` files directly, or use your favorit gcov viewer. +Look at the generated `.gcov` files directly, or use your favorite gcov viewer. ### Using afl-fuzz -- cgit v0.12 From f30508e79dad5c9e8903214a6b9d578e10318960 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Fri, 10 Jul 2015 12:15:00 -0700 Subject: win: print right slashes in 'unknown target' message --- src/graph.cc | 7 ++++--- src/graph.h | 7 ++++++- src/ninja.cc | 6 +++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/graph.cc b/src/graph.cc index 355285c..9e65675 100644 --- a/src/graph.cc +++ b/src/graph.cc @@ -347,12 +347,13 @@ bool Edge::use_console() const { return pool() == &State::kConsolePool; } -string Node::PathDecanonicalized() const { - string result = path_; +// static +string Node::PathDecanonicalized(const string& path, unsigned int slash_bits) { + string result = path; #ifdef _WIN32 unsigned int mask = 1; for (char* c = &result[0]; (c = strchr(c, '/')) != NULL;) { - if (slash_bits_ & mask) + if (slash_bits & mask) *c = '\\'; c++; mask <<= 1; diff --git a/src/graph.h b/src/graph.h index 5f8d41a..cf15123 100644 --- a/src/graph.h +++ b/src/graph.h @@ -72,8 +72,13 @@ struct Node { const string& path() const { return path_; } /// Get |path()| but use slash_bits to convert back to original slash styles. - string PathDecanonicalized() const; + string PathDecanonicalized() const { + return PathDecanonicalized(path_, slash_bits_); + } + static string PathDecanonicalized(const string& path, + unsigned int slash_bits); unsigned int slash_bits() const { return slash_bits_; } + TimeStamp mtime() const { return mtime_; } bool dirty() const { return dirty_; } diff --git a/src/ninja.cc b/src/ninja.cc index e5bf98d..3756123 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -260,7 +260,7 @@ bool NinjaMain::RebuildManifest(const char* input_file, string* err) { Node* NinjaMain::CollectTarget(const char* cpath, string* err) { string path = cpath; - unsigned int slash_bits; // Unused because this path is only used for lookup. + unsigned int slash_bits; if (!CanonicalizePath(&path, &slash_bits, err)) return NULL; @@ -287,8 +287,8 @@ Node* NinjaMain::CollectTarget(const char* cpath, string* err) { } return node; } else { - *err = "unknown target '" + path + "'"; - + *err = + "unknown target '" + Node::PathDecanonicalized(path, slash_bits) + "'"; if (path == "clean") { *err += ", did you mean 'ninja -t clean'?"; } else if (path == "help") { -- cgit v0.12 From 4ada4e7ec92b3f9f1fab6e199e86bbf4dbe49ef5 Mon Sep 17 00:00:00 2001 From: Mike Seplowitz Date: Tue, 21 Jul 2015 18:13:26 -0400 Subject: Correct order of ASSERT_EQ arguments The correct order is ASSERT_EQ(expected, actual). --- src/depfile_parser_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/depfile_parser_test.cc b/src/depfile_parser_test.cc index 8b57a1e..fe9424a 100644 --- a/src/depfile_parser_test.cc +++ b/src/depfile_parser_test.cc @@ -139,8 +139,8 @@ TEST_F(DepfileParserTest, UnifyMultipleOutputs) { // check that multiple duplicate targets are properly unified string err; EXPECT_TRUE(Parse("foo foo: x y z", &err)); - ASSERT_EQ(parser_.out_.AsString(), "foo"); - ASSERT_EQ(parser_.ins_.size(), 3u); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); EXPECT_EQ("x", parser_.ins_[0].AsString()); EXPECT_EQ("y", parser_.ins_[1].AsString()); EXPECT_EQ("z", parser_.ins_[2].AsString()); -- cgit v0.12 From a4751630cd6d2d753505a6fd222277cf2d99a135 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 30 Jul 2015 17:30:52 -0700 Subject: Print status when edge finishes on dumb terminals On smart terminals ninja prints the status line both before and after running a command, reusing the same line if possible. On a dumb terminal that doesn't support reusing the line, it only prints the status before starting the command, but prints the output of the command when the command finishes, by which point other commands may have started and printed their status line. This makes it impossible to determine what command produced a line of output. Modify BuildEdgeStarted to only print the status line if the command is going to lock the console, or if ninja is running on a smart terminal. Modify BuildEdgeFinished to always print the status line unless the command locked the console, in which case the status was already printed and no other command can have printed any lines. The end result will be dumb terminal output that much more closely matches smart terminal output. One disadvantage is that dumb terminals won't show anything when starting a command, making it harder to tell what commands are currently running, but I expect most interactive uses of ninja will use a smart terminal. --- src/build.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/build.cc b/src/build.cc index e4820d0..3703fc4 100644 --- a/src/build.cc +++ b/src/build.cc @@ -96,7 +96,8 @@ void BuildStatus::BuildEdgeStarted(Edge* edge) { running_edges_.insert(make_pair(edge, start_time)); ++started_edges_; - PrintStatus(edge); + if (edge->use_console() || printer_.is_smart_terminal()) + PrintStatus(edge); if (edge->use_console()) printer_.SetConsoleLocked(true); @@ -121,7 +122,7 @@ void BuildStatus::BuildEdgeFinished(Edge* edge, if (config_.verbosity == BuildConfig::QUIET) return; - if (!edge->use_console() && printer_.is_smart_terminal()) + if (!edge->use_console()) PrintStatus(edge); // Print the command that is spewing before printing its output. -- cgit v0.12 From de1dd6e0641b4aa279f9385f6c71a77c4efc31d4 Mon Sep 17 00:00:00 2001 From: Lindley French Date: Thu, 30 Jul 2015 17:33:11 -0700 Subject: Make sure not to re-define __STDC_FORMAT_MACROS. --- src/build_log.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/build_log.cc b/src/build_log.cc index 281b851..589c6da 100644 --- a/src/build_log.cc +++ b/src/build_log.cc @@ -19,7 +19,9 @@ #include #ifndef _WIN32 +#ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS +#endif #include #include #endif -- cgit v0.12 From a4327b9fb07f1ea678e92c1f06aeb22c129827e2 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Sun, 9 Aug 2015 21:02:23 -0700 Subject: Set _HAS_EXCEPTIONS=0 on MSVC --- configure.py | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.py b/configure.py index 7413659..b8d7096 100755 --- a/configure.py +++ b/configure.py @@ -297,6 +297,7 @@ if platform.is_msvc(): # We never have strings or arrays larger than 2**31. '/wd4267', '/DNOMINMAX', '/D_CRT_SECURE_NO_WARNINGS', + '/D_HAS_EXCEPTIONS=0', '/DNINJA_PYTHON="%s"' % options.with_python] if options.bootstrap: # In bootstrap mode, we have no ninja process to catch /showIncludes -- cgit v0.12 From a6265082f4d5e2ca08284117ae1a8f037379f5bf Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Sun, 9 Aug 2015 21:25:09 -0700 Subject: Some mucking with std:: for set_terminate and terminate_handler --- src/depfile_parser.cc | 476 +++++++------- src/graphviz.h | 5 +- src/lexer.cc | 1734 ++++++++++++++++++++++++------------------------- src/ninja.cc | 2 +- 4 files changed, 1108 insertions(+), 1109 deletions(-) diff --git a/src/depfile_parser.cc b/src/depfile_parser.cc index 7268f31..c5bc291 100644 --- a/src/depfile_parser.cc +++ b/src/depfile_parser.cc @@ -1,238 +1,238 @@ -/* Generated by re2c 0.13.5 */ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "depfile_parser.h" - -// A note on backslashes in Makefiles, from reading the docs: -// Backslash-newline is the line continuation character. -// Backslash-# escapes a # (otherwise meaningful as a comment start). -// Backslash-% escapes a % (otherwise meaningful as a special). -// Finally, quoting the GNU manual, "Backslashes that are not in danger -// of quoting ‘%’ characters go unmolested." -// How do you end a line with a backslash? The netbsd Make docs suggest -// reading the result of a shell command echoing a backslash! -// -// Rather than implement all of above, we do a simpler thing here: -// Backslashes escape a set of characters (see "escapes" defined below), -// otherwise they are passed through verbatim. -// If anyone actually has depfiles that rely on the more complicated -// behavior we can adjust this. -bool DepfileParser::Parse(string* content, string* err) { - // in: current parser input point. - // end: end of input. - // parsing_targets: whether we are parsing targets or dependencies. - char* in = &(*content)[0]; - char* end = in + content->size(); - bool parsing_targets = true; - while (in < end) { - // out: current output point (typically same as in, but can fall behind - // as we de-escape backslashes). - char* out = in; - // filename: start of the current parsed filename. - char* filename = out; - for (;;) { - // start: beginning of the current parsed span. - const char* start = in; - - { - char yych; - static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 128, 0, 0, 0, 0, 0, 0, - 128, 128, 0, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 0, 0, 128, 0, 0, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 0, 0, 0, 0, 128, - 0, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 0, 128, 128, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - }; - - yych = *in; - if (yych <= '=') { - if (yych <= '$') { - if (yych <= ' ') { - if (yych <= 0x00) goto yy7; - goto yy9; - } else { - if (yych <= '!') goto yy5; - if (yych <= '#') goto yy9; - goto yy4; - } - } else { - if (yych <= '*') { - if (yych <= '\'') goto yy9; - if (yych <= ')') goto yy5; - goto yy9; - } else { - if (yych <= ':') goto yy5; - if (yych <= '<') goto yy9; - goto yy5; - } - } - } else { - if (yych <= '^') { - if (yych <= 'Z') { - if (yych <= '?') goto yy9; - goto yy5; - } else { - if (yych != '\\') goto yy9; - } - } else { - if (yych <= '{') { - if (yych == '`') goto yy9; - goto yy5; - } else { - if (yych <= '|') goto yy9; - if (yych <= '~') goto yy5; - goto yy9; - } - } - } - ++in; - if ((yych = *in) <= '"') { - if (yych <= '\f') { - if (yych <= 0x00) goto yy3; - if (yych != '\n') goto yy14; - } else { - if (yych <= '\r') goto yy3; - if (yych == ' ') goto yy16; - goto yy14; - } - } else { - if (yych <= 'Z') { - if (yych <= '#') goto yy16; - if (yych == '*') goto yy16; - goto yy14; - } else { - if (yych <= '\\') goto yy16; - if (yych == '|') goto yy16; - goto yy14; - } - } -yy3: - { - // For any other character (e.g. whitespace), swallow it here, - // allowing the outer logic to loop around again. - break; - } -yy4: - yych = *++in; - if (yych == '$') goto yy12; - goto yy3; -yy5: - ++in; - yych = *in; - goto yy11; -yy6: - { - // Got a span of plain text. - int len = (int)(in - start); - // Need to shift it over if we're overwriting backslashes. - if (out < start) - memmove(out, start, len); - out += len; - continue; - } -yy7: - ++in; - { - break; - } -yy9: - yych = *++in; - goto yy3; -yy10: - ++in; - yych = *in; -yy11: - if (yybm[0+yych] & 128) { - goto yy10; - } - goto yy6; -yy12: - ++in; - { - // De-escape dollar character. - *out++ = '$'; - continue; - } -yy14: - ++in; - { - // Let backslash before other characters through verbatim. - *out++ = '\\'; - *out++ = yych; - continue; - } -yy16: - ++in; - { - // De-escape backslashed character. - *out++ = yych; - continue; - } - } - - } - - int len = (int)(out - filename); - const bool is_target = parsing_targets; - if (len > 0 && filename[len - 1] == ':') { - len--; // Strip off trailing colon, if any. - parsing_targets = false; - } - - if (len == 0) - continue; - - if (!is_target) { - ins_.push_back(StringPiece(filename, len)); - } else if (!out_.str_) { - out_ = StringPiece(filename, len); - } else if (out_ != StringPiece(filename, len)) { - *err = "depfile has multiple output paths"; - return false; - } - } - if (parsing_targets) { - *err = "expected ':' in depfile"; - return false; - } - return true; -} +/* Generated by re2c 0.13.5 */ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "depfile_parser.h" + +// A note on backslashes in Makefiles, from reading the docs: +// Backslash-newline is the line continuation character. +// Backslash-# escapes a # (otherwise meaningful as a comment start). +// Backslash-% escapes a % (otherwise meaningful as a special). +// Finally, quoting the GNU manual, "Backslashes that are not in danger +// of quoting ‘%’ characters go unmolested." +// How do you end a line with a backslash? The netbsd Make docs suggest +// reading the result of a shell command echoing a backslash! +// +// Rather than implement all of above, we do a simpler thing here: +// Backslashes escape a set of characters (see "escapes" defined below), +// otherwise they are passed through verbatim. +// If anyone actually has depfiles that rely on the more complicated +// behavior we can adjust this. +bool DepfileParser::Parse(string* content, string* err) { + // in: current parser input point. + // end: end of input. + // parsing_targets: whether we are parsing targets or dependencies. + char* in = &(*content)[0]; + char* end = in + content->size(); + bool parsing_targets = true; + while (in < end) { + // out: current output point (typically same as in, but can fall behind + // as we de-escape backslashes). + char* out = in; + // filename: start of the current parsed filename. + char* filename = out; + for (;;) { + // start: beginning of the current parsed span. + const char* start = in; + + { + char yych; + static const unsigned char yybm[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 128, 0, 0, 0, 0, 0, 0, + 128, 128, 0, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 0, 0, 128, 0, 0, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 0, 0, 0, 0, 128, + 0, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 0, 128, 128, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }; + + yych = *in; + if (yych <= '=') { + if (yych <= '$') { + if (yych <= ' ') { + if (yych <= 0x00) goto yy7; + goto yy9; + } else { + if (yych <= '!') goto yy5; + if (yych <= '#') goto yy9; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '\'') goto yy9; + if (yych <= ')') goto yy5; + goto yy9; + } else { + if (yych <= ':') goto yy5; + if (yych <= '<') goto yy9; + goto yy5; + } + } + } else { + if (yych <= '^') { + if (yych <= 'Z') { + if (yych <= '?') goto yy9; + goto yy5; + } else { + if (yych != '\\') goto yy9; + } + } else { + if (yych <= '{') { + if (yych == '`') goto yy9; + goto yy5; + } else { + if (yych <= '|') goto yy9; + if (yych <= '~') goto yy5; + goto yy9; + } + } + } + ++in; + if ((yych = *in) <= '"') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy3; + if (yych != '\n') goto yy14; + } else { + if (yych <= '\r') goto yy3; + if (yych == ' ') goto yy16; + goto yy14; + } + } else { + if (yych <= 'Z') { + if (yych <= '#') goto yy16; + if (yych == '*') goto yy16; + goto yy14; + } else { + if (yych <= '\\') goto yy16; + if (yych == '|') goto yy16; + goto yy14; + } + } +yy3: + { + // For any other character (e.g. whitespace), swallow it here, + // allowing the outer logic to loop around again. + break; + } +yy4: + yych = *++in; + if (yych == '$') goto yy12; + goto yy3; +yy5: + ++in; + yych = *in; + goto yy11; +yy6: + { + // Got a span of plain text. + int len = (int)(in - start); + // Need to shift it over if we're overwriting backslashes. + if (out < start) + memmove(out, start, len); + out += len; + continue; + } +yy7: + ++in; + { + break; + } +yy9: + yych = *++in; + goto yy3; +yy10: + ++in; + yych = *in; +yy11: + if (yybm[0+yych] & 128) { + goto yy10; + } + goto yy6; +yy12: + ++in; + { + // De-escape dollar character. + *out++ = '$'; + continue; + } +yy14: + ++in; + { + // Let backslash before other characters through verbatim. + *out++ = '\\'; + *out++ = yych; + continue; + } +yy16: + ++in; + { + // De-escape backslashed character. + *out++ = yych; + continue; + } + } + + } + + int len = (int)(out - filename); + const bool is_target = parsing_targets; + if (len > 0 && filename[len - 1] == ':') { + len--; // Strip off trailing colon, if any. + parsing_targets = false; + } + + if (len == 0) + continue; + + if (!is_target) { + ins_.push_back(StringPiece(filename, len)); + } else if (!out_.str_) { + out_ = StringPiece(filename, len); + } else if (out_ != StringPiece(filename, len)) { + *err = "depfile has multiple output paths"; + return false; + } + } + if (parsing_targets) { + *err = "expected ':' in depfile"; + return false; + } + return true; +} diff --git a/src/graphviz.h b/src/graphviz.h index 1e2a29d..408496d 100644 --- a/src/graphviz.h +++ b/src/graphviz.h @@ -16,7 +16,6 @@ #define NINJA_GRAPHVIZ_H_ #include -using namespace std; struct Node; struct Edge; @@ -27,8 +26,8 @@ struct GraphViz { void AddTarget(Node* node); void Finish(); - set visited_nodes_; - set visited_edges_; + std::set visited_nodes_; + std::set visited_edges_; }; #endif // NINJA_GRAPHVIZ_H_ diff --git a/src/lexer.cc b/src/lexer.cc index 37b8678..81264bd 100644 --- a/src/lexer.cc +++ b/src/lexer.cc @@ -1,867 +1,867 @@ -/* Generated by re2c 0.13.5 */ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "lexer.h" - -#include - -#include "eval_env.h" -#include "util.h" - -bool Lexer::Error(const string& message, string* err) { - // Compute line/column. - int line = 1; - const char* context = input_.str_; - for (const char* p = input_.str_; p < last_token_; ++p) { - if (*p == '\n') { - ++line; - context = p + 1; - } - } - int col = last_token_ ? (int)(last_token_ - context) : 0; - - char buf[1024]; - snprintf(buf, sizeof(buf), "%s:%d: ", filename_.AsString().c_str(), line); - *err = buf; - *err += message + "\n"; - - // Add some context to the message. - const int kTruncateColumn = 72; - if (col > 0 && col < kTruncateColumn) { - int len; - bool truncated = true; - for (len = 0; len < kTruncateColumn; ++len) { - if (context[len] == 0 || context[len] == '\n') { - truncated = false; - break; - } - } - *err += string(context, len); - if (truncated) - *err += "..."; - *err += "\n"; - *err += string(col, ' '); - *err += "^ near here"; - } - - return false; -} - -Lexer::Lexer(const char* input) { - Start("input", input); -} - -void Lexer::Start(StringPiece filename, StringPiece input) { - filename_ = filename; - input_ = input; - ofs_ = input_.str_; - last_token_ = NULL; -} - -const char* Lexer::TokenName(Token t) { - switch (t) { - case ERROR: return "lexing error"; - case BUILD: return "'build'"; - case COLON: return "':'"; - case DEFAULT: return "'default'"; - case EQUALS: return "'='"; - case IDENT: return "identifier"; - case INCLUDE: return "'include'"; - case INDENT: return "indent"; - 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"; - } - return NULL; // not reached -} - -const char* Lexer::TokenErrorHint(Token expected) { - switch (expected) { - case COLON: - return " ($ also escapes ':')"; - default: - return ""; - } -} - -string Lexer::DescribeLastError() { - if (last_token_) { - switch (last_token_[0]) { - case '\t': - return "tabs are not allowed, use spaces"; - } - } - return "lexing error"; -} - -void Lexer::UnreadToken() { - ofs_ = last_token_; -} - -Lexer::Token Lexer::ReadToken() { - const char* p = ofs_; - const char* q; - const char* start; - Lexer::Token token; - for (;;) { - start = p; - -{ - unsigned char yych; - unsigned int yyaccept = 0; - static const unsigned char yybm[] = { - 0, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 0, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 192, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 96, 96, 64, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 64, 64, 64, 64, 64, 64, - 64, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 64, 64, 64, 64, 96, - 64, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - }; - - yych = *p; - if (yych <= 'Z') { - if (yych <= '#') { - if (yych <= '\f') { - if (yych <= 0x00) goto yy23; - if (yych == '\n') goto yy7; - goto yy25; - } else { - if (yych <= 0x1F) { - if (yych <= '\r') goto yy6; - goto yy25; - } else { - if (yych <= ' ') goto yy2; - if (yych <= '"') goto yy25; - goto yy4; - } - } - } else { - if (yych <= '9') { - if (yych <= ',') goto yy25; - if (yych == '/') goto yy25; - goto yy22; - } else { - if (yych <= '<') { - if (yych <= ':') goto yy16; - goto yy25; - } else { - if (yych <= '=') goto yy14; - if (yych <= '@') goto yy25; - goto yy22; - } - } - } - } else { - if (yych <= 'i') { - if (yych <= 'a') { - if (yych == '_') goto yy22; - if (yych <= '`') goto yy25; - goto yy22; - } else { - if (yych <= 'c') { - if (yych <= 'b') goto yy9; - goto yy22; - } else { - if (yych <= 'd') goto yy13; - if (yych <= 'h') goto yy22; - goto yy20; - } - } - } else { - if (yych <= 'r') { - if (yych == 'p') goto yy11; - if (yych <= 'q') goto yy22; - goto yy12; - } else { - if (yych <= 'z') { - if (yych <= 's') goto yy21; - goto yy22; - } else { - if (yych == '|') goto yy18; - goto yy25; - } - } - } - } -yy2: - yyaccept = 0; - yych = *(q = ++p); - goto yy73; -yy3: - { token = INDENT; break; } -yy4: - yyaccept = 1; - yych = *(q = ++p); - if (yych >= 0x01) goto yy68; -yy5: - { token = ERROR; break; } -yy6: - yych = *++p; - if (yych == '\n') goto yy65; - goto yy5; -yy7: - ++p; -yy8: - { token = NEWLINE; break; } -yy9: - ++p; - if ((yych = *p) == 'u') goto yy60; - goto yy27; -yy10: - { token = IDENT; break; } -yy11: - yych = *++p; - if (yych == 'o') goto yy56; - goto yy27; -yy12: - yych = *++p; - if (yych == 'u') goto yy52; - goto yy27; -yy13: - yych = *++p; - if (yych == 'e') goto yy45; - goto yy27; -yy14: - ++p; - { token = EQUALS; break; } -yy16: - ++p; - { token = COLON; break; } -yy18: - ++p; - if ((yych = *p) == '|') goto yy43; - { token = PIPE; break; } -yy20: - yych = *++p; - if (yych == 'n') goto yy36; - goto yy27; -yy21: - yych = *++p; - if (yych == 'u') goto yy28; - goto yy27; -yy22: - yych = *++p; - goto yy27; -yy23: - ++p; - { token = TEOF; break; } -yy25: - yych = *++p; - goto yy5; -yy26: - ++p; - yych = *p; -yy27: - if (yybm[0+yych] & 32) { - goto yy26; - } - goto yy10; -yy28: - yych = *++p; - if (yych != 'b') goto yy27; - yych = *++p; - if (yych != 'n') goto yy27; - yych = *++p; - if (yych != 'i') goto yy27; - yych = *++p; - if (yych != 'n') goto yy27; - yych = *++p; - if (yych != 'j') goto yy27; - yych = *++p; - if (yych != 'a') goto yy27; - ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; - } - { token = SUBNINJA; break; } -yy36: - yych = *++p; - if (yych != 'c') goto yy27; - yych = *++p; - if (yych != 'l') goto yy27; - yych = *++p; - if (yych != 'u') goto yy27; - yych = *++p; - if (yych != 'd') goto yy27; - yych = *++p; - if (yych != 'e') goto yy27; - ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; - } - { token = INCLUDE; break; } -yy43: - ++p; - { token = PIPE2; break; } -yy45: - yych = *++p; - if (yych != 'f') goto yy27; - yych = *++p; - if (yych != 'a') goto yy27; - yych = *++p; - if (yych != 'u') goto yy27; - yych = *++p; - if (yych != 'l') goto yy27; - yych = *++p; - if (yych != 't') goto yy27; - ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; - } - { token = DEFAULT; break; } -yy52: - yych = *++p; - if (yych != 'l') goto yy27; - yych = *++p; - if (yych != 'e') goto yy27; - ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; - } - { token = RULE; break; } -yy56: - yych = *++p; - if (yych != 'o') goto yy27; - yych = *++p; - if (yych != 'l') goto yy27; - ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; - } - { token = POOL; break; } -yy60: - yych = *++p; - if (yych != 'i') goto yy27; - yych = *++p; - if (yych != 'l') goto yy27; - yych = *++p; - if (yych != 'd') goto yy27; - ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; - } - { token = BUILD; break; } -yy65: - ++p; - { token = NEWLINE; break; } -yy67: - ++p; - yych = *p; -yy68: - if (yybm[0+yych] & 64) { - goto yy67; - } - if (yych >= 0x01) goto yy70; -yy69: - p = q; - if (yyaccept <= 0) { - goto yy3; - } else { - goto yy5; - } -yy70: - ++p; - { continue; } -yy72: - yyaccept = 0; - q = ++p; - yych = *p; -yy73: - if (yybm[0+yych] & 128) { - goto yy72; - } - if (yych <= '\f') { - if (yych != '\n') goto yy3; - } else { - if (yych <= '\r') goto yy75; - if (yych == '#') goto yy67; - goto yy3; - } - yych = *++p; - goto yy8; -yy75: - ++p; - if ((yych = *p) == '\n') goto yy65; - goto yy69; -} - - } - - last_token_ = start; - ofs_ = p; - if (token != NEWLINE && token != TEOF) - EatWhitespace(); - return token; -} - -bool Lexer::PeekToken(Token token) { - Token t = ReadToken(); - if (t == token) - return true; - UnreadToken(); - return false; -} - -void Lexer::EatWhitespace() { - const char* p = ofs_; - const char* q; - for (;;) { - ofs_ = p; - -{ - unsigned char yych; - static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 128, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - }; - yych = *p; - if (yych <= ' ') { - if (yych <= 0x00) goto yy82; - if (yych <= 0x1F) goto yy84; - } else { - if (yych == '$') goto yy80; - goto yy84; - } - ++p; - yych = *p; - goto yy92; -yy79: - { continue; } -yy80: - yych = *(q = ++p); - if (yych == '\n') goto yy85; - if (yych == '\r') goto yy87; -yy81: - { break; } -yy82: - ++p; - { break; } -yy84: - yych = *++p; - goto yy81; -yy85: - ++p; - { continue; } -yy87: - yych = *++p; - if (yych == '\n') goto yy89; - p = q; - goto yy81; -yy89: - ++p; - { continue; } -yy91: - ++p; - yych = *p; -yy92: - if (yybm[0+yych] & 128) { - goto yy91; - } - goto yy79; -} - - } -} - -bool Lexer::ReadIdent(string* out) { - const char* p = ofs_; - for (;;) { - const char* start = p; - -{ - unsigned char yych; - static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 128, 128, 0, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 0, 0, 0, 0, 0, 0, - 0, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 0, 0, 0, 0, 128, - 0, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - }; - yych = *p; - if (yych <= '@') { - if (yych <= '.') { - if (yych <= ',') goto yy97; - } else { - if (yych <= '/') goto yy97; - if (yych >= ':') goto yy97; - } - } else { - if (yych <= '_') { - if (yych <= 'Z') goto yy95; - if (yych <= '^') goto yy97; - } else { - if (yych <= '`') goto yy97; - if (yych >= '{') goto yy97; - } - } -yy95: - ++p; - yych = *p; - goto yy100; -yy96: - { - out->assign(start, p - start); - break; - } -yy97: - ++p; - { return false; } -yy99: - ++p; - yych = *p; -yy100: - if (yybm[0+yych] & 128) { - goto yy99; - } - goto yy96; -} - - } - ofs_ = p; - EatWhitespace(); - return true; -} - -bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) { - const char* p = ofs_; - const char* q; - const char* start; - for (;;) { - start = p; - -{ - unsigned char yych; - static const unsigned char yybm[] = { - 0, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 0, 128, 128, 0, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 16, 128, 128, 128, 0, 128, 128, 128, - 128, 128, 128, 128, 128, 224, 160, 128, - 224, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 0, 128, 128, 128, 128, 128, - 128, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 224, 128, 128, 128, 128, 224, - 128, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 224, 128, 0, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - }; - yych = *p; - if (yych <= ' ') { - if (yych <= '\n') { - if (yych <= 0x00) goto yy110; - if (yych >= '\n') goto yy107; - } else { - if (yych == '\r') goto yy105; - if (yych >= ' ') goto yy107; - } - } else { - if (yych <= '9') { - if (yych == '$') goto yy109; - } else { - if (yych <= ':') goto yy107; - if (yych == '|') goto yy107; - } - } - ++p; - yych = *p; - goto yy140; -yy104: - { - eval->AddText(StringPiece(start, p - start)); - continue; - } -yy105: - ++p; - if ((yych = *p) == '\n') goto yy137; - { - last_token_ = start; - return Error(DescribeLastError(), err); - } -yy107: - ++p; - { - if (path) { - p = start; - break; - } else { - if (*start == '\n') - break; - eval->AddText(StringPiece(start, 1)); - continue; - } - } -yy109: - yych = *++p; - if (yych <= '-') { - if (yych <= 0x1F) { - if (yych <= '\n') { - if (yych <= '\t') goto yy112; - goto yy124; - } else { - if (yych == '\r') goto yy114; - goto yy112; - } - } else { - if (yych <= '#') { - if (yych <= ' ') goto yy115; - goto yy112; - } else { - if (yych <= '$') goto yy117; - if (yych <= ',') goto yy112; - goto yy119; - } - } - } else { - if (yych <= 'Z') { - if (yych <= '9') { - if (yych <= '/') goto yy112; - goto yy119; - } else { - if (yych <= ':') goto yy121; - if (yych <= '@') goto yy112; - goto yy119; - } - } else { - if (yych <= '`') { - if (yych == '_') goto yy119; - goto yy112; - } else { - if (yych <= 'z') goto yy119; - if (yych <= '{') goto yy123; - goto yy112; - } - } - } -yy110: - ++p; - { - last_token_ = start; - return Error("unexpected EOF", err); - } -yy112: - ++p; -yy113: - { - last_token_ = start; - return Error("bad $-escape (literal $ must be written as $$)", err); - } -yy114: - yych = *++p; - if (yych == '\n') goto yy134; - goto yy113; -yy115: - ++p; - { - eval->AddText(StringPiece(" ", 1)); - continue; - } -yy117: - ++p; - { - eval->AddText(StringPiece("$", 1)); - continue; - } -yy119: - ++p; - yych = *p; - goto yy133; -yy120: - { - eval->AddSpecial(StringPiece(start + 1, p - start - 1)); - continue; - } -yy121: - ++p; - { - eval->AddText(StringPiece(":", 1)); - continue; - } -yy123: - yych = *(q = ++p); - if (yybm[0+yych] & 32) { - goto yy127; - } - goto yy113; -yy124: - ++p; - yych = *p; - if (yybm[0+yych] & 16) { - goto yy124; - } - { - continue; - } -yy127: - ++p; - yych = *p; - if (yybm[0+yych] & 32) { - goto yy127; - } - if (yych == '}') goto yy130; - p = q; - goto yy113; -yy130: - ++p; - { - eval->AddSpecial(StringPiece(start + 2, p - start - 3)); - continue; - } -yy132: - ++p; - yych = *p; -yy133: - if (yybm[0+yych] & 64) { - goto yy132; - } - goto yy120; -yy134: - ++p; - yych = *p; - if (yych == ' ') goto yy134; - { - continue; - } -yy137: - ++p; - { - if (path) - p = start; - break; - } -yy139: - ++p; - yych = *p; -yy140: - if (yybm[0+yych] & 128) { - goto yy139; - } - goto yy104; -} - - } - last_token_ = start; - ofs_ = p; - if (path) - EatWhitespace(); - // Non-path strings end in newlines, so there's no whitespace to eat. - return true; -} +/* Generated by re2c 0.13.5 */ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lexer.h" + +#include + +#include "eval_env.h" +#include "util.h" + +bool Lexer::Error(const string& message, string* err) { + // Compute line/column. + int line = 1; + const char* context = input_.str_; + for (const char* p = input_.str_; p < last_token_; ++p) { + if (*p == '\n') { + ++line; + context = p + 1; + } + } + int col = last_token_ ? (int)(last_token_ - context) : 0; + + char buf[1024]; + snprintf(buf, sizeof(buf), "%s:%d: ", filename_.AsString().c_str(), line); + *err = buf; + *err += message + "\n"; + + // Add some context to the message. + const int kTruncateColumn = 72; + if (col > 0 && col < kTruncateColumn) { + int len; + bool truncated = true; + for (len = 0; len < kTruncateColumn; ++len) { + if (context[len] == 0 || context[len] == '\n') { + truncated = false; + break; + } + } + *err += string(context, len); + if (truncated) + *err += "..."; + *err += "\n"; + *err += string(col, ' '); + *err += "^ near here"; + } + + return false; +} + +Lexer::Lexer(const char* input) { + Start("input", input); +} + +void Lexer::Start(StringPiece filename, StringPiece input) { + filename_ = filename; + input_ = input; + ofs_ = input_.str_; + last_token_ = NULL; +} + +const char* Lexer::TokenName(Token t) { + switch (t) { + case ERROR: return "lexing error"; + case BUILD: return "'build'"; + case COLON: return "':'"; + case DEFAULT: return "'default'"; + case EQUALS: return "'='"; + case IDENT: return "identifier"; + case INCLUDE: return "'include'"; + case INDENT: return "indent"; + 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"; + } + return NULL; // not reached +} + +const char* Lexer::TokenErrorHint(Token expected) { + switch (expected) { + case COLON: + return " ($ also escapes ':')"; + default: + return ""; + } +} + +string Lexer::DescribeLastError() { + if (last_token_) { + switch (last_token_[0]) { + case '\t': + return "tabs are not allowed, use spaces"; + } + } + return "lexing error"; +} + +void Lexer::UnreadToken() { + ofs_ = last_token_; +} + +Lexer::Token Lexer::ReadToken() { + const char* p = ofs_; + const char* q; + const char* start; + Lexer::Token token; + for (;;) { + start = p; + +{ + unsigned char yych; + unsigned int yyaccept = 0; + static const unsigned char yybm[] = { + 0, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 0, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 192, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 96, 96, 64, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 64, 64, 64, 64, 64, 64, + 64, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 64, 64, 64, 64, 96, + 64, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + }; + + yych = *p; + if (yych <= 'Z') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy23; + if (yych == '\n') goto yy7; + goto yy25; + } else { + if (yych <= 0x1F) { + if (yych <= '\r') goto yy6; + goto yy25; + } else { + if (yych <= ' ') goto yy2; + if (yych <= '"') goto yy25; + goto yy4; + } + } + } else { + if (yych <= '9') { + if (yych <= ',') goto yy25; + if (yych == '/') goto yy25; + goto yy22; + } else { + if (yych <= '<') { + if (yych <= ':') goto yy16; + goto yy25; + } else { + if (yych <= '=') goto yy14; + if (yych <= '@') goto yy25; + goto yy22; + } + } + } + } else { + if (yych <= 'i') { + if (yych <= 'a') { + if (yych == '_') goto yy22; + if (yych <= '`') goto yy25; + goto yy22; + } else { + if (yych <= 'c') { + if (yych <= 'b') goto yy9; + goto yy22; + } else { + if (yych <= 'd') goto yy13; + if (yych <= 'h') goto yy22; + goto yy20; + } + } + } else { + if (yych <= 'r') { + if (yych == 'p') goto yy11; + if (yych <= 'q') goto yy22; + goto yy12; + } else { + if (yych <= 'z') { + if (yych <= 's') goto yy21; + goto yy22; + } else { + if (yych == '|') goto yy18; + goto yy25; + } + } + } + } +yy2: + yyaccept = 0; + yych = *(q = ++p); + goto yy73; +yy3: + { token = INDENT; break; } +yy4: + yyaccept = 1; + yych = *(q = ++p); + if (yych >= 0x01) goto yy68; +yy5: + { token = ERROR; break; } +yy6: + yych = *++p; + if (yych == '\n') goto yy65; + goto yy5; +yy7: + ++p; +yy8: + { token = NEWLINE; break; } +yy9: + ++p; + if ((yych = *p) == 'u') goto yy60; + goto yy27; +yy10: + { token = IDENT; break; } +yy11: + yych = *++p; + if (yych == 'o') goto yy56; + goto yy27; +yy12: + yych = *++p; + if (yych == 'u') goto yy52; + goto yy27; +yy13: + yych = *++p; + if (yych == 'e') goto yy45; + goto yy27; +yy14: + ++p; + { token = EQUALS; break; } +yy16: + ++p; + { token = COLON; break; } +yy18: + ++p; + if ((yych = *p) == '|') goto yy43; + { token = PIPE; break; } +yy20: + yych = *++p; + if (yych == 'n') goto yy36; + goto yy27; +yy21: + yych = *++p; + if (yych == 'u') goto yy28; + goto yy27; +yy22: + yych = *++p; + goto yy27; +yy23: + ++p; + { token = TEOF; break; } +yy25: + yych = *++p; + goto yy5; +yy26: + ++p; + yych = *p; +yy27: + if (yybm[0+yych] & 32) { + goto yy26; + } + goto yy10; +yy28: + yych = *++p; + if (yych != 'b') goto yy27; + yych = *++p; + if (yych != 'n') goto yy27; + yych = *++p; + if (yych != 'i') goto yy27; + yych = *++p; + if (yych != 'n') goto yy27; + yych = *++p; + if (yych != 'j') goto yy27; + yych = *++p; + if (yych != 'a') goto yy27; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy26; + } + { token = SUBNINJA; break; } +yy36: + yych = *++p; + if (yych != 'c') goto yy27; + yych = *++p; + if (yych != 'l') goto yy27; + yych = *++p; + if (yych != 'u') goto yy27; + yych = *++p; + if (yych != 'd') goto yy27; + yych = *++p; + if (yych != 'e') goto yy27; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy26; + } + { token = INCLUDE; break; } +yy43: + ++p; + { token = PIPE2; break; } +yy45: + yych = *++p; + if (yych != 'f') goto yy27; + yych = *++p; + if (yych != 'a') goto yy27; + yych = *++p; + if (yych != 'u') goto yy27; + yych = *++p; + if (yych != 'l') goto yy27; + yych = *++p; + if (yych != 't') goto yy27; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy26; + } + { token = DEFAULT; break; } +yy52: + yych = *++p; + if (yych != 'l') goto yy27; + yych = *++p; + if (yych != 'e') goto yy27; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy26; + } + { token = RULE; break; } +yy56: + yych = *++p; + if (yych != 'o') goto yy27; + yych = *++p; + if (yych != 'l') goto yy27; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy26; + } + { token = POOL; break; } +yy60: + yych = *++p; + if (yych != 'i') goto yy27; + yych = *++p; + if (yych != 'l') goto yy27; + yych = *++p; + if (yych != 'd') goto yy27; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy26; + } + { token = BUILD; break; } +yy65: + ++p; + { token = NEWLINE; break; } +yy67: + ++p; + yych = *p; +yy68: + if (yybm[0+yych] & 64) { + goto yy67; + } + if (yych >= 0x01) goto yy70; +yy69: + p = q; + if (yyaccept <= 0) { + goto yy3; + } else { + goto yy5; + } +yy70: + ++p; + { continue; } +yy72: + yyaccept = 0; + q = ++p; + yych = *p; +yy73: + if (yybm[0+yych] & 128) { + goto yy72; + } + if (yych <= '\f') { + if (yych != '\n') goto yy3; + } else { + if (yych <= '\r') goto yy75; + if (yych == '#') goto yy67; + goto yy3; + } + yych = *++p; + goto yy8; +yy75: + ++p; + if ((yych = *p) == '\n') goto yy65; + goto yy69; +} + + } + + last_token_ = start; + ofs_ = p; + if (token != NEWLINE && token != TEOF) + EatWhitespace(); + return token; +} + +bool Lexer::PeekToken(Token token) { + Token t = ReadToken(); + if (t == token) + return true; + UnreadToken(); + return false; +} + +void Lexer::EatWhitespace() { + const char* p = ofs_; + const char* q; + for (;;) { + ofs_ = p; + +{ + unsigned char yych; + static const unsigned char yybm[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 128, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }; + yych = *p; + if (yych <= ' ') { + if (yych <= 0x00) goto yy82; + if (yych <= 0x1F) goto yy84; + } else { + if (yych == '$') goto yy80; + goto yy84; + } + ++p; + yych = *p; + goto yy92; +yy79: + { continue; } +yy80: + yych = *(q = ++p); + if (yych == '\n') goto yy85; + if (yych == '\r') goto yy87; +yy81: + { break; } +yy82: + ++p; + { break; } +yy84: + yych = *++p; + goto yy81; +yy85: + ++p; + { continue; } +yy87: + yych = *++p; + if (yych == '\n') goto yy89; + p = q; + goto yy81; +yy89: + ++p; + { continue; } +yy91: + ++p; + yych = *p; +yy92: + if (yybm[0+yych] & 128) { + goto yy91; + } + goto yy79; +} + + } +} + +bool Lexer::ReadIdent(string* out) { + const char* p = ofs_; + for (;;) { + const char* start = p; + +{ + unsigned char yych; + static const unsigned char yybm[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 128, 128, 0, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 0, 0, 0, 0, 0, 0, + 0, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 0, 0, 0, 0, 128, + 0, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }; + yych = *p; + if (yych <= '@') { + if (yych <= '.') { + if (yych <= ',') goto yy97; + } else { + if (yych <= '/') goto yy97; + if (yych >= ':') goto yy97; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy95; + if (yych <= '^') goto yy97; + } else { + if (yych <= '`') goto yy97; + if (yych >= '{') goto yy97; + } + } +yy95: + ++p; + yych = *p; + goto yy100; +yy96: + { + out->assign(start, p - start); + break; + } +yy97: + ++p; + { return false; } +yy99: + ++p; + yych = *p; +yy100: + if (yybm[0+yych] & 128) { + goto yy99; + } + goto yy96; +} + + } + ofs_ = p; + EatWhitespace(); + return true; +} + +bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) { + const char* p = ofs_; + const char* q; + const char* start; + for (;;) { + start = p; + +{ + unsigned char yych; + static const unsigned char yybm[] = { + 0, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 0, 128, 128, 0, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 16, 128, 128, 128, 0, 128, 128, 128, + 128, 128, 128, 128, 128, 224, 160, 128, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 0, 128, 128, 128, 128, 128, + 128, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 128, 128, 128, 128, 224, + 128, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 128, 0, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + }; + yych = *p; + if (yych <= ' ') { + if (yych <= '\n') { + if (yych <= 0x00) goto yy110; + if (yych >= '\n') goto yy107; + } else { + if (yych == '\r') goto yy105; + if (yych >= ' ') goto yy107; + } + } else { + if (yych <= '9') { + if (yych == '$') goto yy109; + } else { + if (yych <= ':') goto yy107; + if (yych == '|') goto yy107; + } + } + ++p; + yych = *p; + goto yy140; +yy104: + { + eval->AddText(StringPiece(start, p - start)); + continue; + } +yy105: + ++p; + if ((yych = *p) == '\n') goto yy137; + { + last_token_ = start; + return Error(DescribeLastError(), err); + } +yy107: + ++p; + { + if (path) { + p = start; + break; + } else { + if (*start == '\n') + break; + eval->AddText(StringPiece(start, 1)); + continue; + } + } +yy109: + yych = *++p; + if (yych <= '-') { + if (yych <= 0x1F) { + if (yych <= '\n') { + if (yych <= '\t') goto yy112; + goto yy124; + } else { + if (yych == '\r') goto yy114; + goto yy112; + } + } else { + if (yych <= '#') { + if (yych <= ' ') goto yy115; + goto yy112; + } else { + if (yych <= '$') goto yy117; + if (yych <= ',') goto yy112; + goto yy119; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '9') { + if (yych <= '/') goto yy112; + goto yy119; + } else { + if (yych <= ':') goto yy121; + if (yych <= '@') goto yy112; + goto yy119; + } + } else { + if (yych <= '`') { + if (yych == '_') goto yy119; + goto yy112; + } else { + if (yych <= 'z') goto yy119; + if (yych <= '{') goto yy123; + goto yy112; + } + } + } +yy110: + ++p; + { + last_token_ = start; + return Error("unexpected EOF", err); + } +yy112: + ++p; +yy113: + { + last_token_ = start; + return Error("bad $-escape (literal $ must be written as $$)", err); + } +yy114: + yych = *++p; + if (yych == '\n') goto yy134; + goto yy113; +yy115: + ++p; + { + eval->AddText(StringPiece(" ", 1)); + continue; + } +yy117: + ++p; + { + eval->AddText(StringPiece("$", 1)); + continue; + } +yy119: + ++p; + yych = *p; + goto yy133; +yy120: + { + eval->AddSpecial(StringPiece(start + 1, p - start - 1)); + continue; + } +yy121: + ++p; + { + eval->AddText(StringPiece(":", 1)); + continue; + } +yy123: + yych = *(q = ++p); + if (yybm[0+yych] & 32) { + goto yy127; + } + goto yy113; +yy124: + ++p; + yych = *p; + if (yybm[0+yych] & 16) { + goto yy124; + } + { + continue; + } +yy127: + ++p; + yych = *p; + if (yybm[0+yych] & 32) { + goto yy127; + } + if (yych == '}') goto yy130; + p = q; + goto yy113; +yy130: + ++p; + { + eval->AddSpecial(StringPiece(start + 2, p - start - 3)); + continue; + } +yy132: + ++p; + yych = *p; +yy133: + if (yybm[0+yych] & 64) { + goto yy132; + } + goto yy120; +yy134: + ++p; + yych = *p; + if (yych == ' ') goto yy134; + { + continue; + } +yy137: + ++p; + { + if (path) + p = start; + break; + } +yy139: + ++p; + yych = *p; +yy140: + if (yybm[0+yych] & 128) { + goto yy139; + } + goto yy104; +} + + } + last_token_ = start; + ofs_ = p; + if (path) + EatWhitespace(); + // Non-path strings end in newlines, so there's no whitespace to eat. + return true; +} diff --git a/src/ninja.cc b/src/ninja.cc index 3756123..a3d963f 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -1156,7 +1156,7 @@ int main(int argc, char** argv) { #if defined(_MSC_VER) // Set a handler to catch crashes not caught by the __try..__except // block (e.g. an exception in a stack-unwind-block). - set_terminate(TerminateHandler); + std::set_terminate(TerminateHandler); __try { // Running inside __try ... __except suppresses any Windows error // dialogs for errors such as bad_alloc. -- cgit v0.12 From 956b4f1bc4f276325fd3ad8670be6bc9c9804fb9 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Sun, 9 Aug 2015 21:27:39 -0700 Subject: Remove accidental changes --- src/depfile_parser.cc | 476 +++++++------- src/lexer.cc | 1734 ++++++++++++++++++++++++------------------------- 2 files changed, 1105 insertions(+), 1105 deletions(-) diff --git a/src/depfile_parser.cc b/src/depfile_parser.cc index c5bc291..7268f31 100644 --- a/src/depfile_parser.cc +++ b/src/depfile_parser.cc @@ -1,238 +1,238 @@ -/* Generated by re2c 0.13.5 */ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "depfile_parser.h" - -// A note on backslashes in Makefiles, from reading the docs: -// Backslash-newline is the line continuation character. -// Backslash-# escapes a # (otherwise meaningful as a comment start). -// Backslash-% escapes a % (otherwise meaningful as a special). -// Finally, quoting the GNU manual, "Backslashes that are not in danger -// of quoting ‘%’ characters go unmolested." -// How do you end a line with a backslash? The netbsd Make docs suggest -// reading the result of a shell command echoing a backslash! -// -// Rather than implement all of above, we do a simpler thing here: -// Backslashes escape a set of characters (see "escapes" defined below), -// otherwise they are passed through verbatim. -// If anyone actually has depfiles that rely on the more complicated -// behavior we can adjust this. -bool DepfileParser::Parse(string* content, string* err) { - // in: current parser input point. - // end: end of input. - // parsing_targets: whether we are parsing targets or dependencies. - char* in = &(*content)[0]; - char* end = in + content->size(); - bool parsing_targets = true; - while (in < end) { - // out: current output point (typically same as in, but can fall behind - // as we de-escape backslashes). - char* out = in; - // filename: start of the current parsed filename. - char* filename = out; - for (;;) { - // start: beginning of the current parsed span. - const char* start = in; - - { - char yych; - static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 128, 0, 0, 0, 0, 0, 0, - 128, 128, 0, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 0, 0, 128, 0, 0, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 0, 0, 0, 0, 128, - 0, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 0, 128, 128, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - }; - - yych = *in; - if (yych <= '=') { - if (yych <= '$') { - if (yych <= ' ') { - if (yych <= 0x00) goto yy7; - goto yy9; - } else { - if (yych <= '!') goto yy5; - if (yych <= '#') goto yy9; - goto yy4; - } - } else { - if (yych <= '*') { - if (yych <= '\'') goto yy9; - if (yych <= ')') goto yy5; - goto yy9; - } else { - if (yych <= ':') goto yy5; - if (yych <= '<') goto yy9; - goto yy5; - } - } - } else { - if (yych <= '^') { - if (yych <= 'Z') { - if (yych <= '?') goto yy9; - goto yy5; - } else { - if (yych != '\\') goto yy9; - } - } else { - if (yych <= '{') { - if (yych == '`') goto yy9; - goto yy5; - } else { - if (yych <= '|') goto yy9; - if (yych <= '~') goto yy5; - goto yy9; - } - } - } - ++in; - if ((yych = *in) <= '"') { - if (yych <= '\f') { - if (yych <= 0x00) goto yy3; - if (yych != '\n') goto yy14; - } else { - if (yych <= '\r') goto yy3; - if (yych == ' ') goto yy16; - goto yy14; - } - } else { - if (yych <= 'Z') { - if (yych <= '#') goto yy16; - if (yych == '*') goto yy16; - goto yy14; - } else { - if (yych <= '\\') goto yy16; - if (yych == '|') goto yy16; - goto yy14; - } - } -yy3: - { - // For any other character (e.g. whitespace), swallow it here, - // allowing the outer logic to loop around again. - break; - } -yy4: - yych = *++in; - if (yych == '$') goto yy12; - goto yy3; -yy5: - ++in; - yych = *in; - goto yy11; -yy6: - { - // Got a span of plain text. - int len = (int)(in - start); - // Need to shift it over if we're overwriting backslashes. - if (out < start) - memmove(out, start, len); - out += len; - continue; - } -yy7: - ++in; - { - break; - } -yy9: - yych = *++in; - goto yy3; -yy10: - ++in; - yych = *in; -yy11: - if (yybm[0+yych] & 128) { - goto yy10; - } - goto yy6; -yy12: - ++in; - { - // De-escape dollar character. - *out++ = '$'; - continue; - } -yy14: - ++in; - { - // Let backslash before other characters through verbatim. - *out++ = '\\'; - *out++ = yych; - continue; - } -yy16: - ++in; - { - // De-escape backslashed character. - *out++ = yych; - continue; - } - } - - } - - int len = (int)(out - filename); - const bool is_target = parsing_targets; - if (len > 0 && filename[len - 1] == ':') { - len--; // Strip off trailing colon, if any. - parsing_targets = false; - } - - if (len == 0) - continue; - - if (!is_target) { - ins_.push_back(StringPiece(filename, len)); - } else if (!out_.str_) { - out_ = StringPiece(filename, len); - } else if (out_ != StringPiece(filename, len)) { - *err = "depfile has multiple output paths"; - return false; - } - } - if (parsing_targets) { - *err = "expected ':' in depfile"; - return false; - } - return true; -} +/* Generated by re2c 0.13.5 */ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "depfile_parser.h" + +// A note on backslashes in Makefiles, from reading the docs: +// Backslash-newline is the line continuation character. +// Backslash-# escapes a # (otherwise meaningful as a comment start). +// Backslash-% escapes a % (otherwise meaningful as a special). +// Finally, quoting the GNU manual, "Backslashes that are not in danger +// of quoting ‘%’ characters go unmolested." +// How do you end a line with a backslash? The netbsd Make docs suggest +// reading the result of a shell command echoing a backslash! +// +// Rather than implement all of above, we do a simpler thing here: +// Backslashes escape a set of characters (see "escapes" defined below), +// otherwise they are passed through verbatim. +// If anyone actually has depfiles that rely on the more complicated +// behavior we can adjust this. +bool DepfileParser::Parse(string* content, string* err) { + // in: current parser input point. + // end: end of input. + // parsing_targets: whether we are parsing targets or dependencies. + char* in = &(*content)[0]; + char* end = in + content->size(); + bool parsing_targets = true; + while (in < end) { + // out: current output point (typically same as in, but can fall behind + // as we de-escape backslashes). + char* out = in; + // filename: start of the current parsed filename. + char* filename = out; + for (;;) { + // start: beginning of the current parsed span. + const char* start = in; + + { + char yych; + static const unsigned char yybm[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 128, 0, 0, 0, 0, 0, 0, + 128, 128, 0, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 0, 0, 128, 0, 0, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 0, 0, 0, 0, 128, + 0, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 0, 128, 128, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }; + + yych = *in; + if (yych <= '=') { + if (yych <= '$') { + if (yych <= ' ') { + if (yych <= 0x00) goto yy7; + goto yy9; + } else { + if (yych <= '!') goto yy5; + if (yych <= '#') goto yy9; + goto yy4; + } + } else { + if (yych <= '*') { + if (yych <= '\'') goto yy9; + if (yych <= ')') goto yy5; + goto yy9; + } else { + if (yych <= ':') goto yy5; + if (yych <= '<') goto yy9; + goto yy5; + } + } + } else { + if (yych <= '^') { + if (yych <= 'Z') { + if (yych <= '?') goto yy9; + goto yy5; + } else { + if (yych != '\\') goto yy9; + } + } else { + if (yych <= '{') { + if (yych == '`') goto yy9; + goto yy5; + } else { + if (yych <= '|') goto yy9; + if (yych <= '~') goto yy5; + goto yy9; + } + } + } + ++in; + if ((yych = *in) <= '"') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy3; + if (yych != '\n') goto yy14; + } else { + if (yych <= '\r') goto yy3; + if (yych == ' ') goto yy16; + goto yy14; + } + } else { + if (yych <= 'Z') { + if (yych <= '#') goto yy16; + if (yych == '*') goto yy16; + goto yy14; + } else { + if (yych <= '\\') goto yy16; + if (yych == '|') goto yy16; + goto yy14; + } + } +yy3: + { + // For any other character (e.g. whitespace), swallow it here, + // allowing the outer logic to loop around again. + break; + } +yy4: + yych = *++in; + if (yych == '$') goto yy12; + goto yy3; +yy5: + ++in; + yych = *in; + goto yy11; +yy6: + { + // Got a span of plain text. + int len = (int)(in - start); + // Need to shift it over if we're overwriting backslashes. + if (out < start) + memmove(out, start, len); + out += len; + continue; + } +yy7: + ++in; + { + break; + } +yy9: + yych = *++in; + goto yy3; +yy10: + ++in; + yych = *in; +yy11: + if (yybm[0+yych] & 128) { + goto yy10; + } + goto yy6; +yy12: + ++in; + { + // De-escape dollar character. + *out++ = '$'; + continue; + } +yy14: + ++in; + { + // Let backslash before other characters through verbatim. + *out++ = '\\'; + *out++ = yych; + continue; + } +yy16: + ++in; + { + // De-escape backslashed character. + *out++ = yych; + continue; + } + } + + } + + int len = (int)(out - filename); + const bool is_target = parsing_targets; + if (len > 0 && filename[len - 1] == ':') { + len--; // Strip off trailing colon, if any. + parsing_targets = false; + } + + if (len == 0) + continue; + + if (!is_target) { + ins_.push_back(StringPiece(filename, len)); + } else if (!out_.str_) { + out_ = StringPiece(filename, len); + } else if (out_ != StringPiece(filename, len)) { + *err = "depfile has multiple output paths"; + return false; + } + } + if (parsing_targets) { + *err = "expected ':' in depfile"; + return false; + } + return true; +} diff --git a/src/lexer.cc b/src/lexer.cc index 81264bd..37b8678 100644 --- a/src/lexer.cc +++ b/src/lexer.cc @@ -1,867 +1,867 @@ -/* Generated by re2c 0.13.5 */ -// Copyright 2011 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "lexer.h" - -#include - -#include "eval_env.h" -#include "util.h" - -bool Lexer::Error(const string& message, string* err) { - // Compute line/column. - int line = 1; - const char* context = input_.str_; - for (const char* p = input_.str_; p < last_token_; ++p) { - if (*p == '\n') { - ++line; - context = p + 1; - } - } - int col = last_token_ ? (int)(last_token_ - context) : 0; - - char buf[1024]; - snprintf(buf, sizeof(buf), "%s:%d: ", filename_.AsString().c_str(), line); - *err = buf; - *err += message + "\n"; - - // Add some context to the message. - const int kTruncateColumn = 72; - if (col > 0 && col < kTruncateColumn) { - int len; - bool truncated = true; - for (len = 0; len < kTruncateColumn; ++len) { - if (context[len] == 0 || context[len] == '\n') { - truncated = false; - break; - } - } - *err += string(context, len); - if (truncated) - *err += "..."; - *err += "\n"; - *err += string(col, ' '); - *err += "^ near here"; - } - - return false; -} - -Lexer::Lexer(const char* input) { - Start("input", input); -} - -void Lexer::Start(StringPiece filename, StringPiece input) { - filename_ = filename; - input_ = input; - ofs_ = input_.str_; - last_token_ = NULL; -} - -const char* Lexer::TokenName(Token t) { - switch (t) { - case ERROR: return "lexing error"; - case BUILD: return "'build'"; - case COLON: return "':'"; - case DEFAULT: return "'default'"; - case EQUALS: return "'='"; - case IDENT: return "identifier"; - case INCLUDE: return "'include'"; - case INDENT: return "indent"; - 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"; - } - return NULL; // not reached -} - -const char* Lexer::TokenErrorHint(Token expected) { - switch (expected) { - case COLON: - return " ($ also escapes ':')"; - default: - return ""; - } -} - -string Lexer::DescribeLastError() { - if (last_token_) { - switch (last_token_[0]) { - case '\t': - return "tabs are not allowed, use spaces"; - } - } - return "lexing error"; -} - -void Lexer::UnreadToken() { - ofs_ = last_token_; -} - -Lexer::Token Lexer::ReadToken() { - const char* p = ofs_; - const char* q; - const char* start; - Lexer::Token token; - for (;;) { - start = p; - -{ - unsigned char yych; - unsigned int yyaccept = 0; - static const unsigned char yybm[] = { - 0, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 0, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 192, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 96, 96, 64, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 64, 64, 64, 64, 64, 64, - 64, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 64, 64, 64, 64, 96, - 64, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - }; - - yych = *p; - if (yych <= 'Z') { - if (yych <= '#') { - if (yych <= '\f') { - if (yych <= 0x00) goto yy23; - if (yych == '\n') goto yy7; - goto yy25; - } else { - if (yych <= 0x1F) { - if (yych <= '\r') goto yy6; - goto yy25; - } else { - if (yych <= ' ') goto yy2; - if (yych <= '"') goto yy25; - goto yy4; - } - } - } else { - if (yych <= '9') { - if (yych <= ',') goto yy25; - if (yych == '/') goto yy25; - goto yy22; - } else { - if (yych <= '<') { - if (yych <= ':') goto yy16; - goto yy25; - } else { - if (yych <= '=') goto yy14; - if (yych <= '@') goto yy25; - goto yy22; - } - } - } - } else { - if (yych <= 'i') { - if (yych <= 'a') { - if (yych == '_') goto yy22; - if (yych <= '`') goto yy25; - goto yy22; - } else { - if (yych <= 'c') { - if (yych <= 'b') goto yy9; - goto yy22; - } else { - if (yych <= 'd') goto yy13; - if (yych <= 'h') goto yy22; - goto yy20; - } - } - } else { - if (yych <= 'r') { - if (yych == 'p') goto yy11; - if (yych <= 'q') goto yy22; - goto yy12; - } else { - if (yych <= 'z') { - if (yych <= 's') goto yy21; - goto yy22; - } else { - if (yych == '|') goto yy18; - goto yy25; - } - } - } - } -yy2: - yyaccept = 0; - yych = *(q = ++p); - goto yy73; -yy3: - { token = INDENT; break; } -yy4: - yyaccept = 1; - yych = *(q = ++p); - if (yych >= 0x01) goto yy68; -yy5: - { token = ERROR; break; } -yy6: - yych = *++p; - if (yych == '\n') goto yy65; - goto yy5; -yy7: - ++p; -yy8: - { token = NEWLINE; break; } -yy9: - ++p; - if ((yych = *p) == 'u') goto yy60; - goto yy27; -yy10: - { token = IDENT; break; } -yy11: - yych = *++p; - if (yych == 'o') goto yy56; - goto yy27; -yy12: - yych = *++p; - if (yych == 'u') goto yy52; - goto yy27; -yy13: - yych = *++p; - if (yych == 'e') goto yy45; - goto yy27; -yy14: - ++p; - { token = EQUALS; break; } -yy16: - ++p; - { token = COLON; break; } -yy18: - ++p; - if ((yych = *p) == '|') goto yy43; - { token = PIPE; break; } -yy20: - yych = *++p; - if (yych == 'n') goto yy36; - goto yy27; -yy21: - yych = *++p; - if (yych == 'u') goto yy28; - goto yy27; -yy22: - yych = *++p; - goto yy27; -yy23: - ++p; - { token = TEOF; break; } -yy25: - yych = *++p; - goto yy5; -yy26: - ++p; - yych = *p; -yy27: - if (yybm[0+yych] & 32) { - goto yy26; - } - goto yy10; -yy28: - yych = *++p; - if (yych != 'b') goto yy27; - yych = *++p; - if (yych != 'n') goto yy27; - yych = *++p; - if (yych != 'i') goto yy27; - yych = *++p; - if (yych != 'n') goto yy27; - yych = *++p; - if (yych != 'j') goto yy27; - yych = *++p; - if (yych != 'a') goto yy27; - ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; - } - { token = SUBNINJA; break; } -yy36: - yych = *++p; - if (yych != 'c') goto yy27; - yych = *++p; - if (yych != 'l') goto yy27; - yych = *++p; - if (yych != 'u') goto yy27; - yych = *++p; - if (yych != 'd') goto yy27; - yych = *++p; - if (yych != 'e') goto yy27; - ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; - } - { token = INCLUDE; break; } -yy43: - ++p; - { token = PIPE2; break; } -yy45: - yych = *++p; - if (yych != 'f') goto yy27; - yych = *++p; - if (yych != 'a') goto yy27; - yych = *++p; - if (yych != 'u') goto yy27; - yych = *++p; - if (yych != 'l') goto yy27; - yych = *++p; - if (yych != 't') goto yy27; - ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; - } - { token = DEFAULT; break; } -yy52: - yych = *++p; - if (yych != 'l') goto yy27; - yych = *++p; - if (yych != 'e') goto yy27; - ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; - } - { token = RULE; break; } -yy56: - yych = *++p; - if (yych != 'o') goto yy27; - yych = *++p; - if (yych != 'l') goto yy27; - ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; - } - { token = POOL; break; } -yy60: - yych = *++p; - if (yych != 'i') goto yy27; - yych = *++p; - if (yych != 'l') goto yy27; - yych = *++p; - if (yych != 'd') goto yy27; - ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; - } - { token = BUILD; break; } -yy65: - ++p; - { token = NEWLINE; break; } -yy67: - ++p; - yych = *p; -yy68: - if (yybm[0+yych] & 64) { - goto yy67; - } - if (yych >= 0x01) goto yy70; -yy69: - p = q; - if (yyaccept <= 0) { - goto yy3; - } else { - goto yy5; - } -yy70: - ++p; - { continue; } -yy72: - yyaccept = 0; - q = ++p; - yych = *p; -yy73: - if (yybm[0+yych] & 128) { - goto yy72; - } - if (yych <= '\f') { - if (yych != '\n') goto yy3; - } else { - if (yych <= '\r') goto yy75; - if (yych == '#') goto yy67; - goto yy3; - } - yych = *++p; - goto yy8; -yy75: - ++p; - if ((yych = *p) == '\n') goto yy65; - goto yy69; -} - - } - - last_token_ = start; - ofs_ = p; - if (token != NEWLINE && token != TEOF) - EatWhitespace(); - return token; -} - -bool Lexer::PeekToken(Token token) { - Token t = ReadToken(); - if (t == token) - return true; - UnreadToken(); - return false; -} - -void Lexer::EatWhitespace() { - const char* p = ofs_; - const char* q; - for (;;) { - ofs_ = p; - -{ - unsigned char yych; - static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 128, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - }; - yych = *p; - if (yych <= ' ') { - if (yych <= 0x00) goto yy82; - if (yych <= 0x1F) goto yy84; - } else { - if (yych == '$') goto yy80; - goto yy84; - } - ++p; - yych = *p; - goto yy92; -yy79: - { continue; } -yy80: - yych = *(q = ++p); - if (yych == '\n') goto yy85; - if (yych == '\r') goto yy87; -yy81: - { break; } -yy82: - ++p; - { break; } -yy84: - yych = *++p; - goto yy81; -yy85: - ++p; - { continue; } -yy87: - yych = *++p; - if (yych == '\n') goto yy89; - p = q; - goto yy81; -yy89: - ++p; - { continue; } -yy91: - ++p; - yych = *p; -yy92: - if (yybm[0+yych] & 128) { - goto yy91; - } - goto yy79; -} - - } -} - -bool Lexer::ReadIdent(string* out) { - const char* p = ofs_; - for (;;) { - const char* start = p; - -{ - unsigned char yych; - static const unsigned char yybm[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 128, 128, 0, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 0, 0, 0, 0, 0, 0, - 0, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 0, 0, 0, 0, 128, - 0, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - }; - yych = *p; - if (yych <= '@') { - if (yych <= '.') { - if (yych <= ',') goto yy97; - } else { - if (yych <= '/') goto yy97; - if (yych >= ':') goto yy97; - } - } else { - if (yych <= '_') { - if (yych <= 'Z') goto yy95; - if (yych <= '^') goto yy97; - } else { - if (yych <= '`') goto yy97; - if (yych >= '{') goto yy97; - } - } -yy95: - ++p; - yych = *p; - goto yy100; -yy96: - { - out->assign(start, p - start); - break; - } -yy97: - ++p; - { return false; } -yy99: - ++p; - yych = *p; -yy100: - if (yybm[0+yych] & 128) { - goto yy99; - } - goto yy96; -} - - } - ofs_ = p; - EatWhitespace(); - return true; -} - -bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) { - const char* p = ofs_; - const char* q; - const char* start; - for (;;) { - start = p; - -{ - unsigned char yych; - static const unsigned char yybm[] = { - 0, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 0, 128, 128, 0, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 16, 128, 128, 128, 0, 128, 128, 128, - 128, 128, 128, 128, 128, 224, 160, 128, - 224, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 0, 128, 128, 128, 128, 128, - 128, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 224, 128, 128, 128, 128, 224, - 128, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 224, 128, 0, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - }; - yych = *p; - if (yych <= ' ') { - if (yych <= '\n') { - if (yych <= 0x00) goto yy110; - if (yych >= '\n') goto yy107; - } else { - if (yych == '\r') goto yy105; - if (yych >= ' ') goto yy107; - } - } else { - if (yych <= '9') { - if (yych == '$') goto yy109; - } else { - if (yych <= ':') goto yy107; - if (yych == '|') goto yy107; - } - } - ++p; - yych = *p; - goto yy140; -yy104: - { - eval->AddText(StringPiece(start, p - start)); - continue; - } -yy105: - ++p; - if ((yych = *p) == '\n') goto yy137; - { - last_token_ = start; - return Error(DescribeLastError(), err); - } -yy107: - ++p; - { - if (path) { - p = start; - break; - } else { - if (*start == '\n') - break; - eval->AddText(StringPiece(start, 1)); - continue; - } - } -yy109: - yych = *++p; - if (yych <= '-') { - if (yych <= 0x1F) { - if (yych <= '\n') { - if (yych <= '\t') goto yy112; - goto yy124; - } else { - if (yych == '\r') goto yy114; - goto yy112; - } - } else { - if (yych <= '#') { - if (yych <= ' ') goto yy115; - goto yy112; - } else { - if (yych <= '$') goto yy117; - if (yych <= ',') goto yy112; - goto yy119; - } - } - } else { - if (yych <= 'Z') { - if (yych <= '9') { - if (yych <= '/') goto yy112; - goto yy119; - } else { - if (yych <= ':') goto yy121; - if (yych <= '@') goto yy112; - goto yy119; - } - } else { - if (yych <= '`') { - if (yych == '_') goto yy119; - goto yy112; - } else { - if (yych <= 'z') goto yy119; - if (yych <= '{') goto yy123; - goto yy112; - } - } - } -yy110: - ++p; - { - last_token_ = start; - return Error("unexpected EOF", err); - } -yy112: - ++p; -yy113: - { - last_token_ = start; - return Error("bad $-escape (literal $ must be written as $$)", err); - } -yy114: - yych = *++p; - if (yych == '\n') goto yy134; - goto yy113; -yy115: - ++p; - { - eval->AddText(StringPiece(" ", 1)); - continue; - } -yy117: - ++p; - { - eval->AddText(StringPiece("$", 1)); - continue; - } -yy119: - ++p; - yych = *p; - goto yy133; -yy120: - { - eval->AddSpecial(StringPiece(start + 1, p - start - 1)); - continue; - } -yy121: - ++p; - { - eval->AddText(StringPiece(":", 1)); - continue; - } -yy123: - yych = *(q = ++p); - if (yybm[0+yych] & 32) { - goto yy127; - } - goto yy113; -yy124: - ++p; - yych = *p; - if (yybm[0+yych] & 16) { - goto yy124; - } - { - continue; - } -yy127: - ++p; - yych = *p; - if (yybm[0+yych] & 32) { - goto yy127; - } - if (yych == '}') goto yy130; - p = q; - goto yy113; -yy130: - ++p; - { - eval->AddSpecial(StringPiece(start + 2, p - start - 3)); - continue; - } -yy132: - ++p; - yych = *p; -yy133: - if (yybm[0+yych] & 64) { - goto yy132; - } - goto yy120; -yy134: - ++p; - yych = *p; - if (yych == ' ') goto yy134; - { - continue; - } -yy137: - ++p; - { - if (path) - p = start; - break; - } -yy139: - ++p; - yych = *p; -yy140: - if (yybm[0+yych] & 128) { - goto yy139; - } - goto yy104; -} - - } - last_token_ = start; - ofs_ = p; - if (path) - EatWhitespace(); - // Non-path strings end in newlines, so there's no whitespace to eat. - return true; -} +/* Generated by re2c 0.13.5 */ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lexer.h" + +#include + +#include "eval_env.h" +#include "util.h" + +bool Lexer::Error(const string& message, string* err) { + // Compute line/column. + int line = 1; + const char* context = input_.str_; + for (const char* p = input_.str_; p < last_token_; ++p) { + if (*p == '\n') { + ++line; + context = p + 1; + } + } + int col = last_token_ ? (int)(last_token_ - context) : 0; + + char buf[1024]; + snprintf(buf, sizeof(buf), "%s:%d: ", filename_.AsString().c_str(), line); + *err = buf; + *err += message + "\n"; + + // Add some context to the message. + const int kTruncateColumn = 72; + if (col > 0 && col < kTruncateColumn) { + int len; + bool truncated = true; + for (len = 0; len < kTruncateColumn; ++len) { + if (context[len] == 0 || context[len] == '\n') { + truncated = false; + break; + } + } + *err += string(context, len); + if (truncated) + *err += "..."; + *err += "\n"; + *err += string(col, ' '); + *err += "^ near here"; + } + + return false; +} + +Lexer::Lexer(const char* input) { + Start("input", input); +} + +void Lexer::Start(StringPiece filename, StringPiece input) { + filename_ = filename; + input_ = input; + ofs_ = input_.str_; + last_token_ = NULL; +} + +const char* Lexer::TokenName(Token t) { + switch (t) { + case ERROR: return "lexing error"; + case BUILD: return "'build'"; + case COLON: return "':'"; + case DEFAULT: return "'default'"; + case EQUALS: return "'='"; + case IDENT: return "identifier"; + case INCLUDE: return "'include'"; + case INDENT: return "indent"; + 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"; + } + return NULL; // not reached +} + +const char* Lexer::TokenErrorHint(Token expected) { + switch (expected) { + case COLON: + return " ($ also escapes ':')"; + default: + return ""; + } +} + +string Lexer::DescribeLastError() { + if (last_token_) { + switch (last_token_[0]) { + case '\t': + return "tabs are not allowed, use spaces"; + } + } + return "lexing error"; +} + +void Lexer::UnreadToken() { + ofs_ = last_token_; +} + +Lexer::Token Lexer::ReadToken() { + const char* p = ofs_; + const char* q; + const char* start; + Lexer::Token token; + for (;;) { + start = p; + +{ + unsigned char yych; + unsigned int yyaccept = 0; + static const unsigned char yybm[] = { + 0, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 0, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 192, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 96, 96, 64, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 64, 64, 64, 64, 64, 64, + 64, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 64, 64, 64, 64, 96, + 64, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + }; + + yych = *p; + if (yych <= 'Z') { + if (yych <= '#') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy23; + if (yych == '\n') goto yy7; + goto yy25; + } else { + if (yych <= 0x1F) { + if (yych <= '\r') goto yy6; + goto yy25; + } else { + if (yych <= ' ') goto yy2; + if (yych <= '"') goto yy25; + goto yy4; + } + } + } else { + if (yych <= '9') { + if (yych <= ',') goto yy25; + if (yych == '/') goto yy25; + goto yy22; + } else { + if (yych <= '<') { + if (yych <= ':') goto yy16; + goto yy25; + } else { + if (yych <= '=') goto yy14; + if (yych <= '@') goto yy25; + goto yy22; + } + } + } + } else { + if (yych <= 'i') { + if (yych <= 'a') { + if (yych == '_') goto yy22; + if (yych <= '`') goto yy25; + goto yy22; + } else { + if (yych <= 'c') { + if (yych <= 'b') goto yy9; + goto yy22; + } else { + if (yych <= 'd') goto yy13; + if (yych <= 'h') goto yy22; + goto yy20; + } + } + } else { + if (yych <= 'r') { + if (yych == 'p') goto yy11; + if (yych <= 'q') goto yy22; + goto yy12; + } else { + if (yych <= 'z') { + if (yych <= 's') goto yy21; + goto yy22; + } else { + if (yych == '|') goto yy18; + goto yy25; + } + } + } + } +yy2: + yyaccept = 0; + yych = *(q = ++p); + goto yy73; +yy3: + { token = INDENT; break; } +yy4: + yyaccept = 1; + yych = *(q = ++p); + if (yych >= 0x01) goto yy68; +yy5: + { token = ERROR; break; } +yy6: + yych = *++p; + if (yych == '\n') goto yy65; + goto yy5; +yy7: + ++p; +yy8: + { token = NEWLINE; break; } +yy9: + ++p; + if ((yych = *p) == 'u') goto yy60; + goto yy27; +yy10: + { token = IDENT; break; } +yy11: + yych = *++p; + if (yych == 'o') goto yy56; + goto yy27; +yy12: + yych = *++p; + if (yych == 'u') goto yy52; + goto yy27; +yy13: + yych = *++p; + if (yych == 'e') goto yy45; + goto yy27; +yy14: + ++p; + { token = EQUALS; break; } +yy16: + ++p; + { token = COLON; break; } +yy18: + ++p; + if ((yych = *p) == '|') goto yy43; + { token = PIPE; break; } +yy20: + yych = *++p; + if (yych == 'n') goto yy36; + goto yy27; +yy21: + yych = *++p; + if (yych == 'u') goto yy28; + goto yy27; +yy22: + yych = *++p; + goto yy27; +yy23: + ++p; + { token = TEOF; break; } +yy25: + yych = *++p; + goto yy5; +yy26: + ++p; + yych = *p; +yy27: + if (yybm[0+yych] & 32) { + goto yy26; + } + goto yy10; +yy28: + yych = *++p; + if (yych != 'b') goto yy27; + yych = *++p; + if (yych != 'n') goto yy27; + yych = *++p; + if (yych != 'i') goto yy27; + yych = *++p; + if (yych != 'n') goto yy27; + yych = *++p; + if (yych != 'j') goto yy27; + yych = *++p; + if (yych != 'a') goto yy27; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy26; + } + { token = SUBNINJA; break; } +yy36: + yych = *++p; + if (yych != 'c') goto yy27; + yych = *++p; + if (yych != 'l') goto yy27; + yych = *++p; + if (yych != 'u') goto yy27; + yych = *++p; + if (yych != 'd') goto yy27; + yych = *++p; + if (yych != 'e') goto yy27; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy26; + } + { token = INCLUDE; break; } +yy43: + ++p; + { token = PIPE2; break; } +yy45: + yych = *++p; + if (yych != 'f') goto yy27; + yych = *++p; + if (yych != 'a') goto yy27; + yych = *++p; + if (yych != 'u') goto yy27; + yych = *++p; + if (yych != 'l') goto yy27; + yych = *++p; + if (yych != 't') goto yy27; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy26; + } + { token = DEFAULT; break; } +yy52: + yych = *++p; + if (yych != 'l') goto yy27; + yych = *++p; + if (yych != 'e') goto yy27; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy26; + } + { token = RULE; break; } +yy56: + yych = *++p; + if (yych != 'o') goto yy27; + yych = *++p; + if (yych != 'l') goto yy27; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy26; + } + { token = POOL; break; } +yy60: + yych = *++p; + if (yych != 'i') goto yy27; + yych = *++p; + if (yych != 'l') goto yy27; + yych = *++p; + if (yych != 'd') goto yy27; + ++p; + if (yybm[0+(yych = *p)] & 32) { + goto yy26; + } + { token = BUILD; break; } +yy65: + ++p; + { token = NEWLINE; break; } +yy67: + ++p; + yych = *p; +yy68: + if (yybm[0+yych] & 64) { + goto yy67; + } + if (yych >= 0x01) goto yy70; +yy69: + p = q; + if (yyaccept <= 0) { + goto yy3; + } else { + goto yy5; + } +yy70: + ++p; + { continue; } +yy72: + yyaccept = 0; + q = ++p; + yych = *p; +yy73: + if (yybm[0+yych] & 128) { + goto yy72; + } + if (yych <= '\f') { + if (yych != '\n') goto yy3; + } else { + if (yych <= '\r') goto yy75; + if (yych == '#') goto yy67; + goto yy3; + } + yych = *++p; + goto yy8; +yy75: + ++p; + if ((yych = *p) == '\n') goto yy65; + goto yy69; +} + + } + + last_token_ = start; + ofs_ = p; + if (token != NEWLINE && token != TEOF) + EatWhitespace(); + return token; +} + +bool Lexer::PeekToken(Token token) { + Token t = ReadToken(); + if (t == token) + return true; + UnreadToken(); + return false; +} + +void Lexer::EatWhitespace() { + const char* p = ofs_; + const char* q; + for (;;) { + ofs_ = p; + +{ + unsigned char yych; + static const unsigned char yybm[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 128, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }; + yych = *p; + if (yych <= ' ') { + if (yych <= 0x00) goto yy82; + if (yych <= 0x1F) goto yy84; + } else { + if (yych == '$') goto yy80; + goto yy84; + } + ++p; + yych = *p; + goto yy92; +yy79: + { continue; } +yy80: + yych = *(q = ++p); + if (yych == '\n') goto yy85; + if (yych == '\r') goto yy87; +yy81: + { break; } +yy82: + ++p; + { break; } +yy84: + yych = *++p; + goto yy81; +yy85: + ++p; + { continue; } +yy87: + yych = *++p; + if (yych == '\n') goto yy89; + p = q; + goto yy81; +yy89: + ++p; + { continue; } +yy91: + ++p; + yych = *p; +yy92: + if (yybm[0+yych] & 128) { + goto yy91; + } + goto yy79; +} + + } +} + +bool Lexer::ReadIdent(string* out) { + const char* p = ofs_; + for (;;) { + const char* start = p; + +{ + unsigned char yych; + static const unsigned char yybm[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 128, 128, 0, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 0, 0, 0, 0, 0, 0, + 0, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 0, 0, 0, 0, 128, + 0, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }; + yych = *p; + if (yych <= '@') { + if (yych <= '.') { + if (yych <= ',') goto yy97; + } else { + if (yych <= '/') goto yy97; + if (yych >= ':') goto yy97; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy95; + if (yych <= '^') goto yy97; + } else { + if (yych <= '`') goto yy97; + if (yych >= '{') goto yy97; + } + } +yy95: + ++p; + yych = *p; + goto yy100; +yy96: + { + out->assign(start, p - start); + break; + } +yy97: + ++p; + { return false; } +yy99: + ++p; + yych = *p; +yy100: + if (yybm[0+yych] & 128) { + goto yy99; + } + goto yy96; +} + + } + ofs_ = p; + EatWhitespace(); + return true; +} + +bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) { + const char* p = ofs_; + const char* q; + const char* start; + for (;;) { + start = p; + +{ + unsigned char yych; + static const unsigned char yybm[] = { + 0, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 0, 128, 128, 0, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 16, 128, 128, 128, 0, 128, 128, 128, + 128, 128, 128, 128, 128, 224, 160, 128, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 0, 128, 128, 128, 128, 128, + 128, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 128, 128, 128, 128, 224, + 128, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 128, 0, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + }; + yych = *p; + if (yych <= ' ') { + if (yych <= '\n') { + if (yych <= 0x00) goto yy110; + if (yych >= '\n') goto yy107; + } else { + if (yych == '\r') goto yy105; + if (yych >= ' ') goto yy107; + } + } else { + if (yych <= '9') { + if (yych == '$') goto yy109; + } else { + if (yych <= ':') goto yy107; + if (yych == '|') goto yy107; + } + } + ++p; + yych = *p; + goto yy140; +yy104: + { + eval->AddText(StringPiece(start, p - start)); + continue; + } +yy105: + ++p; + if ((yych = *p) == '\n') goto yy137; + { + last_token_ = start; + return Error(DescribeLastError(), err); + } +yy107: + ++p; + { + if (path) { + p = start; + break; + } else { + if (*start == '\n') + break; + eval->AddText(StringPiece(start, 1)); + continue; + } + } +yy109: + yych = *++p; + if (yych <= '-') { + if (yych <= 0x1F) { + if (yych <= '\n') { + if (yych <= '\t') goto yy112; + goto yy124; + } else { + if (yych == '\r') goto yy114; + goto yy112; + } + } else { + if (yych <= '#') { + if (yych <= ' ') goto yy115; + goto yy112; + } else { + if (yych <= '$') goto yy117; + if (yych <= ',') goto yy112; + goto yy119; + } + } + } else { + if (yych <= 'Z') { + if (yych <= '9') { + if (yych <= '/') goto yy112; + goto yy119; + } else { + if (yych <= ':') goto yy121; + if (yych <= '@') goto yy112; + goto yy119; + } + } else { + if (yych <= '`') { + if (yych == '_') goto yy119; + goto yy112; + } else { + if (yych <= 'z') goto yy119; + if (yych <= '{') goto yy123; + goto yy112; + } + } + } +yy110: + ++p; + { + last_token_ = start; + return Error("unexpected EOF", err); + } +yy112: + ++p; +yy113: + { + last_token_ = start; + return Error("bad $-escape (literal $ must be written as $$)", err); + } +yy114: + yych = *++p; + if (yych == '\n') goto yy134; + goto yy113; +yy115: + ++p; + { + eval->AddText(StringPiece(" ", 1)); + continue; + } +yy117: + ++p; + { + eval->AddText(StringPiece("$", 1)); + continue; + } +yy119: + ++p; + yych = *p; + goto yy133; +yy120: + { + eval->AddSpecial(StringPiece(start + 1, p - start - 1)); + continue; + } +yy121: + ++p; + { + eval->AddText(StringPiece(":", 1)); + continue; + } +yy123: + yych = *(q = ++p); + if (yybm[0+yych] & 32) { + goto yy127; + } + goto yy113; +yy124: + ++p; + yych = *p; + if (yybm[0+yych] & 16) { + goto yy124; + } + { + continue; + } +yy127: + ++p; + yych = *p; + if (yybm[0+yych] & 32) { + goto yy127; + } + if (yych == '}') goto yy130; + p = q; + goto yy113; +yy130: + ++p; + { + eval->AddSpecial(StringPiece(start + 2, p - start - 3)); + continue; + } +yy132: + ++p; + yych = *p; +yy133: + if (yybm[0+yych] & 64) { + goto yy132; + } + goto yy120; +yy134: + ++p; + yych = *p; + if (yych == ' ') goto yy134; + { + continue; + } +yy137: + ++p; + { + if (path) + p = start; + break; + } +yy139: + ++p; + yych = *p; +yy140: + if (yybm[0+yych] & 128) { + goto yy139; + } + goto yy104; +} + + } + last_token_ = start; + ofs_ = p; + if (path) + EatWhitespace(); + // Non-path strings end in newlines, so there's no whitespace to eat. + return true; +} -- cgit v0.12 From 40a4808074c2daa0f2f83faf22aa7e8a77f8fb22 Mon Sep 17 00:00:00 2001 From: Mike Seplowitz Date: Mon, 12 Jan 2015 17:36:37 -0500 Subject: Strip tabs from od's output /bin/od on Solaris and AIX both generate tabs. --- src/inline.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inline.sh b/src/inline.sh index 5acc17b..fa282fa 100755 --- a/src/inline.sh +++ b/src/inline.sh @@ -20,6 +20,6 @@ varname="$1" echo "const char $varname[] =" -od -t x1 -A n -v | sed -e 's| ||g; s|..|\\x&|g; s|^|"|; s|$|"|' +od -t x1 -A n -v | sed -e 's|[ \t]||g; s|..|\\x&|g; s|^|"|; s|$|"|' echo ";" -- cgit v0.12 From da4e98b715c3e7cdf8bcc26f49868772daae7ff5 Mon Sep 17 00:00:00 2001 From: Mike Seplowitz Date: Mon, 12 Jan 2015 20:47:55 -0500 Subject: Start AIX port --- configure.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/configure.py b/configure.py index b8d7096..f9ea8eb 100755 --- a/configure.py +++ b/configure.py @@ -58,11 +58,13 @@ class Platform(object): self._platform = 'bitrig' elif self._platform.startswith('netbsd'): self._platform = 'netbsd' + elif self._platform.startswith('aix'): + self._platform = 'aix' @staticmethod def known_platforms(): return ['linux', 'darwin', 'freebsd', 'openbsd', 'solaris', 'sunos5', - 'mingw', 'msvc', 'gnukfreebsd', 'bitrig', 'netbsd'] + 'mingw', 'msvc', 'gnukfreebsd', 'bitrig', 'netbsd', 'aix'] def platform(self): return self._platform @@ -89,6 +91,9 @@ class Platform(object): def is_solaris(self): return self._platform == 'solaris' + def is_aix(self): + return self._platform == 'aix' + def uses_usr_local(self): return self._platform in ('freebsd', 'openbsd', 'bitrig') @@ -96,7 +101,9 @@ class Platform(object): return self._platform in ('linux', 'openbsd', 'bitrig') def supports_ninja_browse(self): - return not self.is_windows() and not self.is_solaris() + return (not self.is_windows() + and not self.is_solaris() + and not self.is_aix()) class Bootstrap: @@ -345,6 +352,8 @@ if platform.is_mingw(): ldflags.append('-static') elif platform.is_solaris(): cflags.remove('-fvisibility=hidden') +elif platform.is_aix(): + cflags.remove('-fvisibility=hidden') elif platform.is_msvc(): pass else: -- cgit v0.12 From 164e7f9494a7a9b9c6ec38b4cd4700bdb2aec1c5 Mon Sep 17 00:00:00 2001 From: Mike Seplowitz Date: Mon, 12 Jan 2015 19:13:39 -0500 Subject: Fix AIX compilation error related to printf macros On AIX, inttypes.h gets indirectly included by build_log.h. It's easiest just to ask for the printf format macros right away. --- src/build_log.cc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/build_log.cc b/src/build_log.cc index 589c6da..8a52514 100644 --- a/src/build_log.cc +++ b/src/build_log.cc @@ -12,6 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +// On AIX, inttypes.h gets indirectly included by build_log.h. +// It's easiest just to ask for the printf format macros right away. +#ifndef _WIN32 +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif +#endif + #include "build_log.h" #include @@ -19,9 +27,6 @@ #include #ifndef _WIN32 -#ifndef __STDC_FORMAT_MACROS -#define __STDC_FORMAT_MACROS -#endif #include #include #endif -- cgit v0.12 From 94c10a6a18ceadf78d27245ce389610c67a7cf2e Mon Sep 17 00:00:00 2001 From: Mike Seplowitz Date: Mon, 12 Jan 2015 20:48:07 -0500 Subject: Implement GetLoadAverage on AIX using libperfstat --- configure.py | 3 +++ src/util.cc | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/configure.py b/configure.py index f9ea8eb..fcea72a 100755 --- a/configure.py +++ b/configure.py @@ -505,6 +505,9 @@ if platform.is_msvc(): else: libs.append('-lninja') +if platform.is_aix(): + libs.append('-lperfstat') + all_targets = [] n.comment('Main executable is library plus main() function.') diff --git a/src/util.cc b/src/util.cc index aa47f2f..d150fe2 100644 --- a/src/util.cc +++ b/src/util.cc @@ -45,6 +45,8 @@ #elif defined(__SVR4) && defined(__sun) #include #include +#elif defined(_AIX) +#include #elif defined(linux) || defined(__GLIBC__) #include #endif @@ -573,6 +575,16 @@ double GetLoadAverage() { return posix_compatible_load; } +#elif defined(_AIX) +double GetLoadAverage() { + perfstat_cpu_total_t cpu_stats; + if (perfstat_cpu_total(NULL, &cpu_stats, sizeof(cpu_stats), 1) < 0) { + return -0.0f; + } + + // Calculation taken from comment in libperfstats.h + return double(cpu_stats.loadavg[0]) / double(1 << SBITS); +} #else double GetLoadAverage() { double loadavg[3] = { 0.0f, 0.0f, 0.0f }; -- cgit v0.12 From cdab57de00ab7ce157f1fdd601ce242588fcadce Mon Sep 17 00:00:00 2001 From: Mike Seplowitz Date: Mon, 12 Jan 2015 20:53:14 -0500 Subject: Fix getopt for AIX AIX supplies getopt but not getopt_long. We can't use the embedded getopt implementation, since the constness of its arguments doesn't match the AIX system routine. --- configure.py | 2 ++ src/getopt.c | 2 ++ src/getopt.h | 2 ++ src/ninja.cc | 3 +++ src/ninja_test.cc | 3 +++ 5 files changed, 12 insertions(+) diff --git a/configure.py b/configure.py index fcea72a..611030f 100755 --- a/configure.py +++ b/configure.py @@ -494,6 +494,8 @@ if platform.is_windows(): objs += cc('getopt') else: objs += cxx('subprocess-posix') +if platform.is_aix(): + objs += cc('getopt') if platform.is_msvc(): ninja_lib = n.build(built('ninja.lib'), 'ar', objs) else: diff --git a/src/getopt.c b/src/getopt.c index 3350fb9..0c2ef35 100644 --- a/src/getopt.c +++ b/src/getopt.c @@ -385,11 +385,13 @@ getopt_internal (int argc, char **argv, char *shortopts, return optopt; } +#ifndef _AIX int getopt (int argc, char **argv, char *optstring) { return getopt_internal (argc, argv, optstring, NULL, NULL, 0); } +#endif int getopt_long (int argc, char **argv, const char *shortopts, diff --git a/src/getopt.h b/src/getopt.h index b4247fb..965dc29 100644 --- a/src/getopt.h +++ b/src/getopt.h @@ -39,7 +39,9 @@ extern "C" extern int optopt; /* function prototypes */ +#ifndef _AIX int getopt (int argc, char **argv, char *optstring); +#endif int getopt_long (int argc, char **argv, const char *shortopts, const GETOPT_LONG_OPTION_T * longopts, int *longind); int getopt_long_only (int argc, char **argv, const char *shortopts, diff --git a/src/ninja.cc b/src/ninja.cc index a3d963f..f71f6dc 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -22,6 +22,9 @@ #include "getopt.h" #include #include +#elif defined(_AIX) +#include "getopt.h" +#include #else #include #include diff --git a/src/ninja_test.cc b/src/ninja_test.cc index 54d8784..11087b6 100644 --- a/src/ninja_test.cc +++ b/src/ninja_test.cc @@ -17,6 +17,9 @@ #ifdef _WIN32 #include "getopt.h" +#elif defined(_AIX) +#include "getopt.h" +#include #else #include #endif -- cgit v0.12 From b8f4ad12c0e110f45dbb8d1040c5904df4e29c76 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 26 Aug 2015 19:51:15 -0700 Subject: Don't output null.o during configure --- configure.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure.py b/configure.py index b8d7096..64f940b 100755 --- a/configure.py +++ b/configure.py @@ -325,7 +325,8 @@ else: cflags += ['-O2', '-DNDEBUG'] try: proc = subprocess.Popen( - [CXX, '-fdiagnostics-color', '-c', '-x', 'c++', '/dev/null'], + [CXX, '-fdiagnostics-color', '-c', '-x', 'c++', '/dev/null', + '-o', '/dev/null'], stdout=open(os.devnull, 'wb'), stderr=subprocess.STDOUT) if proc.wait() == 0: cflags += ['-fdiagnostics-color'] -- cgit v0.12 From 3f5b682bfd1f13c55a1eb6b783cbd3c61f664a7b Mon Sep 17 00:00:00 2001 From: Fredrik Medley Date: Thu, 3 Sep 2015 21:27:19 +0200 Subject: Describe why to use relative paths Ninja does resolve relative paths and file system links in paths. Therefore, such paths pointing to the same file will not match and may lead to an invalid dependency graph. Signed-off-by: Fredrik Medley --- doc/manual.asciidoc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc index 003c71e..47b7456 100644 --- a/doc/manual.asciidoc +++ b/doc/manual.asciidoc @@ -598,6 +598,11 @@ rule cc command = cl /showIncludes -c $in /Fo$out ---- +If the include directory directives are using absolute paths, your depfile +may result in a mixture of relative and absolute paths. Paths used by other +build rules need to match exactly. Therefore, it is recommended to use +relative paths in these cases. + [[ref_pool]] Pools ~~~~~ @@ -889,6 +894,9 @@ header file before starting a subsequent compilation step. (Once the header is used in compilation, a generated dependency file will then express the implicit dependency.) +File paths are compared as is, which means that an absolute path and a +relative path, pointing to the same file, are considered different by Ninja. + Variable expansion ~~~~~~~~~~~~~~~~~~ -- cgit v0.12 From 41d2b035e2387dcfbf5b268ffb3da446b28186ff Mon Sep 17 00:00:00 2001 From: Fredrik Medley Date: Fri, 12 Jun 2015 23:59:12 +0200 Subject: Optimize StringPiece hash map Replace strncmp with memcmp to improve performance. Signed-off-by: Fredrik Medley --- src/hash_map.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hash_map.h b/src/hash_map.h index abdba92..a91aeb9 100644 --- a/src/hash_map.h +++ b/src/hash_map.h @@ -76,7 +76,7 @@ struct StringPieceCmp : public hash_compare { return MurmurHash2(key.str_, key.len_); } bool operator()(const StringPiece& a, const StringPiece& b) const { - int cmp = strncmp(a.str_, b.str_, min(a.len_, b.len_)); + int cmp = memcmp(a.str_, b.str_, min(a.len_, b.len_)); if (cmp < 0) { return true; } else if (cmp > 0) { -- cgit v0.12 From fdb885dec781424d1317671db3fac74e6daf5347 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Mon, 7 Sep 2015 14:26:19 -0700 Subject: Merge LLVM r242069, make spell checking allocate less memory. --- src/edit_distance.cc | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/edit_distance.cc b/src/edit_distance.cc index a6719d3..3bb62b8 100644 --- a/src/edit_distance.cc +++ b/src/edit_distance.cc @@ -28,40 +28,42 @@ int EditDistance(const StringPiece& s1, // http://en.wikipedia.org/wiki/Levenshtein_distance // // Although the algorithm is typically described using an m x n - // array, only two rows are used at a time, so this implementation - // just keeps two separate vectors for those two rows. + // array, only one row plus one element are used at a time, so this + // implementation just keeps one vector for the row. To update one entry, + // only the entries to the left, top, and top-left are needed. The left + // entry is in row[x-1], the top entry is what's in row[x] from the last + // iteration, and the top-left entry is stored in previous. int m = s1.len_; int n = s2.len_; - vector previous(n + 1); - vector current(n + 1); - - for (int i = 0; i <= n; ++i) - previous[i] = i; + vector row(n + 1); + for (int i = 1; i <= n; ++i) + row[i] = i; for (int y = 1; y <= m; ++y) { - current[0] = y; - int best_this_row = current[0]; + row[0] = y; + int best_this_row = row[0]; + int previous = y - 1; for (int x = 1; x <= n; ++x) { + int old_row = row[x]; if (allow_replacements) { - current[x] = min(previous[x-1] + (s1.str_[y-1] == s2.str_[x-1] ? 0 : 1), - min(current[x-1], previous[x])+1); + row[x] = min(previous + (s1.str_[y - 1] == s2.str_[x - 1] ? 0 : 1), + min(row[x - 1], row[x]) + 1); } else { - if (s1.str_[y-1] == s2.str_[x-1]) - current[x] = previous[x-1]; + if (s1.str_[y - 1] == s2.str_[x - 1]) + row[x] = previous; else - current[x] = min(current[x-1], previous[x]) + 1; + row[x] = min(row[x - 1], row[x]) + 1; } - best_this_row = min(best_this_row, current[x]); + previous = old_row; + best_this_row = min(best_this_row, row[x]); } if (max_edit_distance && best_this_row > max_edit_distance) return max_edit_distance + 1; - - current.swap(previous); } - return previous[n]; + return row[n]; } -- cgit v0.12 From 23de4b25e9285d24cfa21fbed5acd105492866d8 Mon Sep 17 00:00:00 2001 From: Nicolas Despres Date: Wed, 29 Apr 2015 09:33:37 +0200 Subject: Cleanup build on SIGHUP. SIGHUP is sent when the connection hang up (i.e. when the terminal window is closed or the ssh connection is closed). --- src/subprocess-posix.cc | 12 +++++++++++- src/subprocess.h | 1 + src/subprocess_test.cc | 24 ++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/subprocess-posix.cc b/src/subprocess-posix.cc index f3baec2..2ddc709 100644 --- a/src/subprocess-posix.cc +++ b/src/subprocess-posix.cc @@ -64,6 +64,8 @@ bool Subprocess::Start(SubprocessSet* set, const string& command) { break; if (sigaction(SIGTERM, &set->old_term_act_, 0) < 0) break; + if (sigaction(SIGHUP, &set->old_hup_act_, 0) < 0) + break; if (sigprocmask(SIG_SETMASK, &set->old_mask_, 0) < 0) break; @@ -136,7 +138,8 @@ ExitStatus Subprocess::Finish() { if (exit == 0) return ExitSuccess; } else if (WIFSIGNALED(status)) { - if (WTERMSIG(status) == SIGINT || WTERMSIG(status) == SIGTERM) + if (WTERMSIG(status) == SIGINT || WTERMSIG(status) == SIGTERM + || WTERMSIG(status) == SIGHUP) return ExitInterrupted; } return ExitFailure; @@ -167,6 +170,8 @@ void SubprocessSet::HandlePendingInterruption() { interrupted_ = SIGINT; else if (sigismember(&pending, SIGTERM)) interrupted_ = SIGTERM; + else if (sigismember(&pending, SIGHUP)) + interrupted_ = SIGHUP; } SubprocessSet::SubprocessSet() { @@ -174,6 +179,7 @@ SubprocessSet::SubprocessSet() { sigemptyset(&set); sigaddset(&set, SIGINT); sigaddset(&set, SIGTERM); + sigaddset(&set, SIGHUP); if (sigprocmask(SIG_BLOCK, &set, &old_mask_) < 0) Fatal("sigprocmask: %s", strerror(errno)); @@ -184,6 +190,8 @@ SubprocessSet::SubprocessSet() { Fatal("sigaction: %s", strerror(errno)); if (sigaction(SIGTERM, &act, &old_term_act_) < 0) Fatal("sigaction: %s", strerror(errno)); + if (sigaction(SIGHUP, &act, &old_hup_act_) < 0) + Fatal("sigaction: %s", strerror(errno)); } SubprocessSet::~SubprocessSet() { @@ -193,6 +201,8 @@ SubprocessSet::~SubprocessSet() { Fatal("sigaction: %s", strerror(errno)); if (sigaction(SIGTERM, &old_term_act_, 0) < 0) Fatal("sigaction: %s", strerror(errno)); + if (sigaction(SIGHUP, &old_hup_act_, 0) < 0) + Fatal("sigaction: %s", strerror(errno)); if (sigprocmask(SIG_SETMASK, &old_mask_, 0) < 0) Fatal("sigprocmask: %s", strerror(errno)); } diff --git a/src/subprocess.h b/src/subprocess.h index a001fc9..51f40b2 100644 --- a/src/subprocess.h +++ b/src/subprocess.h @@ -98,6 +98,7 @@ struct SubprocessSet { struct sigaction old_int_act_; struct sigaction old_term_act_; + struct sigaction old_hup_act_; sigset_t old_mask_; #endif }; diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc index 07cc52f..066bbb7 100644 --- a/src/subprocess_test.cc +++ b/src/subprocess_test.cc @@ -122,6 +122,30 @@ TEST_F(SubprocessTest, InterruptParentWithSigTerm) { ASSERT_FALSE("We should have been interrupted"); } +TEST_F(SubprocessTest, InterruptChildWithSigHup) { + Subprocess* subproc = subprocs_.Add("kill -HUP $$"); + ASSERT_NE((Subprocess *) 0, subproc); + + while (!subproc->Done()) { + subprocs_.DoWork(); + } + + EXPECT_EQ(ExitInterrupted, subproc->Finish()); +} + +TEST_F(SubprocessTest, InterruptParentWithSigHup) { + Subprocess* subproc = subprocs_.Add("kill -HUP $PPID ; sleep 1"); + ASSERT_NE((Subprocess *) 0, subproc); + + while (!subproc->Done()) { + bool interrupted = subprocs_.DoWork(); + if (interrupted) + return; + } + + ASSERT_FALSE("We should have been interrupted"); +} + // A shell command to check if the current process is connected to a terminal. // This is different from having stdin/stdout/stderr be a terminal. (For // instance consider the command "yes < /dev/null > /dev/null 2>&1". -- cgit v0.12 From 9c61b016d555f79740b842614cf64d125c44d27f Mon Sep 17 00:00:00 2001 From: Nicolas Despres Date: Thu, 24 Sep 2015 20:02:43 +0200 Subject: Fix typo. --- src/build_log.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/build_log.h b/src/build_log.h index fe81a85..785961e 100644 --- a/src/build_log.h +++ b/src/build_log.h @@ -27,7 +27,7 @@ struct Edge; /// Can answer questions about the manifest for the BuildLog. struct BuildLogUser { - /// Return if a given output no longer part of the build manifest. + /// Return if a given output is no longer part of the build manifest. /// This is only called during recompaction and doesn't have to be fast. virtual bool IsPathDead(StringPiece s) const = 0; }; -- cgit v0.12 From 33b3480d82057a01de8f8de5671046c26c88db4b Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 5 Oct 2015 14:17:56 -0700 Subject: Print output file on failure Modify the FAILED: output to provide the output files that failed to build, followed by the failed command on the next line. This makes the failure much easier to read, as you can immediately see much shorter name of the file that failed instead of trying to parse a very long command line. It also makes manually re-running the failed command much easier because you can copy the whole line without ending up with the FAILED: prefix. --- src/build.cc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/build.cc b/src/build.cc index e4820d0..8865a1d 100644 --- a/src/build.cc +++ b/src/build.cc @@ -125,8 +125,15 @@ void BuildStatus::BuildEdgeFinished(Edge* edge, PrintStatus(edge); // Print the command that is spewing before printing its output. - if (!success) - printer_.PrintOnNewLine("FAILED: " + edge->EvaluateCommand() + "\n"); + if (!success) { + string outputs; + for (vector::const_iterator o = edge->outputs_.begin(); + o != edge->outputs_.end(); ++o) + outputs += (*o)->path() + " "; + + printer_.PrintOnNewLine("FAILED: " + outputs + "\n"); + printer_.PrintOnNewLine(edge->EvaluateCommand() + "\n"); + } if (!output.empty()) { // ninja sets stdout and stderr of subprocesses to a pipe, to be able to -- cgit v0.12 From deb6a401784915e2540ced64ffe4ee9893285dd7 Mon Sep 17 00:00:00 2001 From: peter1000 Date: Mon, 19 Oct 2015 18:44:30 -0200 Subject: Fixes typo --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 66a6516..20630c4 100644 --- a/README +++ b/README @@ -13,7 +13,7 @@ To build your own binary, on many platforms it should be sufficient to just run `./configure.py --bootstrap`; for more details see HACKING.md. (Also read that before making changes to Ninja, as it has advice.) -Installation is not necessary because the only required file is is the +Installation is not necessary because the only required file is the resulting ninja binary. However, to enable features like Bash completion and Emacs and Vim editing modes, some files in misc/ must be copied to appropriate locations. -- cgit v0.12 From aa14d6e067c6491555150c1f40de1388c3491124 Mon Sep 17 00:00:00 2001 From: Mike Seplowitz Date: Wed, 21 Oct 2015 18:37:50 -0400 Subject: Separate bootstrapped build from final build AIX does not support rebuilding ninja in-place from the bootstrapped ninja. --- .gitignore | 1 + configure.py | 33 +++++++++++++++++++++++---------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index f7fc044..5a85203 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ TAGS /build /build.ninja /ninja +/ninja.bootstrap /build_log_perftest /canon_perftest /depfile_parser_perftest diff --git a/configure.py b/configure.py index 611030f..1a6d51c 100755 --- a/configure.py +++ b/configure.py @@ -105,6 +105,8 @@ class Platform(object): and not self.is_solaris() and not self.is_aix()) + def can_rebuild_in_place(self): + return not (self.is_windows() or self.is_aix()) class Bootstrap: """API shim for ninja_syntax.Writer that instead runs the commands. @@ -639,17 +641,28 @@ n.build('all', 'phony', all_targets) n.close() print('wrote %s.' % BUILD_FILENAME) -verbose = '' -if options.verbose: - verbose = ' -v' - if options.bootstrap: print('bootstrap complete. rebuilding...') - if platform.is_windows(): - bootstrap_exe = 'ninja.bootstrap.exe' + + rebuild_args = [] + + if platform.can_rebuild_in_place(): + rebuild_args.append('./ninja') + else: + if platform.is_windows(): + bootstrap_exe = 'ninja.bootstrap.exe' + final_exe = 'ninja.exe' + else: + bootstrap_exe = './ninja.bootstrap' + final_exe = './ninja' + if os.path.exists(bootstrap_exe): os.unlink(bootstrap_exe) - os.rename('ninja.exe', bootstrap_exe) - subprocess.check_call('ninja.bootstrap.exe%s' % verbose, shell=True) - else: - subprocess.check_call('./ninja%s' % verbose, shell=True) + os.rename(final_exe, bootstrap_exe) + + rebuild_args.append(bootstrap_exe) + + if options.verbose: + rebuild_args.append('-v') + + subprocess.check_call(rebuild_args) -- cgit v0.12 From 8f658d63298c5fae29616b8d922d03bb7ea67c0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Vall=C3=A9e?= Date: Fri, 23 Oct 2015 16:36:28 -0400 Subject: Fix wrapping file paths in Writer.comment Long file names, especially with hyphens will get incorrectly wrapped by the comment method. Pass has_path=True to prevent this type of wrapping. This is mainly so that longer path names can show up in comments on their on line without breaking them up. --- misc/ninja_syntax.py | 7 +++++-- misc/ninja_syntax_test.py | 11 +++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/misc/ninja_syntax.py b/misc/ninja_syntax.py index 8673518..091e054 100644 --- a/misc/ninja_syntax.py +++ b/misc/ninja_syntax.py @@ -21,8 +21,11 @@ class Writer(object): def newline(self): self.output.write('\n') - def comment(self, text): - for line in textwrap.wrap(text, self.width - 2): + def comment(self, text, has_path=False): + args = {} + if has_path: + args['break_long_words'] = args['break_on_hyphens'] = False + for line in textwrap.wrap(text, self.width - 2, **args): self.output.write('# ' + line + '\n') def variable(self, key, value, indent=0): diff --git a/misc/ninja_syntax_test.py b/misc/ninja_syntax_test.py index 36b2e7b..46ce382 100755 --- a/misc/ninja_syntax_test.py +++ b/misc/ninja_syntax_test.py @@ -45,6 +45,17 @@ class TestLineWordWrap(unittest.TestCase): INDENT + 'y']) + '\n', self.out.getvalue()) + def test_comment_wrap(self): + # We should wrap the comments + self.n.comment('Hello there') + self.assertEqual('# Hello\n# there\n', self.out.getvalue()) + + def test_comment_wrap_filename(self): + # Filenames shoud not be wrapped + self.n.comment('Hello /usr/local/build-tools/bin', has_path=True) + self.assertEqual('# Hello\n# /usr/local/build-tools/bin\n', + self.out.getvalue()) + def test_short_words_indented(self): # Test that indent is taking into acount when breaking subsequent lines. # The second line should not be ' to tree', as that's longer than the -- cgit v0.12 From 530d2b16662de786f10243b96f6e3596e90b357a Mon Sep 17 00:00:00 2001 From: Ben Falconer Date: Fri, 30 Oct 2015 14:43:32 +0000 Subject: Speed up zsh target completion --- misc/zsh-completion | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/misc/zsh-completion b/misc/zsh-completion index fd9b3a7..ad7b87f 100644 --- a/misc/zsh-completion +++ b/misc/zsh-completion @@ -23,8 +23,7 @@ __get_targets() { eval dir="${opt_args[-C]}" fi targets_command="ninja -C \"${dir}\" -t targets" - eval ${targets_command} 2>/dev/null | while read -r a b; do echo $a | cut -d ':' -f1; done; - + eval ${targets_command} 2>/dev/null | sed "s/^\(.*\): .*$/\1/" } __get_tools() { -- cgit v0.12 From fc135c456d1ba2a39dfb5efe5004102168bef856 Mon Sep 17 00:00:00 2001 From: Hadi Moshayedi Date: Sat, 31 Oct 2015 06:13:33 -0400 Subject: Speed-up bash target auto-complete. These speed-ups include: * Let compgen do the command substitution. Similar to https://lists.gnu.org/archive/html/bug-bash/2012-03/msg00115.html * Use "cut" instead of "awk" for separating fields. --- misc/bash-completion | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/misc/bash-completion b/misc/bash-completion index 0536760..e604cd4 100644 --- a/misc/bash-completion +++ b/misc/bash-completion @@ -49,9 +49,8 @@ _ninja_target() { C) eval dir="$OPTARG" ;; esac done; - targets_command="eval ninja -C \"${dir}\" -t targets all" - targets=$(${targets_command} 2>/dev/null | awk -F: '{print $1}') - COMPREPLY=($(compgen -W "$targets" -- "$cur")) + targets_command="eval ninja -C \"${dir}\" -t targets all 2>/dev/null | cut -d: -f1" + COMPREPLY=($(compgen -W '`${targets_command}`' -- "$cur")) fi return } -- cgit v0.12 From 74a40291bb3b7d7ae1cda1b165acae28c41dc3bc Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Wed, 11 Nov 2015 11:36:16 -0800 Subject: test commit --- HACKING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HACKING.md b/HACKING.md index 7fdb152..6f88943 100644 --- a/HACKING.md +++ b/HACKING.md @@ -53,7 +53,7 @@ obvious exceptions (fixing typos in comments don't need tests). I am very wary of changes that increase the complexity of Ninja (in particular, new build file syntax or command-line flags) or increase -the maintenance burden of Ninja. Ninja is already successfully in use +the maintenance burden of Ninja. Ninja is already successfully used by hundreds of developers for large projects and it already achieves (most of) the goals I set out for it to do. It's probably best to discuss new feature ideas on the mailing list before I shoot down your -- cgit v0.12 From c2307f92772a56818f7dc5e4b4fb03c26b4693c2 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Wed, 11 Nov 2015 11:53:15 -0800 Subject: Make links point to org page --- HACKING.md | 2 +- misc/ninja.vim | 6 +++--- misc/packaging/ninja.spec | 2 +- src/build.cc | 2 +- src/build_test.cc | 8 ++++---- src/graph_test.cc | 4 ++-- src/ninja.cc | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/HACKING.md b/HACKING.md index 6f88943..d8cb2a2 100644 --- a/HACKING.md +++ b/HACKING.md @@ -74,7 +74,7 @@ build "all" before committing to verify the other source still works! ## Testing performance impact of changes If you have a Chrome build handy, it's a good test case. Otherwise, -[the github downoads page](https://github.com/martine/ninja/downloads) +[the github downoads page](https://github.com/ninja-build/ninja/releases) has a copy of the Chrome build files (and depfiles). You can untar that, then run diff --git a/misc/ninja.vim b/misc/ninja.vim index f34588f..13add49 100644 --- a/misc/ninja.vim +++ b/misc/ninja.vim @@ -1,6 +1,6 @@ " ninja build file syntax. " Language: ninja build file as described at -" http://martine.github.com/ninja/manual.html +" https://ninja-build.github.io/manual.html " Version: 1.4 " Last Change: 2014/05/13 " Maintainer: Nicolas Weber @@ -9,8 +9,8 @@ " upstream. " ninja lexer and parser are at -" https://github.com/martine/ninja/blob/master/src/lexer.in.cc -" https://github.com/martine/ninja/blob/master/src/manifest_parser.cc +" https://github.com/ninja-build/ninja/blob/master/src/lexer.in.cc +" https://github.com/ninja-build/ninja/blob/master/src/manifest_parser.cc if exists("b:current_syntax") finish diff --git a/misc/packaging/ninja.spec b/misc/packaging/ninja.spec index fa244a6..05f5a07 100644 --- a/misc/packaging/ninja.spec +++ b/misc/packaging/ninja.spec @@ -4,7 +4,7 @@ Version: %{ver} Release: %{rel}%{?dist} Group: Development/Tools License: Apache 2.0 -URL: https://github.com/martine/ninja +URL: https://github.com/ninja-build/ninja Source0: %{name}-%{version}-%{rel}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{rel} diff --git a/src/build.cc b/src/build.cc index e4820d0..b8c2560 100644 --- a/src/build.cc +++ b/src/build.cc @@ -362,7 +362,7 @@ void Plan::ScheduleWork(Edge* edge) { if (pool->ShouldDelayEdge()) { // The graph is not completely clean. Some Nodes have duplicate Out edges. // We need to explicitly ignore these here, otherwise their work will get - // scheduled twice (see https://github.com/martine/ninja/pull/519) + // scheduled twice (see https://github.com/ninja-build/ninja/pull/519) if (ready_.count(edge)) { return; } diff --git a/src/build_test.cc b/src/build_test.cc index 52a17c9..20fb664 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -718,7 +718,7 @@ TEST_F(BuildTest, TwoOutputs) { } // Test case from -// https://github.com/martine/ninja/issues/148 +// https://github.com/ninja-build/ninja/issues/148 TEST_F(BuildTest, MultiOutIn) { ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, "rule touch\n" @@ -1299,7 +1299,7 @@ TEST_F(BuildWithLogTest, RestatSingleDependentOutputDirty) { } // Test scenario, in which an input file is removed, but output isn't changed -// https://github.com/martine/ninja/issues/295 +// https://github.com/ninja-build/ninja/issues/295 TEST_F(BuildWithLogTest, RestatMissingInput) { ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, "rule true\n" @@ -2047,7 +2047,7 @@ TEST_F(BuildWithDepsLogTest, DepFileDepsLogCanonicalize) { #endif /// Check that a restat rule doesn't clear an edge if the depfile is missing. -/// Follows from: https://github.com/martine/ninja/issues/603 +/// Follows from: https://github.com/ninja-build/ninja/issues/603 TEST_F(BuildTest, RestatMissingDepfile) { const char* manifest = "rule true\n" @@ -2071,7 +2071,7 @@ const char* manifest = } /// Check that a restat rule doesn't clear an edge if the deps are missing. -/// https://github.com/martine/ninja/issues/603 +/// https://github.com/ninja-build/ninja/issues/603 TEST_F(BuildWithDepsLogTest, RestatMissingDepfileDepslog) { string err; const char* manifest = diff --git a/src/graph_test.cc b/src/graph_test.cc index e41f5cc..44be8a5 100644 --- a/src/graph_test.cc +++ b/src/graph_test.cc @@ -153,7 +153,7 @@ TEST_F(GraphTest, VarInOutPathEscaping) { #endif } -// Regression test for https://github.com/martine/ninja/issues/380 +// Regression test for https://github.com/ninja-build/ninja/issues/380 TEST_F(GraphTest, DepfileWithCanonicalizablePath) { ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, "rule catdep\n" @@ -172,7 +172,7 @@ TEST_F(GraphTest, DepfileWithCanonicalizablePath) { EXPECT_FALSE(GetNode("out.o")->dirty()); } -// Regression test for https://github.com/martine/ninja/issues/404 +// Regression test for https://github.com/ninja-build/ninja/issues/404 TEST_F(GraphTest, DepfileRemoved) { ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, "rule catdep\n" diff --git a/src/ninja.cc b/src/ninja.cc index a3d963f..fe4a580 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -254,7 +254,7 @@ bool NinjaMain::RebuildManifest(const char* input_file, string* err) { // Even if the manifest was cleaned by a restat rule, claim that it was // rebuilt. Not doing so can lead to crashes, see - // https://github.com/martine/ninja/issues/874 + // https://github.com/ninja-build/ninja/issues/874 return builder.Build(err); } -- cgit v0.12 From 5e076f059383e5d3a5e5aa376240f6c82ca056ec Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Wed, 11 Nov 2015 13:27:16 -0800 Subject: Update link to point to ninja-build.org --- misc/ninja.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/ninja.vim b/misc/ninja.vim index 13add49..190d9ce 100644 --- a/misc/ninja.vim +++ b/misc/ninja.vim @@ -1,6 +1,6 @@ " ninja build file syntax. " Language: ninja build file as described at -" https://ninja-build.github.io/manual.html +" http://ninja-build.org/manual.html " Version: 1.4 " Last Change: 2014/05/13 " Maintainer: Nicolas Weber -- cgit v0.12 From b1129b15ffad601a18ff4ac83130a71f5de64e43 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Wed, 11 Nov 2015 13:34:13 -0800 Subject: drop more references to github/martine --- README | 6 +++--- RELEASING | 4 ++-- doc/manual.asciidoc | 1 - 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/README b/README index 20630c4..59d7ff9 100644 --- a/README +++ b/README @@ -1,12 +1,12 @@ Ninja is a small build system with a focus on speed. -http://martine.github.com/ninja/ +http://ninja-build.org/ -See the manual -- http://martine.github.com/ninja/manual.html or +See the manual -- http://ninja-build.org/manual.html or doc/manual.asciidoc included in the distribution -- for background and more details. Binaries for Linux, Mac, and Windows are available at - https://github.com/martine/ninja/releases + https://github.com/ninja-build/ninja/releases Run './ninja -h' for Ninja help. To build your own binary, on many platforms it should be sufficient to diff --git a/RELEASING b/RELEASING index 4d08253..20da5d9 100644 --- a/RELEASING +++ b/RELEASING @@ -17,14 +17,14 @@ Push new release branch: Release on github: 1. https://github.com/blog/1547-release-your-software - Add binaries to https://github.com/martine/ninja/releases + Add binaries to https://github.com/ninja-build/ninja/releases Make announcement on mailing list: 1. copy old mail Update website: 1. Make sure your ninja checkout is on the v1.5.0 tag -2. Clone https://github.com/martine/martine.github.io +2. Clone https://github.com/ninja-build/ninja-build.github.io 3. In that repo, `cd ninja && ./update-docs.sh` 4. Update index.html with newest version and link to release notes 5. git commit -m 'run update-docs.sh, 1.5.0 release' diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc index 47b7456..3c193f1 100644 --- a/doc/manual.asciidoc +++ b/doc/manual.asciidoc @@ -1,6 +1,5 @@ Ninja ===== -Evan Martin Introduction -- cgit v0.12 From 805875b44aa2d52cf5eb943db055d3b7e9c8ed21 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Wed, 11 Nov 2015 13:54:14 -0800 Subject: Try to fix #685 --- configure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.py b/configure.py index 64f940b..0cff196 100755 --- a/configure.py +++ b/configure.py @@ -81,7 +81,7 @@ class Platform(object): stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = popen.communicate() - return '/FS ' in str(out) + return b'/FS ' in out def is_windows(self): return self.is_mingw() or self.is_msvc() -- cgit v0.12 From 8c18cf97ff0ff2a6347865443052913c598d7ee6 Mon Sep 17 00:00:00 2001 From: Alex Vallee Date: Wed, 11 Nov 2015 22:48:39 -0500 Subject: Fix indent in pull request #1042. --- misc/ninja_syntax.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/ninja_syntax.py b/misc/ninja_syntax.py index 091e054..f285420 100644 --- a/misc/ninja_syntax.py +++ b/misc/ninja_syntax.py @@ -24,7 +24,7 @@ class Writer(object): def comment(self, text, has_path=False): args = {} if has_path: - args['break_long_words'] = args['break_on_hyphens'] = False + args['break_long_words'] = args['break_on_hyphens'] = False for line in textwrap.wrap(text, self.width - 2, **args): self.output.write('# ' + line + '\n') -- cgit v0.12 From 274db07276106944f63a3387bc3826e2211da154 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Tue, 17 Nov 2015 14:02:43 -0800 Subject: provide a better title for the manual In particular, this helps web search engines index it. --- doc/manual.asciidoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc index 3c193f1..3fef697 100644 --- a/doc/manual.asciidoc +++ b/doc/manual.asciidoc @@ -1,5 +1,5 @@ -Ninja -===== +The Ninja build system +====================== Introduction -- cgit v0.12 From a6859ac8af7377b1a1e136a6b296f8e8ca950507 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Tue, 17 Nov 2015 14:04:50 -0800 Subject: improve docs generation, including PDF output - Fix the manual build rules (missing the .xsl as an input). - Add a README describing how the docs build works. - Add rules that generate PDF, just 'cause we can. --- configure.py | 12 +++++++++--- doc/README.md | 11 +++++++++++ doc/dblatex.xsl | 7 +++++++ doc/docbook.xsl | 22 ++++++++++++++++++---- 4 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 doc/README.md create mode 100644 doc/dblatex.xsl diff --git a/configure.py b/configure.py index 0710ea2..2104aff 100755 --- a/configure.py +++ b/configure.py @@ -592,13 +592,19 @@ n.rule('asciidoc', n.rule('xsltproc', command='xsltproc --nonet doc/docbook.xsl $in > $out', description='XSLTPROC $out') -xml = n.build(built('manual.xml'), 'asciidoc', doc('manual.asciidoc')) -manual = n.build(doc('manual.html'), 'xsltproc', xml, - implicit=doc('style.css')) +docbookxml = n.build(built('manual.xml'), 'asciidoc', doc('manual.asciidoc')) +manual = n.build(doc('manual.html'), 'xsltproc', docbookxml, + implicit=[doc('style.css'), doc('docbook.xsl')]) n.build('manual', 'phony', order_only=manual) n.newline() +n.rule('dblatex', + command='dblatex -q -o $out -p doc/dblatex.xsl $in', + description='DBLATEX $out') +n.build(doc('manual.pdf'), 'dblatex', docbookxml, + implicit=[doc('dblatex.xsl')]) + n.comment('Generate Doxygen.') n.rule('doxygen', command='doxygen $in', diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..6afe5d4 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,11 @@ +This directory contains the Ninja manual and support files used in +building it. Here's a brief overview of how it works. + +The source text, `manual.asciidoc`, is written in the AsciiDoc format. +AsciiDoc can generate HTML but it doesn't look great; instead, we use +AsciiDoc to generate the Docbook XML format and then provide our own +Docbook XSL tweaks to produce HTML from that. + +In theory using AsciiDoc and DocBook allows us to produce nice PDF +documentation etc. In reality it's not clear anyone wants that, but the +build rules are in place to generate it if you install dblatex. diff --git a/doc/dblatex.xsl b/doc/dblatex.xsl new file mode 100644 index 0000000..c0da212 --- /dev/null +++ b/doc/dblatex.xsl @@ -0,0 +1,7 @@ + + + + 0 + 0 + diff --git a/doc/docbook.xsl b/doc/docbook.xsl index 8afdc8c..19cc126 100644 --- a/doc/docbook.xsl +++ b/doc/docbook.xsl @@ -1,15 +1,29 @@ - + ]> + + + + - - + + + book toc + + + 0 + + ul Date: Tue, 17 Nov 2015 14:13:44 -0800 Subject: drop a [horizontal] definition table The two columns of the table run together, making it hard to read. --- doc/manual.asciidoc | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc index 3fef697..6aef164 100644 --- a/doc/manual.asciidoc +++ b/doc/manual.asciidoc @@ -717,7 +717,6 @@ spaces within a token must be escaped. There is only one escape character, `$`, and it has the following behaviors: -[horizontal] `$` followed by a newline:: escape the newline (continue the current line across a line break). -- cgit v0.12 From 3be9710589405634cf65732eb5ded0a0210614a3 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Tue, 17 Nov 2015 14:23:13 -0800 Subject: put warning flags together --- configure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.py b/configure.py index 2104aff..7584a3f 100755 --- a/configure.py +++ b/configure.py @@ -321,11 +321,11 @@ if platform.is_msvc(): else: cflags = ['-g', '-Wall', '-Wextra', '-Wno-deprecated', + '-Wno-missing-field-initializers', '-Wno-unused-parameter', '-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 424325728862347298013c1e39761e51e0e79e9d Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Tue, 17 Nov 2015 14:26:18 -0800 Subject: rename the build-file variable $sourcedir to $root Confusingly, $sourcedir is the path to the root of the repository, *not* the path to the src/ directory. --- configure.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/configure.py b/configure.py index 7584a3f..845b046 100755 --- a/configure.py +++ b/configure.py @@ -265,11 +265,11 @@ if platform.is_msvc(): objext = '.obj' def src(filename): - return os.path.join('$sourcedir', 'src', filename) + return os.path.join('$root', 'src', filename) def built(filename): return os.path.join('$builddir', filename) def doc(filename): - return os.path.join('$sourcedir', 'doc', filename) + return os.path.join('$root', 'doc', filename) def cc(name, **kwargs): return n.build(built(name + objext), 'cxx', src(name + '.c'), **kwargs) def cxx(name, **kwargs): @@ -281,7 +281,7 @@ def binary(name): return exe return name -n.variable('sourcedir', sourcedir) +n.variable('root', sourcedir) n.variable('builddir', 'build') n.variable('cxx', CXX) if platform.is_msvc(): @@ -624,12 +624,12 @@ n.newline() if not host.is_mingw(): n.comment('Regenerate build files if build script changes.') n.rule('configure', - command='${configure_env}%s $sourcedir/configure.py $configure_args' % + command='${configure_env}%s $root/configure.py $configure_args' % options.with_python, generator=True) n.build('build.ninja', 'configure', - implicit=['$sourcedir/configure.py', - os.path.normpath('$sourcedir/misc/ninja_syntax.py')]) + implicit=['$root/configure.py', + os.path.normpath('$root/misc/ninja_syntax.py')]) n.newline() n.default(ninja) -- cgit v0.12 From a1159ff0d500626069e75ed8e697f78df1befd4d Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Tue, 17 Nov 2015 14:28:08 -0800 Subject: when building with cwd = source root, use relative paths The pull request that introduced building from a directory other than the source dir, https://github.com/ninja-build/ninja/pull/979, made it so all source paths are effectively absolute paths. This change restores the old behavior in the case when you are building in the source. See the comments there. --- configure.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/configure.py b/configure.py index 845b046..d60cbac 100755 --- a/configure.py +++ b/configure.py @@ -281,7 +281,12 @@ def binary(name): return exe return name -n.variable('root', sourcedir) +root = sourcedir +if root == os.getcwd(): + # In the common case where we're building directly in the source + # tree, simplify all the paths to just be cwd-relative. + root = '.' +n.variable('root', root) n.variable('builddir', 'build') n.variable('cxx', CXX) if platform.is_msvc(): -- cgit v0.12 From 9d7213e43dcc66f2dccee3e6a97aacd700947671 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Thu, 19 Nov 2015 12:41:32 -0800 Subject: prefer https in readme --- README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README b/README index 59d7ff9..a1535ff 100644 --- a/README +++ b/README @@ -1,7 +1,7 @@ Ninja is a small build system with a focus on speed. -http://ninja-build.org/ +https://ninja-build.org/ -See the manual -- http://ninja-build.org/manual.html or +See the manual -- https://ninja-build.org/manual.html or doc/manual.asciidoc included in the distribution -- for background and more details. -- cgit v0.12 From 97e96284a3cec71b8cf78a3bec5b525cc3e538bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Vall=C3=A9e?= Date: Fri, 27 Nov 2015 16:09:34 -0500 Subject: Disable long word wrapping entirely in comments. As pointed out by nico, we should unconditionally disable breaking of long words in comments. It is unlikely long words that are in comments should be split (like pathnames). --- misc/ninja_syntax.py | 6 ++---- misc/ninja_syntax_test.py | 7 +------ 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/misc/ninja_syntax.py b/misc/ninja_syntax.py index f285420..73d2209 100644 --- a/misc/ninja_syntax.py +++ b/misc/ninja_syntax.py @@ -22,10 +22,8 @@ class Writer(object): self.output.write('\n') def comment(self, text, has_path=False): - args = {} - if has_path: - args['break_long_words'] = args['break_on_hyphens'] = False - for line in textwrap.wrap(text, self.width - 2, **args): + for line in textwrap.wrap(text, self.width - 2, break_long_words=False, + break_on_hyphens=False): self.output.write('# ' + line + '\n') def variable(self, key, value, indent=0): diff --git a/misc/ninja_syntax_test.py b/misc/ninja_syntax_test.py index 46ce382..c9755b8 100755 --- a/misc/ninja_syntax_test.py +++ b/misc/ninja_syntax_test.py @@ -46,13 +46,8 @@ class TestLineWordWrap(unittest.TestCase): self.out.getvalue()) def test_comment_wrap(self): - # We should wrap the comments - self.n.comment('Hello there') - self.assertEqual('# Hello\n# there\n', self.out.getvalue()) - - def test_comment_wrap_filename(self): # Filenames shoud not be wrapped - self.n.comment('Hello /usr/local/build-tools/bin', has_path=True) + self.n.comment('Hello /usr/local/build-tools/bin') self.assertEqual('# Hello\n# /usr/local/build-tools/bin\n', self.out.getvalue()) -- cgit v0.12 From 336814c4ecb7993b994567527c27e0915dc207fb Mon Sep 17 00:00:00 2001 From: Thiago Farina Date: Fri, 4 Dec 2015 20:31:19 -0200 Subject: make use of Bindings typedef Looks like we declared this typedef but never used it. It seems we just forgot to use it so this patch makes uses of it now. Otherwise we could just delete it. --- src/eval_env.cc | 2 +- src/eval_env.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/eval_env.cc b/src/eval_env.cc index e991d21..937a673 100644 --- a/src/eval_env.cc +++ b/src/eval_env.cc @@ -55,7 +55,7 @@ void Rule::AddBinding(const string& key, const EvalString& val) { } const EvalString* Rule::GetBinding(const string& key) const { - map::const_iterator i = bindings_.find(key); + Bindings::const_iterator i = bindings_.find(key); if (i == bindings_.end()) return NULL; return &i->second; diff --git a/src/eval_env.h b/src/eval_env.h index 28c4d16..999ce42 100644 --- a/src/eval_env.h +++ b/src/eval_env.h @@ -57,7 +57,6 @@ struct Rule { const string& name() const { return name_; } - typedef map Bindings; void AddBinding(const string& key, const EvalString& val); static bool IsReservedBinding(const string& var); @@ -69,7 +68,8 @@ struct Rule { friend struct ManifestParser; string name_; - map bindings_; + typedef map Bindings; + Bindings bindings_; }; /// An Env which contains a mapping of variables to values -- cgit v0.12 From e9bbc179deac27e5e13a4d1f36acf5568f9291a1 Mon Sep 17 00:00:00 2001 From: ptzz Date: Sun, 6 Dec 2015 02:23:49 +0100 Subject: Fix typo --- HACKING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HACKING.md b/HACKING.md index d8cb2a2..c544615 100644 --- a/HACKING.md +++ b/HACKING.md @@ -20,7 +20,7 @@ You should end up with a `ninja` binary (or `ninja.exe`) in the source root. On Windows, you'll need to install Python to run `configure.py`, and run everything under a Visual Studio Tools Command Prompt (or after running `vcvarsall` in a normal command prompt). See below if you -want to use mingw or some other compiler instead using Visual Studio. +want to use mingw or some other compiler instead of Visual Studio. ### Adjusting build flags -- cgit v0.12 From 41a1012b9a40e0b883bd5013c0c0a85e4348bb9d Mon Sep 17 00:00:00 2001 From: Niklas Rosenstein Date: Sun, 6 Dec 2015 16:14:33 +0100 Subject: add "msvc_deps_prefix" to Rule::IsReservedBinding() fixes the error about an unexpected variable for a rule that declares the msvc_deps_prefix. The manual suggests that this should work since Ninja 1.5 (https://ninja-build.org/manual.html#ref_rule). Closes #1043 --- src/eval_env.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/eval_env.cc b/src/eval_env.cc index e991d21..0abfdda 100644 --- a/src/eval_env.cc +++ b/src/eval_env.cc @@ -71,7 +71,8 @@ bool Rule::IsReservedBinding(const string& var) { var == "pool" || var == "restat" || var == "rspfile" || - var == "rspfile_content"; + var == "rspfile_content" || + var == "msvc_deps_prefix"; } const map& BindingEnv::GetRules() const { -- cgit v0.12 From 956876408a64135d8448f2f55fc9213003edb40e Mon Sep 17 00:00:00 2001 From: Nicolas Despres Date: Wed, 9 Dec 2015 23:18:18 +0100 Subject: Do not attempt to rebuild infinitely in dry-run mode. --- src/ninja.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ninja.cc b/src/ninja.cc index 21dede6..a73f83c 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -1135,6 +1135,10 @@ int real_main(int argc, char** argv) { // Attempt to rebuild the manifest before building anything else if (ninja.RebuildManifest(options.input_file, &err)) { + // In dry_run mode the regeneration will succeed without changing the + // manifest forever. Better to return immediately. + if (config.dry_run) + return 0; // Start the build over with the new manifest. continue; } else if (!err.empty()) { -- cgit v0.12 From 50a1cc39b731ff144781d7f3020769d7f4c54e28 Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Wed, 16 Dec 2015 08:48:59 -0800 Subject: use the default font size for manual headings The third-level subsection headings were almost indistinguishable from the second-level ones. Fix this by just using the default styling. --- doc/style.css | 6 ------ 1 file changed, 6 deletions(-) diff --git a/doc/style.css b/doc/style.css index 5d14a1c..9976c03 100644 --- a/doc/style.css +++ b/doc/style.css @@ -24,12 +24,6 @@ div.chapter { margin-top: 4em; border-top: solid 2px black; } -.section .title { - font-size: 1.3em; -} -.section .section .title { - font-size: 1.2em; -} p { margin-top: 0; } -- cgit v0.12 From 202fdd64c869eb7e1d6084a10fe93f164aa4cedb Mon Sep 17 00:00:00 2001 From: Evan Martin Date: Wed, 16 Dec 2015 08:44:23 -0800 Subject: add a section to the manual discussing the command= variable This includes a mention of using cmd /c on Windows. This would have helped on issue #1070 for example. --- doc/manual.asciidoc | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc index 003c71e..4d0727e 100644 --- a/doc/manual.asciidoc +++ b/doc/manual.asciidoc @@ -778,11 +778,9 @@ A `rule` block contains a list of `key = value` declarations that affect the processing of the rule. Here is a full list of special keys. -`command` (_required_):: the command line to run. This string (after - $variables are expanded) is passed directly to `sh -c` without - interpretation by Ninja. Each `rule` may have only one `command` - declaration. To specify multiple commands use `&&` (or similar) to - concatenate operations. +`command` (_required_):: the command line to run. Each `rule` may + have only one `command` declaration. See <> for more details on quoting and executing multiple commands. `depfile`:: path to an optional `Makefile` that contains extra _implicit dependencies_ (see < Date: Wed, 16 Dec 2015 15:29:02 -0800 Subject: Canonicalize "." to "." "." is a legal path, if the string is empty after canonicalization return ".". --- src/util.cc | 4 ++-- src/util_test.cc | 32 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/util.cc b/src/util.cc index d150fe2..e31fd1f 100644 --- a/src/util.cc +++ b/src/util.cc @@ -226,8 +226,8 @@ bool CanonicalizePath(char* path, size_t* len, unsigned int* slash_bits, } if (dst == start) { - *err = "path canonicalizes to the empty path"; - return false; + *dst++ = '.'; + *dst++ = '\0'; } *len = dst - start - 1; diff --git a/src/util_test.cc b/src/util_test.cc index 8ca7f56..33a4107 100644 --- a/src/util_test.cc +++ b/src/util_test.cc @@ -91,6 +91,22 @@ TEST(CanonicalizePath, PathSamples) { path = "/"; EXPECT_TRUE(CanonicalizePath(&path, &err)); EXPECT_EQ("", path); + + path = "/foo/.."; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ("", path); + + path = "."; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ(".", path); + + path = "./."; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ(".", path); + + path = "foo/.."; + EXPECT_TRUE(CanonicalizePath(&path, &err)); + EXPECT_EQ(".", path); } #ifdef _WIN32 @@ -288,22 +304,6 @@ TEST(CanonicalizePath, TooManyComponents) { } #endif -TEST(CanonicalizePath, EmptyResult) { - string path; - string err; - - EXPECT_FALSE(CanonicalizePath(&path, &err)); - EXPECT_EQ("empty path", err); - - path = "."; - EXPECT_FALSE(CanonicalizePath(&path, &err)); - EXPECT_EQ("path canonicalizes to the empty path", err); - - path = "./."; - EXPECT_FALSE(CanonicalizePath(&path, &err)); - EXPECT_EQ("path canonicalizes to the empty path", err); -} - TEST(CanonicalizePath, UpDir) { string path, err; path = "../../foo/bar.h"; -- cgit v0.12 From 48e647781da3986ffbd3b6974ef9f3ac23d4047c Mon Sep 17 00:00:00 2001 From: Nicolas Despres Date: Thu, 24 Dec 2015 16:47:33 +0100 Subject: Teach zsh to complete intermediary targets. Bash completion script uses "-t targets all" to list the target which is faster than "-t targets" and reports intermediary targets (see the manual entry for the 'targets' tool). See commit fc135c45. --- misc/zsh-completion | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/misc/zsh-completion b/misc/zsh-completion index ad7b87f..446e269 100644 --- a/misc/zsh-completion +++ b/misc/zsh-completion @@ -22,8 +22,8 @@ __get_targets() { then eval dir="${opt_args[-C]}" fi - targets_command="ninja -C \"${dir}\" -t targets" - eval ${targets_command} 2>/dev/null | sed "s/^\(.*\): .*$/\1/" + targets_command="ninja -C \"${dir}\" -t targets all" + eval ${targets_command} 2>/dev/null | cut -d: -f1 } __get_tools() { @@ -65,4 +65,3 @@ _arguments \ '-d+[Enable debugging (use -d list to list modes)]:modes:__modes' \ '-t+[Run a subtool (use -t list to list subtools)]:tools:__tools' \ '*::targets:__targets' - -- cgit v0.12 From 9c5aded8743ebac5c4f76a26e7fface04058bcda Mon Sep 17 00:00:00 2001 From: Tetsuo Kiso Date: Sun, 10 Jan 2016 05:23:35 -0800 Subject: Remove unnecessary `std::` --- src/includes_normalize_test.cc | 10 +++++----- src/ninja.cc | 4 ++-- src/subprocess-win32.cc | 2 +- src/subprocess_test.cc | 4 ++-- src/test.cc | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/includes_normalize_test.cc b/src/includes_normalize_test.cc index aba25d0..f18795c 100644 --- a/src/includes_normalize_test.cc +++ b/src/includes_normalize_test.cc @@ -30,15 +30,15 @@ string GetCurDir() { return parts[parts.size() - 1]; } -string NormalizeAndCheckNoError(const std::string& input) { +string NormalizeAndCheckNoError(const string& input) { string result, err; EXPECT_TRUE(IncludesNormalize::Normalize(input.c_str(), NULL, &result, &err)); EXPECT_EQ("", err); return result; } -string NormalizeRelativeAndCheckNoError(const std::string& input, - const std::string& relative_to) { +string NormalizeRelativeAndCheckNoError(const string& input, + const string& relative_to) { string result, err; EXPECT_TRUE(IncludesNormalize::Normalize(input.c_str(), relative_to.c_str(), &result, &err)); @@ -160,8 +160,8 @@ TEST(IncludesNormalize, LongInvalidPath) { "012345678\\" "012345678\\" "0123456789"; - std::string forward_slashes(kExactlyMaxPath); - std::replace(forward_slashes.begin(), forward_slashes.end(), '\\', '/'); + string forward_slashes(kExactlyMaxPath); + replace(forward_slashes.begin(), forward_slashes.end(), '\\', '/'); // Make sure a path that's exactly _MAX_PATH long is canonicalized. EXPECT_EQ(forward_slashes, NormalizeAndCheckNoError(kExactlyMaxPath)); diff --git a/src/ninja.cc b/src/ninja.cc index a73f83c..691afad 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -701,7 +701,7 @@ int NinjaMain::ToolUrtle(int argc, char** argv) { if ('0' <= *p && *p <= '9') { count = count*10 + *p - '0'; } else { - for (int i = 0; i < std::max(count, 1); ++i) + for (int i = 0; i < max(count, 1); ++i) printf("%c", *p); count = 0; } @@ -1163,7 +1163,7 @@ int main(int argc, char** argv) { #if defined(_MSC_VER) // Set a handler to catch crashes not caught by the __try..__except // block (e.g. an exception in a stack-unwind-block). - std::set_terminate(TerminateHandler); + set_terminate(TerminateHandler); __try { // Running inside __try ... __except suppresses any Windows error // dialogs for errors such as bad_alloc. diff --git a/src/subprocess-win32.cc b/src/subprocess-win32.cc index fad66e8..4bab719 100644 --- a/src/subprocess-win32.cc +++ b/src/subprocess-win32.cc @@ -255,7 +255,7 @@ bool SubprocessSet::DoWork() { if (subproc->Done()) { vector::iterator end = - std::remove(running_.begin(), running_.end(), subproc); + remove(running_.begin(), running_.end(), subproc); if (running_.end() != end) { finished_.push(subproc); running_.resize(end - running_.begin()); diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc index 066bbb7..2fe4bce 100644 --- a/src/subprocess_test.cc +++ b/src/subprocess_test.cc @@ -159,8 +159,8 @@ TEST_F(SubprocessTest, Console) { // Test that stdin, stdout and stderr are a terminal. // Also check that the current process is connected to a terminal. Subprocess* subproc = - subprocs_.Add(std::string("test -t 0 -a -t 1 -a -t 2 && ") + - std::string(kIsConnectedToTerminal), + subprocs_.Add(string("test -t 0 -a -t 1 -a -t 2 && ") + + string(kIsConnectedToTerminal), /*use_console=*/true); ASSERT_NE((Subprocess*)0, subproc); diff --git a/src/test.cc b/src/test.cc index aed8db7..d231b90 100644 --- a/src/test.cc +++ b/src/test.cc @@ -115,7 +115,7 @@ void VerifyGraph(const State& state) { for (vector::const_iterator in_node = (*e)->inputs_.begin(); in_node != (*e)->inputs_.end(); ++in_node) { const vector& out_edges = (*in_node)->out_edges(); - EXPECT_NE(std::find(out_edges.begin(), out_edges.end(), *e), + EXPECT_NE(find(out_edges.begin(), out_edges.end(), *e), out_edges.end()); } // Check that the edge's outputs have the edge as in-edge. -- cgit v0.12 From 4927698defddf4e5721028f6deb5609848d665d2 Mon Sep 17 00:00:00 2001 From: Tetsuo Kiso Date: Sun, 10 Jan 2016 05:35:45 -0800 Subject: Use container-based infrastructure for travis-ci. To start up faster. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 544db6f..216b8b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +sudo: false language: cpp compiler: - gcc -- cgit v0.12 From eb1a4fb48945aa509070f2704cbed30629af5bd1 Mon Sep 17 00:00:00 2001 From: sphawk Date: Mon, 25 Jan 2016 22:38:08 +0900 Subject: fix for localized version of cl.exe --- configure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.py b/configure.py index d60cbac..1c97db7 100755 --- a/configure.py +++ b/configure.py @@ -83,7 +83,7 @@ class Platform(object): stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = popen.communicate() - return b'/FS ' in out + return b'/FS' in out def is_windows(self): return self.is_mingw() or self.is_msvc() -- cgit v0.12 From 902f78227f1dba59c18ecb600dac849fdffde567 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 26 Jan 2016 19:44:27 -0800 Subject: Make dupbuild=err work in subninja --- src/manifest_parser.cc | 2 +- src/manifest_parser.h | 2 +- src/manifest_parser_test.cc | 104 +++++++++++++++++++++++++------------------- src/test.cc | 2 +- 4 files changed, 63 insertions(+), 47 deletions(-) diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc index e8c0436..3d11a2c 100644 --- a/src/manifest_parser.cc +++ b/src/manifest_parser.cc @@ -380,7 +380,7 @@ bool ManifestParser::ParseFileInclude(bool new_scope, string* err) { return false; string path = eval.Evaluate(env_); - ManifestParser subparser(state_, file_reader_); + ManifestParser subparser(state_, file_reader_, dupe_edge_should_err_); if (new_scope) { subparser.env_ = new BindingEnv(env_); } else { diff --git a/src/manifest_parser.h b/src/manifest_parser.h index f72cd6f..0883349 100644 --- a/src/manifest_parser.h +++ b/src/manifest_parser.h @@ -33,7 +33,7 @@ struct ManifestParser { }; ManifestParser(State* state, FileReader* file_reader, - bool dupe_edge_should_err = false); + bool dupe_edge_should_err); /// Load and parse a file. bool Load(const string& filename, string* err, Lexer* parent = NULL); diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc index 8f7b575..dc90599 100644 --- a/src/manifest_parser_test.cc +++ b/src/manifest_parser_test.cc @@ -24,7 +24,7 @@ struct ParserTest : public testing::Test, public ManifestParser::FileReader { void AssertParse(const char* input) { - ManifestParser parser(&state, this); + ManifestParser parser(&state, this, false); string err; EXPECT_TRUE(parser.ParseTest(input, &err)); ASSERT_EQ("", err); @@ -377,6 +377,22 @@ TEST_F(ParserTest, DuplicateEdgeWithMultipleOutputsError) { EXPECT_EQ("input:5: multiple rules generate out1 [-w dupbuild=err]\n", err); } +TEST_F(ParserTest, DuplicateEdgeInIncludedFile) { + files_["sub.ninja"] = + "rule cat\n" + " command = cat $in > $out\n" + "build out1 out2: cat in1\n" + "build out1: cat in2\n" + "build final: cat out1\n"; + const char kInput[] = + "subninja sub.ninja\n"; + ManifestParser parser(&state, this, /*dupe_edges_should_err=*/true); + string err; + EXPECT_FALSE(parser.ParseTest(kInput, &err)); + EXPECT_EQ("sub.ninja:5: multiple rules generate out1 [-w dupbuild=err]\n", + err); +} + TEST_F(ParserTest, ReservedWords) { ASSERT_NO_FATAL_FAILURE(AssertParse( "rule build\n" @@ -388,7 +404,7 @@ TEST_F(ParserTest, ReservedWords) { TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest(string("subn", 4), &err)); EXPECT_EQ("input:1: expected '=', got eof\n" @@ -399,7 +415,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("foobar", &err)); EXPECT_EQ("input:1: expected '=', got eof\n" @@ -410,7 +426,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("x 3", &err)); EXPECT_EQ("input:1: expected '=', got identifier\n" @@ -421,7 +437,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("x = 3", &err)); EXPECT_EQ("input:1: unexpected EOF\n" @@ -432,7 +448,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("x = 3\ny 2", &err)); EXPECT_EQ("input:2: expected '=', got identifier\n" @@ -443,7 +459,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("x = $", &err)); EXPECT_EQ("input:1: bad $-escape (literal $ must be written as $$)\n" @@ -454,7 +470,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("x = $\n $[\n", &err)); EXPECT_EQ("input:2: bad $-escape (literal $ must be written as $$)\n" @@ -465,7 +481,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("x = a$\n b$\n $\n", &err)); EXPECT_EQ("input:4: unexpected EOF\n" @@ -474,7 +490,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("build\n", &err)); EXPECT_EQ("input:1: expected path\n" @@ -485,7 +501,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("build x: y z\n", &err)); EXPECT_EQ("input:1: unknown build rule 'y'\n" @@ -496,7 +512,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("build x:: y z\n", &err)); EXPECT_EQ("input:1: expected build command name\n" @@ -507,7 +523,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n command = cat ok\n" "build x: cat $\n :\n", @@ -520,7 +536,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n", &err)); @@ -529,7 +545,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n" " command = echo\n" @@ -543,7 +559,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n" " command = echo\n" @@ -555,7 +571,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n" " command = ${fafsd\n" @@ -570,7 +586,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n" " command = cat\n" @@ -585,7 +601,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n" " command = cat\n" @@ -599,7 +615,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("rule %foo\n", &err)); @@ -608,7 +624,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("rule cc\n" " command = foo\n" @@ -622,7 +638,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n" "build $.: cc bar.cc\n", @@ -635,7 +651,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n && bar", &err)); @@ -644,7 +660,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n" "build $: cc bar.cc\n", @@ -657,7 +673,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("default\n", &err)); @@ -669,7 +685,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("default nonexistent\n", &err)); @@ -681,7 +697,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("rule r\n command = r\n" "build b: r\n" @@ -695,7 +711,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("default $a\n", &err)); EXPECT_EQ("input:1: empty path\n" @@ -706,7 +722,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("rule r\n" " command = r\n" @@ -718,7 +734,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; // the indented blank line must terminate the rule // this also verifies that "unexpected (token)" errors are correct @@ -731,7 +747,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("pool\n", &err)); EXPECT_EQ("input:1: expected pool name\n", err); @@ -739,7 +755,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("pool foo\n", &err)); EXPECT_EQ("input:2: expected 'depth =' line\n", err); @@ -747,7 +763,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("pool foo\n" " depth = 4\n" @@ -760,7 +776,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("pool foo\n" " depth = -1\n", &err)); @@ -772,7 +788,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("pool foo\n" " bar = 1\n", &err)); @@ -784,7 +800,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; // Pool names are dereferenced at edge parsing time. EXPECT_FALSE(parser.ParseTest("rule run\n" @@ -797,7 +813,7 @@ TEST_F(ParserTest, Errors) { TEST_F(ParserTest, MissingInput) { State state; - ManifestParser parser(&state, this); + ManifestParser parser(&state, this, false); string err; EXPECT_FALSE(parser.Load("build.ninja", &err)); EXPECT_EQ("loading 'build.ninja': No such file or directory", err); @@ -805,7 +821,7 @@ TEST_F(ParserTest, MissingInput) { TEST_F(ParserTest, MultipleOutputs) { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_TRUE(parser.ParseTest("rule cc\n command = foo\n depfile = bar\n" "build a.o b.o: cc c.cc\n", @@ -815,7 +831,7 @@ TEST_F(ParserTest, MultipleOutputs) { TEST_F(ParserTest, MultipleOutputsWithDeps) { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n deps = gcc\n" "build a.o b.o: cc c.cc\n", @@ -850,7 +866,7 @@ TEST_F(ParserTest, SubNinja) { } TEST_F(ParserTest, MissingSubNinja) { - ManifestParser parser(&state, this); + ManifestParser parser(&state, this, false); string err; EXPECT_FALSE(parser.ParseTest("subninja foo.ninja\n", &err)); EXPECT_EQ("input:1: loading 'foo.ninja': No such file or directory\n" @@ -863,7 +879,7 @@ TEST_F(ParserTest, DuplicateRuleInDifferentSubninjas) { // Test that rules are scoped to subninjas. files_["test.ninja"] = "rule cat\n" " command = cat\n"; - ManifestParser parser(&state, this); + ManifestParser parser(&state, this, false); string err; EXPECT_TRUE(parser.ParseTest("rule cat\n" " command = cat\n" @@ -876,7 +892,7 @@ TEST_F(ParserTest, DuplicateRuleInDifferentSubninjasWithInclude) { " command = cat\n"; files_["test.ninja"] = "include rules.ninja\n" "build x : cat\n"; - ManifestParser parser(&state, this); + ManifestParser parser(&state, this, false); string err; EXPECT_TRUE(parser.ParseTest("include rules.ninja\n" "subninja test.ninja\n" @@ -896,7 +912,7 @@ TEST_F(ParserTest, Include) { TEST_F(ParserTest, BrokenInclude) { files_["include.ninja"] = "build\n"; - ManifestParser parser(&state, this); + ManifestParser parser(&state, this, false); string err; EXPECT_FALSE(parser.ParseTest("include include.ninja\n", &err)); EXPECT_EQ("include.ninja:1: expected path\n" @@ -976,7 +992,7 @@ TEST_F(ParserTest, UTF8) { TEST_F(ParserTest, CRLF) { State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, false); string err; EXPECT_TRUE(parser.ParseTest("# comment with crlf\r\n", &err)); diff --git a/src/test.cc b/src/test.cc index d231b90..6548848 100644 --- a/src/test.cc +++ b/src/test.cc @@ -95,7 +95,7 @@ Node* StateTestWithBuiltinRules::GetNode(const string& path) { } void AssertParse(State* state, const char* input) { - ManifestParser parser(state, NULL); + ManifestParser parser(state, NULL, false); string err; EXPECT_TRUE(parser.ParseTest(input, &err)); ASSERT_EQ("", err); -- cgit v0.12 From 56bab441b70e6f09f3faf7936118c309f9ce48cb Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 27 Jan 2016 11:10:34 -0800 Subject: dupe_edge_should_err from bool to enum --- src/manifest_parser.cc | 8 ++-- src/manifest_parser.h | 9 ++++- src/manifest_parser_test.cc | 92 ++++++++++++++++++++++----------------------- src/ninja.cc | 4 +- src/test.cc | 2 +- 5 files changed, 61 insertions(+), 54 deletions(-) diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc index 3d11a2c..d0fac59 100644 --- a/src/manifest_parser.cc +++ b/src/manifest_parser.cc @@ -25,9 +25,9 @@ #include "version.h" ManifestParser::ManifestParser(State* state, FileReader* file_reader, - bool dupe_edge_should_err) + DupeEdgeAction dupe_edge_action) : state_(state), file_reader_(file_reader), - dupe_edge_should_err_(dupe_edge_should_err), quiet_(false) { + dupe_edge_action_(dupe_edge_action), quiet_(false) { env_ = &state->bindings_; } @@ -331,7 +331,7 @@ bool ManifestParser::ParseEdge(string* err) { if (!CanonicalizePath(&path, &slash_bits, &path_err)) return lexer_.Error(path_err, err); if (!state_->AddOut(edge, path, slash_bits)) { - if (dupe_edge_should_err_) { + if (dupe_edge_action_ == kDupeEdgeActionError) { lexer_.Error("multiple rules generate " + path + " [-w dupbuild=err]", err); return false; @@ -380,7 +380,7 @@ bool ManifestParser::ParseFileInclude(bool new_scope, string* err) { return false; string path = eval.Evaluate(env_); - ManifestParser subparser(state_, file_reader_, dupe_edge_should_err_); + ManifestParser subparser(state_, file_reader_, dupe_edge_action_); if (new_scope) { subparser.env_ = new BindingEnv(env_); } else { diff --git a/src/manifest_parser.h b/src/manifest_parser.h index 0883349..41d388c 100644 --- a/src/manifest_parser.h +++ b/src/manifest_parser.h @@ -25,6 +25,11 @@ struct BindingEnv; struct EvalString; struct State; +enum DupeEdgeAction { + kDupeEdgeActionWarn, + kDupeEdgeActionError, +}; + /// Parses .ninja files. struct ManifestParser { struct FileReader { @@ -33,7 +38,7 @@ struct ManifestParser { }; ManifestParser(State* state, FileReader* file_reader, - bool dupe_edge_should_err); + DupeEdgeAction dupe_edge_action); /// Load and parse a file. bool Load(const string& filename, string* err, Lexer* parent = NULL); @@ -66,7 +71,7 @@ private: BindingEnv* env_; FileReader* file_reader_; Lexer lexer_; - bool dupe_edge_should_err_; + DupeEdgeAction dupe_edge_action_; bool quiet_; }; diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc index dc90599..a18433a 100644 --- a/src/manifest_parser_test.cc +++ b/src/manifest_parser_test.cc @@ -24,7 +24,7 @@ struct ParserTest : public testing::Test, public ManifestParser::FileReader { void AssertParse(const char* input) { - ManifestParser parser(&state, this, false); + ManifestParser parser(&state, this, kDupeEdgeActionWarn); string err; EXPECT_TRUE(parser.ParseTest(input, &err)); ASSERT_EQ("", err); @@ -371,7 +371,7 @@ TEST_F(ParserTest, DuplicateEdgeWithMultipleOutputsError) { "build out1 out2: cat in1\n" "build out1: cat in2\n" "build final: cat out1\n"; - ManifestParser parser(&state, this, /*dupe_edges_should_err=*/true); + ManifestParser parser(&state, this, kDupeEdgeActionError); string err; EXPECT_FALSE(parser.ParseTest(kInput, &err)); EXPECT_EQ("input:5: multiple rules generate out1 [-w dupbuild=err]\n", err); @@ -386,7 +386,7 @@ TEST_F(ParserTest, DuplicateEdgeInIncludedFile) { "build final: cat out1\n"; const char kInput[] = "subninja sub.ninja\n"; - ManifestParser parser(&state, this, /*dupe_edges_should_err=*/true); + ManifestParser parser(&state, this, kDupeEdgeActionError); string err; EXPECT_FALSE(parser.ParseTest(kInput, &err)); EXPECT_EQ("sub.ninja:5: multiple rules generate out1 [-w dupbuild=err]\n", @@ -404,7 +404,7 @@ TEST_F(ParserTest, ReservedWords) { TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest(string("subn", 4), &err)); EXPECT_EQ("input:1: expected '=', got eof\n" @@ -415,7 +415,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("foobar", &err)); EXPECT_EQ("input:1: expected '=', got eof\n" @@ -426,7 +426,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("x 3", &err)); EXPECT_EQ("input:1: expected '=', got identifier\n" @@ -437,7 +437,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("x = 3", &err)); EXPECT_EQ("input:1: unexpected EOF\n" @@ -448,7 +448,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("x = 3\ny 2", &err)); EXPECT_EQ("input:2: expected '=', got identifier\n" @@ -459,7 +459,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("x = $", &err)); EXPECT_EQ("input:1: bad $-escape (literal $ must be written as $$)\n" @@ -470,7 +470,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("x = $\n $[\n", &err)); EXPECT_EQ("input:2: bad $-escape (literal $ must be written as $$)\n" @@ -481,7 +481,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("x = a$\n b$\n $\n", &err)); EXPECT_EQ("input:4: unexpected EOF\n" @@ -490,7 +490,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("build\n", &err)); EXPECT_EQ("input:1: expected path\n" @@ -501,7 +501,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("build x: y z\n", &err)); EXPECT_EQ("input:1: unknown build rule 'y'\n" @@ -512,7 +512,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("build x:: y z\n", &err)); EXPECT_EQ("input:1: expected build command name\n" @@ -523,7 +523,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n command = cat ok\n" "build x: cat $\n :\n", @@ -536,7 +536,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n", &err)); @@ -545,7 +545,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n" " command = echo\n" @@ -559,7 +559,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n" " command = echo\n" @@ -571,7 +571,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n" " command = ${fafsd\n" @@ -586,7 +586,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n" " command = cat\n" @@ -601,7 +601,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n" " command = cat\n" @@ -615,7 +615,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule %foo\n", &err)); @@ -624,7 +624,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cc\n" " command = foo\n" @@ -638,7 +638,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n" "build $.: cc bar.cc\n", @@ -651,7 +651,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n && bar", &err)); @@ -660,7 +660,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n" "build $: cc bar.cc\n", @@ -673,7 +673,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("default\n", &err)); @@ -685,7 +685,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("default nonexistent\n", &err)); @@ -697,7 +697,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule r\n command = r\n" "build b: r\n" @@ -711,7 +711,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("default $a\n", &err)); EXPECT_EQ("input:1: empty path\n" @@ -722,7 +722,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule r\n" " command = r\n" @@ -734,7 +734,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; // the indented blank line must terminate the rule // this also verifies that "unexpected (token)" errors are correct @@ -747,7 +747,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("pool\n", &err)); EXPECT_EQ("input:1: expected pool name\n", err); @@ -755,7 +755,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("pool foo\n", &err)); EXPECT_EQ("input:2: expected 'depth =' line\n", err); @@ -763,7 +763,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("pool foo\n" " depth = 4\n" @@ -776,7 +776,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("pool foo\n" " depth = -1\n", &err)); @@ -788,7 +788,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("pool foo\n" " bar = 1\n", &err)); @@ -800,7 +800,7 @@ TEST_F(ParserTest, Errors) { { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; // Pool names are dereferenced at edge parsing time. EXPECT_FALSE(parser.ParseTest("rule run\n" @@ -813,7 +813,7 @@ TEST_F(ParserTest, Errors) { TEST_F(ParserTest, MissingInput) { State state; - ManifestParser parser(&state, this, false); + ManifestParser parser(&state, this, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.Load("build.ninja", &err)); EXPECT_EQ("loading 'build.ninja': No such file or directory", err); @@ -821,7 +821,7 @@ TEST_F(ParserTest, MissingInput) { TEST_F(ParserTest, MultipleOutputs) { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_TRUE(parser.ParseTest("rule cc\n command = foo\n depfile = bar\n" "build a.o b.o: cc c.cc\n", @@ -831,7 +831,7 @@ TEST_F(ParserTest, MultipleOutputs) { TEST_F(ParserTest, MultipleOutputsWithDeps) { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n deps = gcc\n" "build a.o b.o: cc c.cc\n", @@ -866,7 +866,7 @@ TEST_F(ParserTest, SubNinja) { } TEST_F(ParserTest, MissingSubNinja) { - ManifestParser parser(&state, this, false); + ManifestParser parser(&state, this, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("subninja foo.ninja\n", &err)); EXPECT_EQ("input:1: loading 'foo.ninja': No such file or directory\n" @@ -879,7 +879,7 @@ TEST_F(ParserTest, DuplicateRuleInDifferentSubninjas) { // Test that rules are scoped to subninjas. files_["test.ninja"] = "rule cat\n" " command = cat\n"; - ManifestParser parser(&state, this, false); + ManifestParser parser(&state, this, kDupeEdgeActionWarn); string err; EXPECT_TRUE(parser.ParseTest("rule cat\n" " command = cat\n" @@ -892,7 +892,7 @@ TEST_F(ParserTest, DuplicateRuleInDifferentSubninjasWithInclude) { " command = cat\n"; files_["test.ninja"] = "include rules.ninja\n" "build x : cat\n"; - ManifestParser parser(&state, this, false); + ManifestParser parser(&state, this, kDupeEdgeActionWarn); string err; EXPECT_TRUE(parser.ParseTest("include rules.ninja\n" "subninja test.ninja\n" @@ -912,7 +912,7 @@ TEST_F(ParserTest, Include) { TEST_F(ParserTest, BrokenInclude) { files_["include.ninja"] = "build\n"; - ManifestParser parser(&state, this, false); + ManifestParser parser(&state, this, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("include include.ninja\n", &err)); EXPECT_EQ("include.ninja:1: expected path\n" @@ -992,7 +992,7 @@ TEST_F(ParserTest, UTF8) { TEST_F(ParserTest, CRLF) { State state; - ManifestParser parser(&state, NULL, false); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; EXPECT_TRUE(parser.ParseTest("# comment with crlf\r\n", &err)); diff --git a/src/ninja.cc b/src/ninja.cc index 691afad..a15d423 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -1114,7 +1114,9 @@ int real_main(int argc, char** argv) { RealFileReader file_reader; ManifestParser parser(&ninja.state_, &file_reader, - options.dupe_edges_should_err); + options.dupe_edges_should_err + ? kDupeEdgeActionError + : kDupeEdgeActionWarn); string err; if (!parser.Load(options.input_file, &err)) { Error("%s", err.c_str()); diff --git a/src/test.cc b/src/test.cc index 6548848..841ce04 100644 --- a/src/test.cc +++ b/src/test.cc @@ -95,7 +95,7 @@ Node* StateTestWithBuiltinRules::GetNode(const string& path) { } void AssertParse(State* state, const char* input) { - ManifestParser parser(state, NULL, false); + ManifestParser parser(state, NULL, kDupeEdgeActionWarn); string err; EXPECT_TRUE(parser.ParseTest(input, &err)); ASSERT_EQ("", err); -- cgit v0.12 From 24750de7d7ec1a74830410ffdb53ca1f22efdc26 Mon Sep 17 00:00:00 2001 From: Shinichiro Hamaji Date: Thu, 28 Jan 2016 18:16:07 +0900 Subject: Add -d keepdepfile to preserve depfiles This is useful when you are developing a tool which generates GCC-style depfiles. --- src/build.cc | 8 +++++--- src/debug_flags.cc | 2 ++ src/debug_flags.h | 2 ++ src/ninja.cc | 15 ++++++++++----- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/build.cc b/src/build.cc index 0e9a399..ab2460a 100644 --- a/src/build.cc +++ b/src/build.cc @@ -892,9 +892,11 @@ bool Builder::ExtractDeps(CommandRunner::Result* result, deps_nodes->push_back(state_->GetNode(*i, slash_bits)); } - if (disk_interface_->RemoveFile(depfile) < 0) { - *err = string("deleting depfile: ") + strerror(errno) + string("\n"); - return false; + if (!g_keep_depfile) { + if (disk_interface_->RemoveFile(depfile) < 0) { + *err = string("deleting depfile: ") + strerror(errno) + string("\n"); + return false; + } } } else { Fatal("unknown deps type '%s'", deps_type.c_str()); diff --git a/src/debug_flags.cc b/src/debug_flags.cc index 8065001..44b14c4 100644 --- a/src/debug_flags.cc +++ b/src/debug_flags.cc @@ -14,6 +14,8 @@ bool g_explaining = false; +bool g_keep_depfile = false; + bool g_keep_rsp = false; bool g_experimental_statcache = true; diff --git a/src/debug_flags.h b/src/debug_flags.h index 7965585..e08a43b 100644 --- a/src/debug_flags.h +++ b/src/debug_flags.h @@ -24,6 +24,8 @@ extern bool g_explaining; +extern bool g_keep_depfile; + extern bool g_keep_rsp; extern bool g_experimental_statcache; diff --git a/src/ninja.cc b/src/ninja.cc index 691afad..bb76f5d 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -774,9 +774,10 @@ const Tool* ChooseTool(const string& tool_name) { bool DebugEnable(const string& name) { if (name == "list") { printf("debugging modes:\n" -" stats print operation counts/timing info\n" -" explain explain what caused a command to execute\n" -" keeprsp don't delete @response files on success\n" +" stats print operation counts/timing info\n" +" explain explain what caused a command to execute\n" +" keepdepfile don't delete depfiles after they're read by ninja\n" +" keeprsp don't delete @response files on success\n" #ifdef _WIN32 " nostatcache don't batch stat() calls per directory and cache them\n" #endif @@ -788,6 +789,9 @@ bool DebugEnable(const string& name) { } else if (name == "explain") { g_explaining = true; return true; + } else if (name == "keepdepfile") { + g_keep_depfile = true; + return true; } else if (name == "keeprsp") { g_keep_rsp = true; return true; @@ -796,8 +800,9 @@ bool DebugEnable(const string& name) { return true; } else { const char* suggestion = - SpellcheckString(name.c_str(), "stats", "explain", "keeprsp", - "nostatcache", NULL); + SpellcheckString(name.c_str(), + "stats", "explain", "keepdepfile", "keeprsp", + "nostatcache", NULL); if (suggestion) { Error("unknown debug setting '%s', did you mean '%s'?", name.c_str(), suggestion); -- cgit v0.12 From 94086e990cac21451a98d949afec0b705b1c4d88 Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 2 Feb 2016 09:34:53 -0500 Subject: perftest: fix compilation after dupe_edge_should_err change Fix some ManifestParser constructor calls missed by commit 56bab441b7 (dupe_edge_should_err from bool to enum, 2016-01-27). --- src/build_log_perftest.cc | 2 +- src/manifest_parser_perftest.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/build_log_perftest.cc b/src/build_log_perftest.cc index 810c065..185c512 100644 --- a/src/build_log_perftest.cc +++ b/src/build_log_perftest.cc @@ -71,7 +71,7 @@ bool WriteTestData(string* err) { long_rule_command += "$in -o $out\n"; State state; - ManifestParser parser(&state, NULL); + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); if (!parser.ParseTest("rule cxx\n command = " + long_rule_command, err)) return false; diff --git a/src/manifest_parser_perftest.cc b/src/manifest_parser_perftest.cc index 6b56ab0..ef3b663 100644 --- a/src/manifest_parser_perftest.cc +++ b/src/manifest_parser_perftest.cc @@ -61,7 +61,7 @@ int LoadManifests(bool measure_command_evaluation) { string err; RealFileReader file_reader; State state; - ManifestParser parser(&state, &file_reader); + ManifestParser parser(&state, &file_reader, kDupeEdgeActionWarn); if (!parser.Load("build.ninja", &err)) { fprintf(stderr, "Failed to read test data: %s\n", err.c_str()); exit(1); -- cgit v0.12 From cc39240a10fb040fca80bf3669245f2f2d5736c5 Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 13 Jul 2015 15:25:50 -0400 Subject: Add support for build statement implicit outputs Some build rules produce outputs that are not mentioned on the command line but that should be part of the build graph. Such outputs should not be named in the `$out` variable. Extend the build statement syntax to support specification of implicit outputs using the syntax `| out1 out2` after the explicit outputs and before the `:`. For example, compilation of a Fortran source file `foo.f90` that defines `MODULE FOO` may now be specified as: rule fc command = f95 -c $in -o $out build foo.o | foo.mod: fc foo.f90 The `foo.mod` file is an implicit output generated by the compiler based on the content of the source file and not mentioned on the command line. --- doc/manual.asciidoc | 27 ++++++++++++++++++++++++++- src/build_test.cc | 16 ++++++++++++++++ src/graph.cc | 3 ++- src/graph.h | 12 +++++++++++- src/graph_test.cc | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/manifest_parser.cc | 15 +++++++++++++++ src/manifest_parser_test.cc | 34 ++++++++++++++++++++++++++++++++++ 7 files changed, 148 insertions(+), 3 deletions(-) diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc index ab5c945..4e73df3 100644 --- a/doc/manual.asciidoc +++ b/doc/manual.asciidoc @@ -689,6 +689,10 @@ A file is a series of declarations. A declaration can be one of: Order-only dependencies may be tacked on the end with +|| _dependency1_ _dependency2_+. (See <>.) ++ +Implicit outputs _(available since Ninja 1.7)_ may be added before +the `:` with +| _output1_ _output2_+ and do not appear in `$out`. +(See <>.) 3. Variable declarations, which look like +_variable_ = _value_+. @@ -872,6 +876,27 @@ interpretation of the command (such as the use of `&&` to chain multiple commands), make the command execute the Windows shell by prefixing the command with `cmd /c`. +[[ref_outputs]] +Build outputs +~~~~~~~~~~~~~ + +There are two types of build outputs which are subtly different. + +1. _Explicit outputs_, as listed in a build line. These are + available as the `$out` variable in the rule. ++ +This is the standard form of output to be used for e.g. the +object file of a compile command. + +2. _Implicit outputs_, as listed in a build line with the syntax +| + _out1_ _out2_+ + before the `:` of a build line _(available since + Ninja 1.7)_. The semantics are identical to explicit outputs, + the only difference is that implicit outputs don't show up in the + `$out` variable. ++ +This is for expressing outputs that don't show up on the +command line of the command. + [[ref_dependencies]] Build dependencies ~~~~~~~~~~~~~~~~~~ @@ -883,7 +908,7 @@ There are three types of build dependencies which are subtly different. cause the output to be rebuilt; if these file are missing and Ninja doesn't know how to build them, the build is aborted. + -This is the standard form of dependency to be used for e.g. the +This is the standard form of dependency to be used e.g. for the source file of a compile command. 2. _Implicit dependencies_, either as picked up from diff --git a/src/build_test.cc b/src/build_test.cc index 20fb664..7c6060d 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -717,6 +717,22 @@ TEST_F(BuildTest, TwoOutputs) { EXPECT_EQ("touch out1 out2", command_runner_.commands_ran_[0]); } +TEST_F(BuildTest, ImplicitOutput) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule touch\n" +" command = touch $out $out.imp\n" +"build out | out.imp: touch in.txt\n")); + fs_.Create("in.txt", ""); + + string err; + EXPECT_TRUE(builder_.AddTarget("out.imp", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + EXPECT_EQ("", err); + ASSERT_EQ(1u, command_runner_.commands_ran_.size()); + EXPECT_EQ("touch out out.imp", command_runner_.commands_ran_[0]); +} + // Test case from // https://github.com/ninja-build/ninja/issues/148 TEST_F(BuildTest, MultiOutIn) { diff --git a/src/graph.cc b/src/graph.cc index 9e65675..3391305 100644 --- a/src/graph.cc +++ b/src/graph.cc @@ -241,8 +241,9 @@ string EdgeEnv::LookupVariable(const string& var) { edge_->inputs_.begin() + explicit_deps_count, var == "in" ? ' ' : '\n'); } else if (var == "out") { + int explicit_outs_count = edge_->outputs_.size() - edge_->implicit_outs_; return MakePathList(edge_->outputs_.begin(), - edge_->outputs_.end(), + edge_->outputs_.begin() + explicit_outs_count, ' '); } diff --git a/src/graph.h b/src/graph.h index cf15123..add8d3d 100644 --- a/src/graph.h +++ b/src/graph.h @@ -129,7 +129,7 @@ private: struct Edge { Edge() : rule_(NULL), pool_(NULL), env_(NULL), outputs_ready_(false), deps_missing_(false), - implicit_deps_(0), order_only_deps_(0) {} + implicit_deps_(0), order_only_deps_(0), implicit_outs_(0) {} /// Return true if all inputs' in-edges are ready. bool AllInputsReady() const; @@ -181,6 +181,16 @@ struct Edge { return index >= inputs_.size() - order_only_deps_; } + // There are two types of outputs. + // 1) explicit outs, which show up as $out on the command line; + // 2) implicit outs, which the target generates but are not part of $out. + // These are stored in outputs_ in that order, and we keep a count of + // #2 to use when we need to access the various subsets. + int implicit_outs_; + bool is_implicit_out(size_t index) const { + return index >= outputs_.size() - implicit_outs_; + } + bool is_phony() const; bool use_console() const; }; diff --git a/src/graph_test.cc b/src/graph_test.cc index 44be8a5..723e8ea 100644 --- a/src/graph_test.cc +++ b/src/graph_test.cc @@ -105,6 +105,50 @@ TEST_F(GraphTest, ExplicitImplicit) { EXPECT_TRUE(GetNode("out.o")->dirty()); } +TEST_F(GraphTest, ImplicitOutputParse) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out | out.imp: cat in\n")); + + Edge* edge = GetNode("out")->in_edge(); + EXPECT_EQ(2, edge->outputs_.size()); + EXPECT_EQ("out", edge->outputs_[0]->path()); + EXPECT_EQ("out.imp", edge->outputs_[1]->path()); + EXPECT_EQ(1, edge->implicit_outs_); + EXPECT_EQ(edge, GetNode("out.imp")->in_edge()); +} + +TEST_F(GraphTest, ImplicitOutputMissing) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out | out.imp: cat in\n")); + fs_.Create("in", ""); + fs_.Create("out", ""); + + Edge* edge = GetNode("out")->in_edge(); + string err; + EXPECT_TRUE(scan_.RecomputeDirty(edge, &err)); + ASSERT_EQ("", err); + + EXPECT_TRUE(GetNode("out")->dirty()); + EXPECT_TRUE(GetNode("out.imp")->dirty()); +} + +TEST_F(GraphTest, ImplicitOutputOutOfDate) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"build out | out.imp: cat in\n")); + fs_.Create("out.imp", ""); + fs_.Tick(); + fs_.Create("in", ""); + fs_.Create("out", ""); + + Edge* edge = GetNode("out")->in_edge(); + string err; + EXPECT_TRUE(scan_.RecomputeDirty(edge, &err)); + ASSERT_EQ("", err); + + EXPECT_TRUE(GetNode("out")->dirty()); + EXPECT_TRUE(GetNode("out.imp")->dirty()); +} + TEST_F(GraphTest, PathWithCurrentDirectory) { ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, "rule catdep\n" diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc index d0fac59..0724e14 100644 --- a/src/manifest_parser.cc +++ b/src/manifest_parser.cc @@ -247,6 +247,20 @@ bool ManifestParser::ParseEdge(string* err) { } while (!out.empty()); } + // Add all implicit outs, counting how many as we go. + int implicit_outs = 0; + if (lexer_.PeekToken(Lexer::PIPE)) { + for (;;) { + EvalString out; + if (!lexer_.ReadPath(&out, err)) + return err; + if (out.empty()) + break; + outs.push_back(out); + ++implicit_outs; + } + } + if (!ExpectToken(Lexer::COLON, err)) return false; @@ -350,6 +364,7 @@ bool ManifestParser::ParseEdge(string* err) { delete edge; return true; } + edge->implicit_outs_ = implicit_outs; edge->inputs_.reserve(ins.size()); for (vector::iterator i = ins.begin(); i != ins.end(); ++i) { diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc index a18433a..2a7900b 100644 --- a/src/manifest_parser_test.cc +++ b/src/manifest_parser_test.cc @@ -940,6 +940,40 @@ TEST_F(ParserTest, OrderOnly) { ASSERT_TRUE(edge->is_order_only(1)); } +TEST_F(ParserTest, ImplicitOutput) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build foo | imp: cat bar\n")); + + Edge* edge = state.LookupNode("imp")->in_edge(); + ASSERT_EQ(edge->outputs_.size(), 2); + EXPECT_TRUE(edge->is_implicit_out(1)); +} + +TEST_F(ParserTest, ImplicitOutputEmpty) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build foo | : cat bar\n")); + + Edge* edge = state.LookupNode("foo")->in_edge(); + ASSERT_EQ(edge->outputs_.size(), 1); + EXPECT_FALSE(edge->is_implicit_out(0)); +} + +TEST_F(ParserTest, NoExplicitOutput) { + ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + string err; + EXPECT_FALSE(parser.ParseTest( +"rule cat\n" +" command = cat $in > $out\n" +"build | imp : cat bar\n", &err)); + ASSERT_EQ("input:3: expected path\n" + "build | imp : cat bar\n" + " ^ near here", err); +} + TEST_F(ParserTest, DefaultDefault) { ASSERT_NO_FATAL_FAILURE(AssertParse( "rule cat\n command = cat $in > $out\n" -- cgit v0.12 From f9487ac7971a668ddf142d5adc741f789c7bbb68 Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 3 Feb 2016 13:18:37 -0500 Subject: Factor a `FileReader` base class out of `DiskInterface` Some clients will need only the ability to read files, so provide this as a more narrow interface than the full disk interface. --- src/disk_interface.h | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/disk_interface.h b/src/disk_interface.h index b61d192..94f25dc 100644 --- a/src/disk_interface.h +++ b/src/disk_interface.h @@ -21,13 +21,20 @@ using namespace std; #include "timestamp.h" +/// Interface for reading files from disk. See DiskInterface for details. +/// This base offers the minimum interface needed just to read files. +struct FileReader { + virtual ~FileReader() {} + + /// Read a file to a string. Fill in |err| on error. + virtual string ReadFile(const string& path, string* err) = 0; +}; + /// Interface for accessing the disk. /// /// Abstract so it can be mocked out for tests. The real implementation /// is RealDiskInterface. -struct DiskInterface { - virtual ~DiskInterface() {} - +struct DiskInterface: public FileReader { /// stat() a file, returning the mtime, or 0 if missing and -1 on /// other errors. virtual TimeStamp Stat(const string& path, string* err) const = 0; @@ -39,9 +46,6 @@ struct DiskInterface { /// Returns true on success, false on failure virtual bool WriteFile(const string& path, const string& contents) = 0; - /// Read a file to a string. Fill in |err| on error. - virtual string ReadFile(const string& path, string* err) = 0; - /// Remove the file named @a path. It behaves like 'rm -f path' so no errors /// are reported if it does not exists. /// @returns 0 if the file has been removed, -- cgit v0.12 From 858386d8415d2ee932fe3c01ebfbe5e0737f94a3 Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 3 Feb 2016 13:20:00 -0500 Subject: Expose more details in FileReader::ReadFile signature Return a status so callers can distinguish a missing file from an empty file. This allows our VirtualFileSystem test infrastructure to report as missing any file for which it has no entry. --- src/build.cc | 12 ++++++++++-- src/disk_interface.cc | 14 +++++++------- src/disk_interface.h | 15 ++++++++++++--- src/disk_interface_test.cc | 16 +++++++++++----- src/graph.cc | 11 +++++++++-- src/test.cc | 13 +++++++++---- src/test.h | 2 +- 7 files changed, 59 insertions(+), 24 deletions(-) diff --git a/src/build.cc b/src/build.cc index ab2460a..45d3f32 100644 --- a/src/build.cc +++ b/src/build.cc @@ -871,9 +871,17 @@ bool Builder::ExtractDeps(CommandRunner::Result* result, return false; } - string content = disk_interface_->ReadFile(depfile, err); - if (!err->empty()) + // Read depfile content. Treat a missing depfile as empty. + string content; + switch (disk_interface_->ReadFile(depfile, &content, err)) { + case DiskInterface::Okay: + break; + case DiskInterface::NotFound: + err->clear(); + break; + case DiskInterface::OtherError: return false; + } if (content.empty()) return true; diff --git a/src/disk_interface.cc b/src/disk_interface.cc index 70282c0..451a9b4 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -229,14 +229,14 @@ bool RealDiskInterface::MakeDir(const string& path) { return true; } -string RealDiskInterface::ReadFile(const string& path, string* err) { - string contents; - int ret = ::ReadFile(path, &contents, err); - if (ret == -ENOENT) { - // Swallow ENOENT. - err->clear(); +FileReader::Status RealDiskInterface::ReadFile(const string& path, + string* contents, + string* err) { + switch (::ReadFile(path, contents, err)) { + case 0: return Okay; + case -ENOENT: return NotFound; + default: return OtherError; } - return contents; } int RealDiskInterface::RemoveFile(const string& path) { diff --git a/src/disk_interface.h b/src/disk_interface.h index 94f25dc..145e089 100644 --- a/src/disk_interface.h +++ b/src/disk_interface.h @@ -26,8 +26,17 @@ using namespace std; struct FileReader { virtual ~FileReader() {} - /// Read a file to a string. Fill in |err| on error. - virtual string ReadFile(const string& path, string* err) = 0; + /// Result of ReadFile. + enum Status { + Okay, + NotFound, + OtherError + }; + + /// Read and store in given string. On success, return Okay. + /// On error, return another Status and fill |err|. + virtual Status ReadFile(const string& path, string* contents, + string* err) = 0; }; /// Interface for accessing the disk. @@ -69,7 +78,7 @@ struct RealDiskInterface : public DiskInterface { virtual TimeStamp Stat(const string& path, string* err) const; virtual bool MakeDir(const string& path); virtual bool WriteFile(const string& path, const string& contents); - virtual string ReadFile(const string& path, string* err); + virtual Status ReadFile(const string& path, string* contents, string* err); virtual int RemoveFile(const string& path); /// Whether stat information can be cached. Only has an effect on Windows. diff --git a/src/disk_interface_test.cc b/src/disk_interface_test.cc index 9d210b4..7187bdf 100644 --- a/src/disk_interface_test.cc +++ b/src/disk_interface_test.cc @@ -157,8 +157,12 @@ TEST_F(DiskInterfaceTest, StatCache) { TEST_F(DiskInterfaceTest, ReadFile) { string err; - EXPECT_EQ("", disk_.ReadFile("foobar", &err)); - EXPECT_EQ("", err); + std::string content; + ASSERT_EQ(DiskInterface::NotFound, + disk_.ReadFile("foobar", &content, &err)); + EXPECT_EQ("", content); + EXPECT_NE("", err); // actual value is platform-specific + err.clear(); const char* kTestFile = "testfile"; FILE* f = fopen(kTestFile, "wb"); @@ -167,7 +171,9 @@ TEST_F(DiskInterfaceTest, ReadFile) { fprintf(f, "%s", kTestContent); ASSERT_EQ(0, fclose(f)); - EXPECT_EQ(kTestContent, disk_.ReadFile(kTestFile, &err)); + ASSERT_EQ(DiskInterface::Okay, + disk_.ReadFile(kTestFile, &content, &err)); + EXPECT_EQ(kTestContent, content); EXPECT_EQ("", err); } @@ -208,9 +214,9 @@ struct StatTest : public StateTestWithBuiltinRules, assert(false); return false; } - virtual string ReadFile(const string& path, string* err) { + virtual Status ReadFile(const string& path, string* contents, string* err) { assert(false); - return ""; + return NotFound; } virtual int RemoveFile(const string& path) { assert(false); diff --git a/src/graph.cc b/src/graph.cc index 9e65675..98f1461 100644 --- a/src/graph.cc +++ b/src/graph.cc @@ -395,8 +395,15 @@ bool ImplicitDepLoader::LoadDeps(Edge* edge, string* err) { bool ImplicitDepLoader::LoadDepFile(Edge* edge, const string& path, string* err) { METRIC_RECORD("depfile load"); - string content = disk_interface_->ReadFile(path, err); - if (!err->empty()) { + // Read depfile content. Treat a missing depfile as empty. + string content; + switch (disk_interface_->ReadFile(path, &content, err)) { + case DiskInterface::Okay: + break; + case DiskInterface::NotFound: + err->clear(); + break; + case DiskInterface::OtherError: *err = "loading '" + path + "': " + *err; return false; } diff --git a/src/test.cc b/src/test.cc index 841ce04..53bfc48 100644 --- a/src/test.cc +++ b/src/test.cc @@ -164,12 +164,17 @@ bool VirtualFileSystem::MakeDir(const string& path) { return true; // success } -string VirtualFileSystem::ReadFile(const string& path, string* err) { +FileReader::Status VirtualFileSystem::ReadFile(const string& path, + string* contents, + string* err) { files_read_.push_back(path); FileMap::iterator i = files_.find(path); - if (i != files_.end()) - return i->second.contents; - return ""; + if (i != files_.end()) { + *contents = i->second.contents; + return Okay; + } + *err = strerror(ENOENT); + return NotFound; } int VirtualFileSystem::RemoveFile(const string& path) { diff --git a/src/test.h b/src/test.h index 156e68a..488c243 100644 --- a/src/test.h +++ b/src/test.h @@ -145,7 +145,7 @@ struct VirtualFileSystem : public DiskInterface { virtual TimeStamp Stat(const string& path, string* err) const; virtual bool WriteFile(const string& path, const string& contents); virtual bool MakeDir(const string& path); - virtual string ReadFile(const string& path, string* err); + virtual Status ReadFile(const string& path, string* contents, string* err); virtual int RemoveFile(const string& path); /// An entry for a single in-memory file. -- cgit v0.12 From e74cefa6c606d69ddfd6bc9b055ef0c697c4f29a Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 3 Feb 2016 13:44:46 -0500 Subject: Replace ManifestParser::FileReader with general FileReader Avoid having two separate filesystem interfaces. Simplify test infrastructure by avoiding custom `ManifestParser::FileReader` implementations. --- src/manifest_parser.cc | 3 +- src/manifest_parser.h | 6 +--- src/manifest_parser_perftest.cc | 10 ++----- src/manifest_parser_test.cc | 65 +++++++++++++++++------------------------ src/ninja.cc | 11 +------ 5 files changed, 32 insertions(+), 63 deletions(-) diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc index d0fac59..8bdb330 100644 --- a/src/manifest_parser.cc +++ b/src/manifest_parser.cc @@ -18,6 +18,7 @@ #include #include +#include "disk_interface.h" #include "graph.h" #include "metrics.h" #include "state.h" @@ -35,7 +36,7 @@ bool ManifestParser::Load(const string& filename, string* err, Lexer* parent) { METRIC_RECORD(".ninja parse"); string contents; string read_err; - if (!file_reader_->ReadFile(filename, &contents, &read_err)) { + if (file_reader_->ReadFile(filename, &contents, &read_err) != FileReader::Okay) { *err = "loading '" + filename + "': " + read_err; if (parent) parent->Error(string(*err), err); diff --git a/src/manifest_parser.h b/src/manifest_parser.h index 41d388c..043e4b2 100644 --- a/src/manifest_parser.h +++ b/src/manifest_parser.h @@ -23,6 +23,7 @@ using namespace std; struct BindingEnv; struct EvalString; +struct FileReader; struct State; enum DupeEdgeAction { @@ -32,11 +33,6 @@ enum DupeEdgeAction { /// Parses .ninja files. struct ManifestParser { - struct FileReader { - virtual ~FileReader() {} - virtual bool ReadFile(const string& path, string* content, string* err) = 0; - }; - ManifestParser(State* state, FileReader* file_reader, DupeEdgeAction dupe_edge_action); diff --git a/src/manifest_parser_perftest.cc b/src/manifest_parser_perftest.cc index ef3b663..572e2d5 100644 --- a/src/manifest_parser_perftest.cc +++ b/src/manifest_parser_perftest.cc @@ -36,12 +36,6 @@ #include "state.h" #include "util.h" -struct RealFileReader : public ManifestParser::FileReader { - virtual bool ReadFile(const string& path, string* content, string* err) { - return ::ReadFile(path, content, err) == 0; - } -}; - bool WriteFakeManifests(const string& dir, string* err) { RealDiskInterface disk_interface; TimeStamp mtime = disk_interface.Stat(dir + "/build.ninja", err); @@ -59,9 +53,9 @@ bool WriteFakeManifests(const string& dir, string* err) { int LoadManifests(bool measure_command_evaluation) { string err; - RealFileReader file_reader; + RealDiskInterface disk_interface; State state; - ManifestParser parser(&state, &file_reader, kDupeEdgeActionWarn); + ManifestParser parser(&state, &disk_interface, kDupeEdgeActionWarn); if (!parser.Load("build.ninja", &err)) { fprintf(stderr, "Failed to read test data: %s\n", err.c_str()); exit(1); diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc index a18433a..cb9f405 100644 --- a/src/manifest_parser_test.cc +++ b/src/manifest_parser_test.cc @@ -21,30 +21,17 @@ #include "state.h" #include "test.h" -struct ParserTest : public testing::Test, - public ManifestParser::FileReader { +struct ParserTest : public testing::Test { void AssertParse(const char* input) { - ManifestParser parser(&state, this, kDupeEdgeActionWarn); + ManifestParser parser(&state, &fs_, kDupeEdgeActionWarn); string err; EXPECT_TRUE(parser.ParseTest(input, &err)); ASSERT_EQ("", err); VerifyGraph(state); } - virtual bool ReadFile(const string& path, string* content, string* err) { - files_read_.push_back(path); - map::iterator i = files_.find(path); - if (i == files_.end()) { - *err = "No such file or directory"; // Match strerror() for ENOENT. - return false; - } - *content = i->second; - return true; - } - State state; - map files_; - vector files_read_; + VirtualFileSystem fs_; }; TEST_F(ParserTest, Empty) { @@ -371,22 +358,22 @@ TEST_F(ParserTest, DuplicateEdgeWithMultipleOutputsError) { "build out1 out2: cat in1\n" "build out1: cat in2\n" "build final: cat out1\n"; - ManifestParser parser(&state, this, kDupeEdgeActionError); + ManifestParser parser(&state, &fs_, kDupeEdgeActionError); string err; EXPECT_FALSE(parser.ParseTest(kInput, &err)); EXPECT_EQ("input:5: multiple rules generate out1 [-w dupbuild=err]\n", err); } TEST_F(ParserTest, DuplicateEdgeInIncludedFile) { - files_["sub.ninja"] = + fs_.Create("sub.ninja", "rule cat\n" " command = cat $in > $out\n" "build out1 out2: cat in1\n" "build out1: cat in2\n" - "build final: cat out1\n"; + "build final: cat out1\n"); const char kInput[] = "subninja sub.ninja\n"; - ManifestParser parser(&state, this, kDupeEdgeActionError); + ManifestParser parser(&state, &fs_, kDupeEdgeActionError); string err; EXPECT_FALSE(parser.ParseTest(kInput, &err)); EXPECT_EQ("sub.ninja:5: multiple rules generate out1 [-w dupbuild=err]\n", @@ -813,7 +800,7 @@ TEST_F(ParserTest, Errors) { TEST_F(ParserTest, MissingInput) { State state; - ManifestParser parser(&state, this, kDupeEdgeActionWarn); + ManifestParser parser(&state, &fs_, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.Load("build.ninja", &err)); EXPECT_EQ("loading 'build.ninja': No such file or directory", err); @@ -841,9 +828,9 @@ TEST_F(ParserTest, MultipleOutputsWithDeps) { } TEST_F(ParserTest, SubNinja) { - files_["test.ninja"] = + fs_.Create("test.ninja", "var = inner\n" - "build $builddir/inner: varref\n"; + "build $builddir/inner: varref\n"); ASSERT_NO_FATAL_FAILURE(AssertParse( "builddir = some_dir/\n" "rule varref\n" @@ -852,9 +839,9 @@ TEST_F(ParserTest, SubNinja) { "build $builddir/outer: varref\n" "subninja test.ninja\n" "build $builddir/outer2: varref\n")); - ASSERT_EQ(1u, files_read_.size()); + ASSERT_EQ(1u, fs_.files_read_.size()); - EXPECT_EQ("test.ninja", files_read_[0]); + EXPECT_EQ("test.ninja", fs_.files_read_[0]); EXPECT_TRUE(state.LookupNode("some_dir/outer")); // Verify our builddir setting is inherited. EXPECT_TRUE(state.LookupNode("some_dir/inner")); @@ -866,7 +853,7 @@ TEST_F(ParserTest, SubNinja) { } TEST_F(ParserTest, MissingSubNinja) { - ManifestParser parser(&state, this, kDupeEdgeActionWarn); + ManifestParser parser(&state, &fs_, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("subninja foo.ninja\n", &err)); EXPECT_EQ("input:1: loading 'foo.ninja': No such file or directory\n" @@ -877,9 +864,9 @@ TEST_F(ParserTest, MissingSubNinja) { TEST_F(ParserTest, DuplicateRuleInDifferentSubninjas) { // Test that rules are scoped to subninjas. - files_["test.ninja"] = "rule cat\n" - " command = cat\n"; - ManifestParser parser(&state, this, kDupeEdgeActionWarn); + fs_.Create("test.ninja", "rule cat\n" + " command = cat\n"); + ManifestParser parser(&state, &fs_, kDupeEdgeActionWarn); string err; EXPECT_TRUE(parser.ParseTest("rule cat\n" " command = cat\n" @@ -888,11 +875,11 @@ TEST_F(ParserTest, DuplicateRuleInDifferentSubninjas) { TEST_F(ParserTest, DuplicateRuleInDifferentSubninjasWithInclude) { // Test that rules are scoped to subninjas even with includes. - files_["rules.ninja"] = "rule cat\n" - " command = cat\n"; - files_["test.ninja"] = "include rules.ninja\n" - "build x : cat\n"; - ManifestParser parser(&state, this, kDupeEdgeActionWarn); + fs_.Create("rules.ninja", "rule cat\n" + " command = cat\n"); + fs_.Create("test.ninja", "include rules.ninja\n" + "build x : cat\n"); + ManifestParser parser(&state, &fs_, kDupeEdgeActionWarn); string err; EXPECT_TRUE(parser.ParseTest("include rules.ninja\n" "subninja test.ninja\n" @@ -900,19 +887,19 @@ TEST_F(ParserTest, DuplicateRuleInDifferentSubninjasWithInclude) { } TEST_F(ParserTest, Include) { - files_["include.ninja"] = "var = inner\n"; + fs_.Create("include.ninja", "var = inner\n"); ASSERT_NO_FATAL_FAILURE(AssertParse( "var = outer\n" "include include.ninja\n")); - ASSERT_EQ(1u, files_read_.size()); - EXPECT_EQ("include.ninja", files_read_[0]); + ASSERT_EQ(1u, fs_.files_read_.size()); + EXPECT_EQ("include.ninja", fs_.files_read_[0]); EXPECT_EQ("inner", state.bindings_.LookupVariable("var")); } TEST_F(ParserTest, BrokenInclude) { - files_["include.ninja"] = "build\n"; - ManifestParser parser(&state, this, kDupeEdgeActionWarn); + fs_.Create("include.ninja", "build\n"); + ManifestParser parser(&state, &fs_, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("include include.ninja\n", &err)); EXPECT_EQ("include.ninja:1: expected path\n" diff --git a/src/ninja.cc b/src/ninja.cc index 3af10d6..a3f1be0 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -229,14 +229,6 @@ int GuessParallelism() { } } -/// An implementation of ManifestParser::FileReader that actually reads -/// the file. -struct RealFileReader : public ManifestParser::FileReader { - virtual bool ReadFile(const string& path, string* content, string* err) { - return ::ReadFile(path, content, err) == 0; - } -}; - /// Rebuild the build manifest, if necessary. /// Returns true if the manifest was rebuilt. bool NinjaMain::RebuildManifest(const char* input_file, string* err) { @@ -1117,8 +1109,7 @@ int real_main(int argc, char** argv) { for (int cycle = 1; cycle <= kCycleLimit; ++cycle) { NinjaMain ninja(ninja_command, config); - RealFileReader file_reader; - ManifestParser parser(&ninja.state_, &file_reader, + ManifestParser parser(&ninja.state_, &ninja.disk_interface_, options.dupe_edges_should_err ? kDupeEdgeActionError : kDupeEdgeActionWarn); -- cgit v0.12 From e8d855f2622693a4b616cba96084cd7a58cb0611 Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 11 Aug 2015 13:17:12 -0400 Subject: Avoid double-scheduling build edges in another case The change in commit v1.2.0~3^2~3^2~3 (Fix duplicate edge Pool crash in the minimally invasive way, 2013-03-18) avoids double-scheduling in a case involving duplicate out edges. However, double-scheduling may also occur on a consistent graph when an edge and one of its dependencies share an order-only input: $ cat build.ninja ... build c: touch build b: touch || c build a: touch | b || c $ ninja a $ rm a c $ ninja a In this case 'c' will build first. When NodeFinished('c') loops over the out edges it will find AllInputsReady is true for 'b' and call EdgeFinished('b') since it is not wanted (up to date). This will call NodeFinished('b') which will loop over its out edges, find AllInputsReady is true for 'a', and call ScheduleEdge('a'). When we eventually return to the loop in NodeFinished('c') it will move on to its second output and find that AllInputsReady is true for 'a' and call ScheduleEdge('a') again. Teach ScheduleEdge to tolerate duplicate calls for an edge that has already been scheduled. Avoid calling EdgeScheduled more than once for the same edge. --- src/build.cc | 17 ++++++++++------- src/build_test.cc | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/build.cc b/src/build.cc index e33a007..0f3c8ef 100644 --- a/src/build.cc +++ b/src/build.cc @@ -366,19 +366,22 @@ Edge* Plan::FindWork() { } void Plan::ScheduleWork(Edge* edge) { + set::iterator e = ready_.lower_bound(edge); + if (e != ready_.end() && !ready_.key_comp()(edge, *e)) { + // This edge has already been scheduled. We can get here again if an edge + // and one of its dependencies share an order-only input, or if a node + // duplicates an out edge (see https://github.com/ninja-build/ninja/pull/519). + // Avoid scheduling the work again. + return; + } + Pool* pool = edge->pool(); if (pool->ShouldDelayEdge()) { - // The graph is not completely clean. Some Nodes have duplicate Out edges. - // We need to explicitly ignore these here, otherwise their work will get - // scheduled twice (see https://github.com/ninja-build/ninja/pull/519) - if (ready_.count(edge)) { - return; - } pool->DelayEdge(edge); pool->RetrieveReadyEdges(&ready_); } else { pool->EdgeScheduled(*edge); - ready_.insert(edge); + ready_.insert(e, edge); } } diff --git a/src/build_test.cc b/src/build_test.cc index 7c6060d..26bf7ef 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -875,6 +875,29 @@ TEST_F(BuildTest, DepFileParseError) { EXPECT_EQ("foo.o.d: expected ':' in depfile", err); } +TEST_F(BuildTest, EncounterReadyTwice) { + string err; + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"rule touch\n" +" command = touch $out\n" +"build c: touch\n" +"build b: touch || c\n" +"build a: touch | b || c\n")); + + vector c_out = GetNode("c")->out_edges(); + ASSERT_EQ(2u, c_out.size()); + EXPECT_EQ("b", c_out[0]->outputs_[0]->path()); + EXPECT_EQ("a", c_out[1]->outputs_[0]->path()); + + fs_.Create("b", ""); + EXPECT_TRUE(builder_.AddTarget("a", &err)); + ASSERT_EQ("", err); + + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ("", err); + ASSERT_EQ(2u, command_runner_.commands_ran_.size()); +} + TEST_F(BuildTest, OrderOnlyDeps) { string err; ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, -- cgit v0.12 From 17852dae5bd7f63fdaa091db5e3284e87c56ef99 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Wed, 3 Feb 2016 17:01:45 -0500 Subject: Minor updates to the manual. * Update link to Chromium's ninja docs (fixes #1038) * Update cmake URL to what it redirects to, and mention that ninja is well-supported on all platforms in newer CMake versions. * Let "others" link to the wiki page listing generators. --- doc/manual.asciidoc | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc index ab5c945..52b3a31 100644 --- a/doc/manual.asciidoc +++ b/doc/manual.asciidoc @@ -158,17 +158,13 @@ http://code.google.com/p/gyp/[gyp]:: The meta-build system used to generate build files for Google Chrome and related projects (v8, node.js). gyp can generate Ninja files for all platforms supported by Chrome. See the -http://code.google.com/p/chromium/wiki/NinjaBuild[Chromium Ninja -documentation for more details]. +https://chromium.googlesource.com/chromium/src/+/master/docs/ninja_build.md[Chromium Ninja documentation for more details]. -http://www.cmake.org/[CMake]:: A widely used meta-build system that -can generate Ninja files on Linux as of CMake version 2.8.8. (There -is some Mac and Windows support -- http://www.reactos.org[ReactOS] -uses Ninja on Windows for their buildbots, but those platforms are not -yet officially supported by CMake as the full test suite doesn't -pass.) +https://cmake.org/[CMake]:: A widely used meta-build system that +can generate Ninja files on Linux as of CMake version 2.8.8. Newer versions +of CMake support generating Ninja files on Windows and Mac OS X too. -others:: Ninja ought to fit perfectly into other meta-build software +https://github.com/ninja-build/ninja/wiki/List-of-generators-producing-ninja-build-files[others]:: Ninja ought to fit perfectly into other meta-build software like http://industriousone.com/premake[premake]. If you do this work, please let us know! -- cgit v0.12 From ab652787de83d94af71711ae211c4223958457c4 Mon Sep 17 00:00:00 2001 From: Frank Benkstein Date: Thu, 4 Feb 2016 13:20:58 +0100 Subject: MSVC: disable warning about constant conditional expressions The standard headers for Visual Studio 2008 generate a warning about constant conditional expressions when compiled with exception support disabled. This is caused by the _CATCH_ALL macro in xstddef which is defined thusly: #ifdef _HAS_EXCEPTIONS #define _CATCH_ALL } catch (...) { #else #define _CATCH_ALL } if (0) { #endif --- configure.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.py b/configure.py index 1c97db7..92d0573 100755 --- a/configure.py +++ b/configure.py @@ -302,6 +302,8 @@ if platform.is_msvc(): '/WX', # Warnings as errors. '/wd4530', '/wd4100', '/wd4706', '/wd4512', '/wd4800', '/wd4702', '/wd4819', + # Disable warnings about constant conditional expressions. + '/wd4127', # Disable warnings about passing "this" during initialization. '/wd4355', # Disable warnings about ignored typedef in DbgHelp.h -- cgit v0.12 From 2f127fab680c44977190e45af82ce23215369a96 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Fri, 5 Feb 2016 13:28:56 -0500 Subject: win: Only drop /showIncludes in bootstrap builds. Fixes #1103. --- configure.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/configure.py b/configure.py index 0710ea2..b9c5f57 100755 --- a/configure.py +++ b/configure.py @@ -133,7 +133,9 @@ class Bootstrap: return self.writer.newline() def variable(self, key, val): - self.vars[key] = self._expand(val) + # In bootstrap mode, we have no ninja process to catch /showIncludes + # output. + self.vars[key] = self._expand(val).replace('/showIncludes', '') return self.writer.variable(key, val) def rule(self, name, **kwargs): @@ -308,10 +310,6 @@ if platform.is_msvc(): '/DNOMINMAX', '/D_CRT_SECURE_NO_WARNINGS', '/D_HAS_EXCEPTIONS=0', '/DNINJA_PYTHON="%s"' % options.with_python] - if options.bootstrap: - # In bootstrap mode, we have no ninja process to catch /showIncludes - # output. - cflags.remove('/showIncludes') if platform.msvc_needs_fs(): cflags.append('/FS') ldflags = ['/DEBUG', '/libpath:$builddir'] -- cgit v0.12 From d3c7da58930fd5985712e4410b271086eef02b37 Mon Sep 17 00:00:00 2001 From: Nicolas Despres Date: Sat, 6 Feb 2016 09:37:51 +0100 Subject: Add support for implicit outputs in ninja_syntax.py. --- misc/ninja_syntax.py | 7 ++++++- misc/ninja_syntax_test.py | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/misc/ninja_syntax.py b/misc/ninja_syntax.py index 73d2209..5c52ea2 100644 --- a/misc/ninja_syntax.py +++ b/misc/ninja_syntax.py @@ -60,7 +60,7 @@ class Writer(object): self.variable('deps', deps, indent=1) def build(self, outputs, rule, inputs=None, implicit=None, order_only=None, - variables=None): + variables=None, implicit_outputs=None): outputs = as_list(outputs) out_outputs = [escape_path(x) for x in outputs] all_inputs = [escape_path(x) for x in as_list(inputs)] @@ -73,6 +73,11 @@ class Writer(object): order_only = [escape_path(x) for x in as_list(order_only)] all_inputs.append('||') all_inputs.extend(order_only) + if implicit_outputs: + implicit_outputs = [escape_path(x) + for x in as_list(implicit_outputs)] + out_outputs.append('|') + out_outputs.extend(implicit_outputs) self._line('build %s: %s' % (' '.join(out_outputs), ' '.join([rule] + all_inputs))) diff --git a/misc/ninja_syntax_test.py b/misc/ninja_syntax_test.py index c9755b8..07e3ed3 100755 --- a/misc/ninja_syntax_test.py +++ b/misc/ninja_syntax_test.py @@ -154,6 +154,13 @@ build out: cc in ''', self.out.getvalue()) + def test_implicit_outputs(self): + self.n.build('o', 'cc', 'i', implicit_outputs='io') + self.assertEqual('''\ +build o | io: cc i +''', + self.out.getvalue()) + class TestExpand(unittest.TestCase): def test_basic(self): vars = {'x': 'X'} -- cgit v0.12 From d49d111842fa2cf68854a82fe201c3b610b5c52b Mon Sep 17 00:00:00 2001 From: Ed Baunton Date: Sat, 6 Feb 2016 22:56:20 +0000 Subject: Add link to mailing list As a convenience to new users, provide a link to the mailing list. --- HACKING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HACKING.md b/HACKING.md index c544615..c9c6601 100644 --- a/HACKING.md +++ b/HACKING.md @@ -56,8 +56,8 @@ particular, new build file syntax or command-line flags) or increase the maintenance burden of Ninja. Ninja is already successfully used by hundreds of developers for large projects and it already achieves (most of) the goals I set out for it to do. It's probably best to -discuss new feature ideas on the mailing list before I shoot down your -patch. +discuss new feature ideas on the [mailing list](https://groups.google.com/forum/#!forum/ninja-build) +before I shoot down your patch. ## Testing -- cgit v0.12 From 71eaf652bacf74d9379f97e5c7c9da79b5ef6fe0 Mon Sep 17 00:00:00 2001 From: g4m4 Date: Wed, 17 Feb 2016 11:35:52 +0100 Subject: Fix ambiguous call to set_terminate on Windows platform On Windows set_terminate() could either be the standard C++ one or (actually the same one but in the global namespace) the CRT one declared in corecrt_terminate.h Hence this ambiguity - this patch solves it. Signed-off-by: g4m4 --- src/ninja.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ninja.cc b/src/ninja.cc index a3f1be0..35f293b 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -1161,7 +1161,7 @@ int main(int argc, char** argv) { #if defined(_MSC_VER) // Set a handler to catch crashes not caught by the __try..__except // block (e.g. an exception in a stack-unwind-block). - set_terminate(TerminateHandler); + std::set_terminate(TerminateHandler); __try { // Running inside __try ... __except suppresses any Windows error // dialogs for errors such as bad_alloc. -- cgit v0.12 From 1a8e5455939ed3f701141cc72262add9a7d75055 Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 17 Nov 2015 14:28:11 -0500 Subject: Remove StatIfNecessary call that is never necessary The call to StatIfNecessary in DependencyScan::RecomputeOutputsDirty was added by commit v1.4.0^2~7^2~1 (Share more code between CleanNode() and RecomputeDirty(), 2013-09-02) while consolidating code paths. However, it was needed only when called from RecomputeDirty because prior to refactoring the CleanNode code path did not call it. Later commit v1.6.0^2~46^2 (Let DependencyScan::RecomputeDirty() work correclty with cyclic graphs, 2014-12-07) added back to RecomputeDirty a loop over outputs that calls StatIfNecessary. Therefore RecomputeOutputsDirty no longer needs to call StatIfNecessary for either of its own callers. --- src/graph.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/graph.cc b/src/graph.cc index 98a2c18..f1d9ca2 100644 --- a/src/graph.cc +++ b/src/graph.cc @@ -37,6 +37,7 @@ bool DependencyScan::RecomputeDirty(Edge* edge, string* err) { edge->outputs_ready_ = true; edge->deps_missing_ = false; + // Load output mtimes so we can compare them to the most recent input below. // RecomputeDirty() recursively walks the graph following the input nodes // of |edge| and the in_edges of these nodes. It uses the stat state of each // node to mark nodes as visited and doesn't traverse across nodes that have @@ -126,8 +127,6 @@ bool DependencyScan::RecomputeOutputsDirty(Edge* edge, Node* most_recent_input, string command = edge->EvaluateCommand(/*incl_rsp_file=*/true); for (vector::iterator o = edge->outputs_.begin(); o != edge->outputs_.end(); ++o) { - if (!(*o)->StatIfNecessary(disk_interface_, err)) - return false; if (RecomputeOutputDirty(edge, most_recent_input, command, *o)) { *outputs_dirty = true; return true; -- cgit v0.12 From 572cc8eb6f70e3acbae57e586788f15e9b562f96 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Fri, 26 Feb 2016 00:16:15 +0100 Subject: browse.py: allow port override via environment variable --- doc/manual.asciidoc | 3 ++- src/browse.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc index 0d842f9..4e69ad4 100644 --- a/doc/manual.asciidoc +++ b/doc/manual.asciidoc @@ -223,7 +223,8 @@ found useful during Ninja's development. The current tools are: `browse`:: browse the dependency graph in a web browser. Clicking a file focuses the view on that file, showing inputs and outputs. This -feature requires a Python installation. +feature requires a Python installation. The `PORT` environment variable +can be used to override the default port number (8000). `graph`:: output a file in the syntax used by `graphviz`, a automatic graph layout tool. Use it like: diff --git a/src/browse.py b/src/browse.py index 9e59bd8..081eb8c 100755 --- a/src/browse.py +++ b/src/browse.py @@ -182,7 +182,7 @@ class RequestHandler(httpserver.BaseHTTPRequestHandler): def log_message(self, format, *args): pass # Swallow console spam. -port = 8000 +port = int(os.getenv("PORT", '8000')) httpd = httpserver.HTTPServer(('',port), RequestHandler) try: hostname = socket.gethostname() -- cgit v0.12 From e605fcc8ea91487e1653444ecf63f5597261f17c Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Fri, 4 Mar 2016 17:51:06 -0800 Subject: Revert #910. The change caused some issues (it makes it impossible ot use posix_spawn() and makes it harder to suspend children on ctrl-z). After discussing with jln: Since it fixes a corner case that can be fixed by explicitly running commands that need it in a wrapper that setsid()s them, let's try reverting it for a while. Please shout if this is a problem for you. See also #1097. --- src/subprocess-posix.cc | 7 ++----- src/subprocess_test.cc | 27 +-------------------------- 2 files changed, 3 insertions(+), 31 deletions(-) diff --git a/src/subprocess-posix.cc b/src/subprocess-posix.cc index 2ddc709..ae7ae6f 100644 --- a/src/subprocess-posix.cc +++ b/src/subprocess-posix.cc @@ -70,11 +70,8 @@ bool Subprocess::Start(SubprocessSet* set, const string& command) { break; if (!use_console_) { - // Put the child in its own session and process group. It will be - // detached from the current terminal and ctrl-c won't reach it. - // Since this process was just forked, it is not a process group leader - // and setsid() will succeed. - if (setsid() < 0) + // Put the child in its own process group, so ctrl-c won't reach it. + if (setpgid(0, 0) < 0) break; // Open /dev/null over stdin. diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc index 2fe4bce..c8f2fb8 100644 --- a/src/subprocess_test.cc +++ b/src/subprocess_test.cc @@ -16,8 +16,6 @@ #include "test.h" -#include - #ifndef _WIN32 // SetWithLots need setrlimit. #include @@ -146,22 +144,11 @@ TEST_F(SubprocessTest, InterruptParentWithSigHup) { ASSERT_FALSE("We should have been interrupted"); } -// A shell command to check if the current process is connected to a terminal. -// This is different from having stdin/stdout/stderr be a terminal. (For -// instance consider the command "yes < /dev/null > /dev/null 2>&1". -// As "ps" will confirm, "yes" could still be connected to a terminal, despite -// not having any of the standard file descriptors be a terminal. -static const char kIsConnectedToTerminal[] = "tty < /dev/tty > /dev/null"; - TEST_F(SubprocessTest, Console) { // Skip test if we don't have the console ourselves. if (isatty(0) && isatty(1) && isatty(2)) { - // Test that stdin, stdout and stderr are a terminal. - // Also check that the current process is connected to a terminal. Subprocess* subproc = - subprocs_.Add(string("test -t 0 -a -t 1 -a -t 2 && ") + - string(kIsConnectedToTerminal), - /*use_console=*/true); + subprocs_.Add("test -t 0 -a -t 1 -a -t 2", /*use_console=*/true); ASSERT_NE((Subprocess*)0, subproc); while (!subproc->Done()) { @@ -172,18 +159,6 @@ TEST_F(SubprocessTest, Console) { } } -TEST_F(SubprocessTest, NoConsole) { - Subprocess* subproc = - subprocs_.Add(kIsConnectedToTerminal, /*use_console=*/false); - ASSERT_NE((Subprocess*)0, subproc); - - while (!subproc->Done()) { - subprocs_.DoWork(); - } - - EXPECT_NE(ExitSuccess, subproc->Finish()); -} - #endif TEST_F(SubprocessTest, SetWithSingle) { -- cgit v0.12 From f7491398a2e97c7c76d5a28cfb3b8ce1a0a1a580 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Fri, 26 Feb 2016 00:23:48 +0100 Subject: browse.py: Python 3 compatibility --- src/browse.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/browse.py b/src/browse.py index 081eb8c..bf85e4d 100755 --- a/src/browse.py +++ b/src/browse.py @@ -31,7 +31,10 @@ import socket import subprocess import sys import webbrowser -import urllib2 +try: + from urllib.request import unquote +except ImportError: + from urllib2 import unquote from collections import namedtuple Node = namedtuple('Node', ['inputs', 'rule', 'target', 'outputs']) @@ -154,7 +157,7 @@ def ninja_dump(target): class RequestHandler(httpserver.BaseHTTPRequestHandler): def do_GET(self): assert self.path[0] == '/' - target = urllib2.unquote(self.path[1:]) + target = unquote(self.path[1:]) if target == '': self.send_response(302) -- cgit v0.12 From 048ad189e16917b26c370669c9b978d9fd23bae2 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Sat, 12 Mar 2016 00:24:00 +0100 Subject: browse: support --port and --no-browser options Add --port option to override the default port (8000). Add --no-browser option to avoid opening a web browser (useful over SSH). Make the target name optional, using "all" if omitted. --- doc/manual.asciidoc | 10 +++++++--- src/browse.cc | 18 ++++++++++++------ src/browse.h | 5 +++-- src/browse.py | 22 ++++++++++++++++++---- src/ninja.cc | 6 +----- 5 files changed, 41 insertions(+), 20 deletions(-) diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc index 4e69ad4..9fc5fb9 100644 --- a/doc/manual.asciidoc +++ b/doc/manual.asciidoc @@ -223,9 +223,13 @@ found useful during Ninja's development. The current tools are: `browse`:: browse the dependency graph in a web browser. Clicking a file focuses the view on that file, showing inputs and outputs. This -feature requires a Python installation. The `PORT` environment variable -can be used to override the default port number (8000). - +feature requires a Python installation. By default port 8000 is used +and a web browser will be opened. This can be changed as follows: ++ +---- +ninja -t browse --port=8000 --no-browser mytarget +---- ++ `graph`:: output a file in the syntax used by `graphviz`, a automatic graph layout tool. Use it like: + diff --git a/src/browse.cc b/src/browse.cc index 8673919..46434d7 100644 --- a/src/browse.cc +++ b/src/browse.cc @@ -17,11 +17,12 @@ #include #include #include +#include #include "build/browse_py.h" void RunBrowsePython(State* state, const char* ninja_command, - const char* initial_target) { + int argc, char* argv[]) { // Fork off a Python process and have it run our code via its stdin. // (Actually the Python process becomes the parent.) int pipefd[2]; @@ -44,11 +45,16 @@ void RunBrowsePython(State* state, const char* ninja_command, break; } - // exec Python, telling it to run the program from stdin. - const char* command[] = { - NINJA_PYTHON, "-", ninja_command, initial_target, NULL - }; - execvp(command[0], (char**)command); + std::vector command; + command.push_back(NINJA_PYTHON); + command.push_back("-"); + command.push_back("--ninja-command"); + command.push_back(ninja_command); + for (int i = 0; i < argc; i++) { + command.push_back(argv[i]); + } + command.push_back(NULL); + execvp(command[0], (char**)&command[0]); perror("ninja: execvp"); } while (false); _exit(1); diff --git a/src/browse.h b/src/browse.h index 263641f..842c6ff 100644 --- a/src/browse.h +++ b/src/browse.h @@ -19,9 +19,10 @@ struct State; /// Run in "browse" mode, which execs a Python webserver. /// \a ninja_command is the command used to invoke ninja. -/// \a initial_target is the first target to load. +/// \a args are the number of arguments to be passed to the Python script. +/// \a argv are arguments to be passed to the Python script. /// This function does not return if it runs successfully. void RunBrowsePython(State* state, const char* ninja_command, - const char* initial_target); + int argc, char* argv[]); #endif // NINJA_BROWSE_H_ diff --git a/src/browse.py b/src/browse.py index bf85e4d..63c60aa 100755 --- a/src/browse.py +++ b/src/browse.py @@ -26,6 +26,7 @@ try: import http.server as httpserver except ImportError: import BaseHTTPServer as httpserver +import argparse import os import socket import subprocess @@ -149,7 +150,7 @@ def generate_html(node): return '\n'.join(document) def ninja_dump(target): - proc = subprocess.Popen([sys.argv[1], '-t', 'query', target], + proc = subprocess.Popen([args.ninja_command, '-t', 'query', target], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) return proc.communicate() + (proc.returncode,) @@ -161,7 +162,7 @@ class RequestHandler(httpserver.BaseHTTPRequestHandler): if target == '': self.send_response(302) - self.send_header('Location', '?' + sys.argv[2]) + self.send_header('Location', '?' + args.initial_target) self.end_headers() return @@ -185,13 +186,26 @@ class RequestHandler(httpserver.BaseHTTPRequestHandler): def log_message(self, format, *args): pass # Swallow console spam. -port = int(os.getenv("PORT", '8000')) +parser = argparse.ArgumentParser(prog='ninja -t browse') +parser.add_argument('--port', '-p', default=8000, type=int, + help='Port number to use (default %(default)d)') +parser.add_argument('--no-browser', action='store_true', + help='Do not open a webbrowser on startup.') + +parser.add_argument('--ninja-command', default='ninja', + help='Path to ninja binary (default %(default)s)') +parser.add_argument('initial_target', default='all', nargs='?', + help='Initial target to show (default %(default)s)') + +args = parser.parse_args() +port = args.port httpd = httpserver.HTTPServer(('',port), RequestHandler) try: hostname = socket.gethostname() print('Web server running on %s:%d, ctl-C to abort...' % (hostname,port) ) print('Web server pid %d' % os.getpid(), file=sys.stderr ) - webbrowser.open_new('http://%s:%s' % (hostname, port) ) + if not args.no_browser: + webbrowser.open_new('http://%s:%s' % (hostname, port) ) httpd.serve_forever() except KeyboardInterrupt: print() diff --git a/src/ninja.cc b/src/ninja.cc index 35f293b..b3b9bed 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -371,11 +371,7 @@ int NinjaMain::ToolQuery(int argc, char* argv[]) { #if defined(NINJA_HAVE_BROWSE) int NinjaMain::ToolBrowse(int argc, char* argv[]) { - if (argc < 1) { - Error("expected a target to browse"); - return 1; - } - RunBrowsePython(&state_, ninja_command_, argv[0]); + RunBrowsePython(&state_, ninja_command_, argc, argv); // If we get here, the browse failed. return 1; } -- cgit v0.12 From 89587196705f54afb904c8f4572e65de7274dd81 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Sun, 20 Mar 2016 21:41:15 -0400 Subject: Use posix_spawn() instead of fork()/exec(). posix_spawn() is a syscall on OS X and Solaris and a bit faster. It's also easier emulate for cygwin, and the code is a bit simpler. --- src/subprocess-posix.cc | 107 ++++++++++++++++++++++++------------------------ src/subprocess_test.cc | 3 +- 2 files changed, 56 insertions(+), 54 deletions(-) diff --git a/src/subprocess-posix.cc b/src/subprocess-posix.cc index ae7ae6f..5ffe85b 100644 --- a/src/subprocess-posix.cc +++ b/src/subprocess-posix.cc @@ -22,6 +22,9 @@ #include #include #include +#include + +extern char** environ; #include "util.h" @@ -50,62 +53,60 @@ bool Subprocess::Start(SubprocessSet* set, const string& command) { #endif // !USE_PPOLL SetCloseOnExec(fd_); - pid_ = fork(); - if (pid_ < 0) - Fatal("fork: %s", strerror(errno)); - - if (pid_ == 0) { - close(output_pipe[0]); - - // Track which fd we use to report errors on. - int error_pipe = output_pipe[1]; - do { - if (sigaction(SIGINT, &set->old_int_act_, 0) < 0) - break; - if (sigaction(SIGTERM, &set->old_term_act_, 0) < 0) - break; - if (sigaction(SIGHUP, &set->old_hup_act_, 0) < 0) - break; - if (sigprocmask(SIG_SETMASK, &set->old_mask_, 0) < 0) - break; - - if (!use_console_) { - // Put the child in its own process group, so ctrl-c won't reach it. - if (setpgid(0, 0) < 0) - break; - - // Open /dev/null over stdin. - int devnull = open("/dev/null", O_RDONLY); - if (devnull < 0) - break; - if (dup2(devnull, 0) < 0) - break; - close(devnull); - - if (dup2(output_pipe[1], 1) < 0 || - dup2(output_pipe[1], 2) < 0) - break; - - // Now can use stderr for errors. - error_pipe = 2; - close(output_pipe[1]); - } - // In the console case, output_pipe is still inherited by the child and - // closed when the subprocess finishes, which then notifies ninja. - - execl("/bin/sh", "/bin/sh", "-c", command.c_str(), (char *) NULL); - } while (false); - - // If we get here, something went wrong; the execl should have - // replaced us. - char* err = strerror(errno); - if (write(error_pipe, err, strlen(err)) < 0) { - // If the write fails, there's nothing we can do. - // But this block seems necessary to silence the warning. + posix_spawn_file_actions_t action; + if (posix_spawn_file_actions_init(&action) != 0) + Fatal("posix_spawn_file_actions_init: %s", strerror(errno)); + + if (posix_spawn_file_actions_addclose(&action, output_pipe[0]) != 0) + Fatal("posix_spawn_file_actions_addclose: %s", strerror(errno)); + + posix_spawnattr_t attr; + if (posix_spawnattr_init(&attr) != 0) + Fatal("posix_spawnattr_init: %s", strerror(errno)); + + short flags = 0; + + flags |= POSIX_SPAWN_SETSIGMASK; + if (posix_spawnattr_setsigmask(&attr, &set->old_mask_) != 0) + Fatal("posix_spawnattr_setsigmask: %s", strerror(errno)); + // Signals which are set to be caught in the calling process image are set to + // default action in the new process image, so no explicit + // POSIX_SPAWN_SETSIGDEF parameter is needed. + + // TODO: Consider using POSIX_SPAWN_USEVFORK on Linux with glibc? + + if (!use_console_) { + // Put the child in its own process group, so ctrl-c won't reach it. + flags |= POSIX_SPAWN_SETPGROUP; + // No need to posix_spawnattr_setpgroup(&attr, 0), it's the default. + + // Open /dev/null over stdin. + if (posix_spawn_file_actions_addopen(&action, 0, "/dev/null", O_RDONLY, + 0) != 0) { + Fatal("posix_spawn_file_actions_addopen: %s", strerror(errno)); } - _exit(1); + + if (posix_spawn_file_actions_adddup2(&action, output_pipe[1], 1) != 0) + Fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno)); + if (posix_spawn_file_actions_adddup2(&action, output_pipe[1], 2) != 0) + Fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno)); + // In the console case, output_pipe is still inherited by the child and + // closed when the subprocess finishes, which then notifies ninja. } + if (posix_spawnattr_setflags(&attr, flags) != 0) + Fatal("posix_spawnattr_setflags: %s", strerror(errno)); + + const char* spawned_args[] = { "/bin/sh", "-c", command.c_str(), NULL }; + if (posix_spawn(&pid_, "/bin/sh", &action, &attr, + const_cast(spawned_args), environ) != 0) + Fatal("posix_spawn: %s", strerror(errno)); + + if (posix_spawnattr_destroy(&attr) != 0) + Fatal("posix_spawnattr_destroy: %s", strerror(errno)); + if (posix_spawn_file_actions_destroy(&action) != 0) + Fatal("posix_spawn_file_actions_destroy: %s", strerror(errno)); + close(output_pipe[1]); return true; } diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc index c8f2fb8..ee16190 100644 --- a/src/subprocess_test.cc +++ b/src/subprocess_test.cc @@ -226,7 +226,8 @@ TEST_F(SubprocessTest, SetWithLots) { rlimit rlim; ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlim)); if (rlim.rlim_cur < kNumProcs) { - printf("Raise [ulimit -n] well above %u (currently %lu) to make this test go\n", kNumProcs, rlim.rlim_cur); + printf("Raise [ulimit -n] above %u (currently %lu) to make this test go\n", + kNumProcs, rlim.rlim_cur); return; } -- cgit v0.12 From 3a889126247d31d637c7470554be12f7c1df2ffa Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Thu, 11 Jun 2015 23:53:32 -0700 Subject: Make deps=msvc experimentally available on non-Windows. This makes it possible to run most of the clparser tests on non-Windows, and is potentially useful for cross-compiling on non-Windows hosts. Also, the manual didn't document this as Windows-only previously. If you use this on non-Windows, please let me know, else I might undo this change again in the future. --- configure.py | 2 + src/build.cc | 4 +- src/clparser.cc | 116 +++++++++++++++++++++++++++++++++++++++++ src/clparser.h | 52 +++++++++++++++++++ src/clparser_test.cc | 117 ++++++++++++++++++++++++++++++++++++++++++ src/msvc_helper-win32.cc | 83 ------------------------------ src/msvc_helper.h | 32 ------------ src/msvc_helper_main-win32.cc | 1 + src/msvc_helper_test.cc | 94 +-------------------------------- 9 files changed, 290 insertions(+), 211 deletions(-) create mode 100644 src/clparser.cc create mode 100644 src/clparser.h create mode 100644 src/clparser_test.cc diff --git a/configure.py b/configure.py index f0e452f..84218b9 100755 --- a/configure.py +++ b/configure.py @@ -475,6 +475,7 @@ n.comment('Core source files all build into ninja library.') for name in ['build', 'build_log', 'clean', + 'clparser', 'debug_flags', 'depfile_parser', 'deps_log', @@ -540,6 +541,7 @@ objs = [] for name in ['build_log_test', 'build_test', 'clean_test', + 'clparser_test', 'depfile_parser_test', 'deps_log_test', 'disk_interface_test', diff --git a/src/build.cc b/src/build.cc index e33a007..2df5c1a 100644 --- a/src/build.cc +++ b/src/build.cc @@ -25,12 +25,12 @@ #endif #include "build_log.h" +#include "clparser.h" #include "debug_flags.h" #include "depfile_parser.h" #include "deps_log.h" #include "disk_interface.h" #include "graph.h" -#include "msvc_helper.h" #include "state.h" #include "subprocess.h" #include "util.h" @@ -854,7 +854,6 @@ bool Builder::ExtractDeps(CommandRunner::Result* result, const string& deps_prefix, vector* deps_nodes, string* err) { -#ifdef _WIN32 if (deps_type == "msvc") { CLParser parser; string output; @@ -870,7 +869,6 @@ bool Builder::ExtractDeps(CommandRunner::Result* result, deps_nodes->push_back(state_->GetNode(*i, ~0u)); } } else -#endif if (deps_type == "gcc") { string depfile = result->edge->GetUnescapedDepfile(); if (depfile.empty()) { diff --git a/src/clparser.cc b/src/clparser.cc new file mode 100644 index 0000000..f73a8c1 --- /dev/null +++ b/src/clparser.cc @@ -0,0 +1,116 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "clparser.h" + +#include +#include +#include + +#ifdef _WIN32 +#include "includes_normalize.h" +#else +#include "util.h" +#endif + +namespace { + +/// Return true if \a input ends with \a needle. +bool EndsWith(const string& input, const string& needle) { + return (input.size() >= needle.size() && + input.substr(input.size() - needle.size()) == needle); +} + +} // anonymous namespace + +// static +string CLParser::FilterShowIncludes(const string& line, + const string& deps_prefix) { + const string kDepsPrefixEnglish = "Note: including file: "; + const char* in = line.c_str(); + const char* end = in + line.size(); + const string& prefix = deps_prefix.empty() ? kDepsPrefixEnglish : deps_prefix; + if (end - in > (int)prefix.size() && + memcmp(in, prefix.c_str(), (int)prefix.size()) == 0) { + in += prefix.size(); + while (*in == ' ') + ++in; + return line.substr(in - line.c_str()); + } + return ""; +} + +// static +bool CLParser::IsSystemInclude(string path) { + transform(path.begin(), path.end(), path.begin(), ::tolower); + // TODO: this is a heuristic, perhaps there's a better way? + return (path.find("program files") != string::npos || + path.find("microsoft visual studio") != string::npos); +} + +// static +bool CLParser::FilterInputFilename(string line) { + transform(line.begin(), line.end(), line.begin(), ::tolower); + // TODO: other extensions, like .asm? + return EndsWith(line, ".c") || + EndsWith(line, ".cc") || + EndsWith(line, ".cxx") || + EndsWith(line, ".cpp"); +} + +// static +bool CLParser::Parse(const string& output, const string& deps_prefix, + string* filtered_output, string* err) { + // Loop over all lines in the output to process them. + assert(&output != filtered_output); + size_t start = 0; + while (start < output.size()) { + size_t end = output.find_first_of("\r\n", start); + if (end == string::npos) + end = output.size(); + string line = output.substr(start, end - start); + + string include = FilterShowIncludes(line, deps_prefix); + if (!include.empty()) { + string normalized; +#ifdef _WIN32 + if (!IncludesNormalize::Normalize(include, NULL, &normalized, err)) + return false; +#else + // TODO: should this make the path relative to cwd? + normalized = include; + unsigned int slash_bits; + if (!CanonicalizePath(&normalized, &slash_bits, err)) + return false; +#endif + if (!IsSystemInclude(normalized)) + includes_.insert(normalized); + } else if (FilterInputFilename(line)) { + // Drop it. + // TODO: if we support compiling multiple output files in a single + // cl.exe invocation, we should stash the filename. + } else { + filtered_output->append(line); + filtered_output->append("\n"); + } + + if (end < output.size() && output[end] == '\r') + ++end; + if (end < output.size() && output[end] == '\n') + ++end; + start = end; + } + + return true; +} diff --git a/src/clparser.h b/src/clparser.h new file mode 100644 index 0000000..e597e7e --- /dev/null +++ b/src/clparser.h @@ -0,0 +1,52 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_CLPARSER_H_ +#define NINJA_CLPARSER_H_ + +#include +#include +using namespace std; + +/// Visual Studio's cl.exe requires some massaging to work with Ninja; +/// for example, it emits include information on stderr in a funny +/// format when building with /showIncludes. This class parses this +/// output. +struct CLParser { + /// Parse a line of cl.exe output and extract /showIncludes info. + /// If a dependency is extracted, returns a nonempty string. + /// Exposed for testing. + static string FilterShowIncludes(const string& line, + const string& deps_prefix); + + /// Return true if a mentioned include file is a system path. + /// Filtering these out reduces dependency information considerably. + static bool IsSystemInclude(string path); + + /// Parse a line of cl.exe output and return true if it looks like + /// it's printing an input filename. This is a heuristic but it appears + /// to be the best we can do. + /// Exposed for testing. + static bool FilterInputFilename(string line); + + /// Parse the full output of cl, filling filtered_output with the text that + /// should be printed (if any). Returns true on success, or false with err + /// filled. output must not be the same object as filtered_object. + bool Parse(const string& output, const string& deps_prefix, + string* filtered_output, string* err); + + set includes_; +}; + +#endif // NINJA_CLPARSER_H_ diff --git a/src/clparser_test.cc b/src/clparser_test.cc new file mode 100644 index 0000000..1549ab1 --- /dev/null +++ b/src/clparser_test.cc @@ -0,0 +1,117 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "clparser.h" + +#include "test.h" +#include "util.h" + +TEST(CLParserTest, ShowIncludes) { + ASSERT_EQ("", CLParser::FilterShowIncludes("", "")); + + ASSERT_EQ("", CLParser::FilterShowIncludes("Sample compiler output", "")); + ASSERT_EQ("c:\\Some Files\\foobar.h", + CLParser::FilterShowIncludes("Note: including file: " + "c:\\Some Files\\foobar.h", "")); + ASSERT_EQ("c:\\initspaces.h", + CLParser::FilterShowIncludes("Note: including file: " + "c:\\initspaces.h", "")); + ASSERT_EQ("c:\\initspaces.h", + CLParser::FilterShowIncludes("Non-default prefix: inc file: " + "c:\\initspaces.h", + "Non-default prefix: inc file:")); +} + +TEST(CLParserTest, FilterInputFilename) { + ASSERT_TRUE(CLParser::FilterInputFilename("foobar.cc")); + ASSERT_TRUE(CLParser::FilterInputFilename("foo bar.cc")); + ASSERT_TRUE(CLParser::FilterInputFilename("baz.c")); + ASSERT_TRUE(CLParser::FilterInputFilename("FOOBAR.CC")); + + ASSERT_FALSE(CLParser::FilterInputFilename( + "src\\cl_helper.cc(166) : fatal error C1075: end " + "of file found ...")); +} + +TEST(CLParserTest, ParseSimple) { + CLParser parser; + string output, err; + ASSERT_TRUE(parser.Parse( + "foo\r\n" + "Note: inc file prefix: foo.h\r\n" + "bar\r\n", + "Note: inc file prefix:", &output, &err)); + + ASSERT_EQ("foo\nbar\n", output); + ASSERT_EQ(1u, parser.includes_.size()); + ASSERT_EQ("foo.h", *parser.includes_.begin()); +} + +TEST(CLParserTest, ParseFilenameFilter) { + CLParser parser; + string output, err; + ASSERT_TRUE(parser.Parse( + "foo.cc\r\n" + "cl: warning\r\n", + "", &output, &err)); + ASSERT_EQ("cl: warning\n", output); +} + +TEST(CLParserTest, ParseSystemInclude) { + CLParser parser; + string output, err; + ASSERT_TRUE(parser.Parse( + "Note: including file: c:\\Program Files\\foo.h\r\n" + "Note: including file: d:\\Microsoft Visual Studio\\bar.h\r\n" + "Note: including file: path.h\r\n", + "", &output, &err)); + // We should have dropped the first two includes because they look like + // system headers. + ASSERT_EQ("", output); + ASSERT_EQ(1u, parser.includes_.size()); + ASSERT_EQ("path.h", *parser.includes_.begin()); +} + +TEST(CLParserTest, DuplicatedHeader) { + CLParser parser; + string output, err; + ASSERT_TRUE(parser.Parse( + "Note: including file: foo.h\r\n" + "Note: including file: bar.h\r\n" + "Note: including file: foo.h\r\n", + "", &output, &err)); + // We should have dropped one copy of foo.h. + ASSERT_EQ("", output); + ASSERT_EQ(2u, parser.includes_.size()); +} + +TEST(CLParserTest, DuplicatedHeaderPathConverted) { + CLParser parser; + string output, err; + + // This isn't inline in the Parse() call below because the #ifdef in + // a macro expansion would confuse MSVC2013's preprocessor. + const char kInput[] = + "Note: including file: sub/./foo.h\r\n" + "Note: including file: bar.h\r\n" +#ifdef _WIN32 + "Note: including file: sub\\foo.h\r\n"; +#else + "Note: including file: sub/foo.h\r\n"; +#endif + ASSERT_TRUE(parser.Parse(kInput, "", &output, &err)); + // We should have dropped one copy of foo.h. + ASSERT_EQ("", output); + ASSERT_EQ(2u, parser.includes_.size()); +} diff --git a/src/msvc_helper-win32.cc b/src/msvc_helper-win32.cc index d516240..e37a26e 100644 --- a/src/msvc_helper-win32.cc +++ b/src/msvc_helper-win32.cc @@ -14,23 +14,12 @@ #include "msvc_helper.h" -#include -#include -#include -#include #include -#include "includes_normalize.h" #include "util.h" namespace { -/// Return true if \a input ends with \a needle. -bool EndsWith(const string& input, const string& needle) { - return (input.size() >= needle.size() && - input.substr(input.size() - needle.size()) == needle); -} - string Replace(const string& input, const string& find, const string& replace) { string result = input; size_t start_pos = 0; @@ -48,78 +37,6 @@ string EscapeForDepfile(const string& path) { return Replace(path, " ", "\\ "); } -// static -string CLParser::FilterShowIncludes(const string& line, - const string& deps_prefix) { - const string kDepsPrefixEnglish = "Note: including file: "; - const char* in = line.c_str(); - const char* end = in + line.size(); - const string& prefix = deps_prefix.empty() ? kDepsPrefixEnglish : deps_prefix; - if (end - in > (int)prefix.size() && - memcmp(in, prefix.c_str(), (int)prefix.size()) == 0) { - in += prefix.size(); - while (*in == ' ') - ++in; - return line.substr(in - line.c_str()); - } - return ""; -} - -// static -bool CLParser::IsSystemInclude(string path) { - transform(path.begin(), path.end(), path.begin(), ::tolower); - // TODO: this is a heuristic, perhaps there's a better way? - return (path.find("program files") != string::npos || - path.find("microsoft visual studio") != string::npos); -} - -// static -bool CLParser::FilterInputFilename(string line) { - transform(line.begin(), line.end(), line.begin(), ::tolower); - // TODO: other extensions, like .asm? - return EndsWith(line, ".c") || - EndsWith(line, ".cc") || - EndsWith(line, ".cxx") || - EndsWith(line, ".cpp"); -} - -bool CLParser::Parse(const string& output, const string& deps_prefix, - string* filtered_output, string* err) { - // Loop over all lines in the output to process them. - assert(&output != filtered_output); - size_t start = 0; - while (start < output.size()) { - size_t end = output.find_first_of("\r\n", start); - if (end == string::npos) - end = output.size(); - string line = output.substr(start, end - start); - - string include = FilterShowIncludes(line, deps_prefix); - if (!include.empty()) { - string normalized; - if (!IncludesNormalize::Normalize(include, NULL, &normalized, err)) - return false; - if (!IsSystemInclude(normalized)) - includes_.insert(normalized); - } else if (FilterInputFilename(line)) { - // Drop it. - // TODO: if we support compiling multiple output files in a single - // cl.exe invocation, we should stash the filename. - } else { - filtered_output->append(line); - filtered_output->append("\n"); - } - - if (end < output.size() && output[end] == '\r') - ++end; - if (end < output.size() && output[end] == '\n') - ++end; - start = end; - } - - return true; -} - int CLWrapper::Run(const string& command, string* output) { SECURITY_ATTRIBUTES security_attributes = {}; security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); diff --git a/src/msvc_helper.h b/src/msvc_helper.h index 30f87f3..70d1fff 100644 --- a/src/msvc_helper.h +++ b/src/msvc_helper.h @@ -13,42 +13,10 @@ // limitations under the License. #include -#include -#include using namespace std; string EscapeForDepfile(const string& path); -/// Visual Studio's cl.exe requires some massaging to work with Ninja; -/// for example, it emits include information on stderr in a funny -/// format when building with /showIncludes. This class parses this -/// output. -struct CLParser { - /// Parse a line of cl.exe output and extract /showIncludes info. - /// If a dependency is extracted, returns a nonempty string. - /// Exposed for testing. - static string FilterShowIncludes(const string& line, - const string& deps_prefix); - - /// Return true if a mentioned include file is a system path. - /// Filtering these out reduces dependency information considerably. - static bool IsSystemInclude(string path); - - /// Parse a line of cl.exe output and return true if it looks like - /// it's printing an input filename. This is a heuristic but it appears - /// to be the best we can do. - /// Exposed for testing. - static bool FilterInputFilename(string line); - - /// Parse the full output of cl, filling filtered_output with the text that - /// should be printed (if any). Returns true on success, or false with err - /// filled. output must not be the same object as filtered_object. - bool Parse(const string& output, const string& deps_prefix, - string* filtered_output, string* err); - - set includes_; -}; - /// Wraps a synchronous execution of a CL subprocess. struct CLWrapper { CLWrapper() : env_block_(NULL) {} diff --git a/src/msvc_helper_main-win32.cc b/src/msvc_helper_main-win32.cc index 680aaad..e419cd7 100644 --- a/src/msvc_helper_main-win32.cc +++ b/src/msvc_helper_main-win32.cc @@ -19,6 +19,7 @@ #include #include +#include "clparser.h" #include "util.h" #include "getopt.h" diff --git a/src/msvc_helper_test.cc b/src/msvc_helper_test.cc index 49d27c1..eaae51f 100644 --- a/src/msvc_helper_test.cc +++ b/src/msvc_helper_test.cc @@ -17,99 +17,7 @@ #include "test.h" #include "util.h" -TEST(CLParserTest, ShowIncludes) { - ASSERT_EQ("", CLParser::FilterShowIncludes("", "")); - - ASSERT_EQ("", CLParser::FilterShowIncludes("Sample compiler output", "")); - ASSERT_EQ("c:\\Some Files\\foobar.h", - CLParser::FilterShowIncludes("Note: including file: " - "c:\\Some Files\\foobar.h", "")); - ASSERT_EQ("c:\\initspaces.h", - CLParser::FilterShowIncludes("Note: including file: " - "c:\\initspaces.h", "")); - ASSERT_EQ("c:\\initspaces.h", - CLParser::FilterShowIncludes("Non-default prefix: inc file: " - "c:\\initspaces.h", - "Non-default prefix: inc file:")); -} - -TEST(CLParserTest, FilterInputFilename) { - ASSERT_TRUE(CLParser::FilterInputFilename("foobar.cc")); - ASSERT_TRUE(CLParser::FilterInputFilename("foo bar.cc")); - ASSERT_TRUE(CLParser::FilterInputFilename("baz.c")); - ASSERT_TRUE(CLParser::FilterInputFilename("FOOBAR.CC")); - - ASSERT_FALSE(CLParser::FilterInputFilename( - "src\\cl_helper.cc(166) : fatal error C1075: end " - "of file found ...")); -} - -TEST(CLParserTest, ParseSimple) { - CLParser parser; - string output, err; - ASSERT_TRUE(parser.Parse( - "foo\r\n" - "Note: inc file prefix: foo.h\r\n" - "bar\r\n", - "Note: inc file prefix:", &output, &err)); - - ASSERT_EQ("foo\nbar\n", output); - ASSERT_EQ(1u, parser.includes_.size()); - ASSERT_EQ("foo.h", *parser.includes_.begin()); -} - -TEST(CLParserTest, ParseFilenameFilter) { - CLParser parser; - string output, err; - ASSERT_TRUE(parser.Parse( - "foo.cc\r\n" - "cl: warning\r\n", - "", &output, &err)); - ASSERT_EQ("cl: warning\n", output); -} - -TEST(CLParserTest, ParseSystemInclude) { - CLParser parser; - string output, err; - ASSERT_TRUE(parser.Parse( - "Note: including file: c:\\Program Files\\foo.h\r\n" - "Note: including file: d:\\Microsoft Visual Studio\\bar.h\r\n" - "Note: including file: path.h\r\n", - "", &output, &err)); - // We should have dropped the first two includes because they look like - // system headers. - ASSERT_EQ("", output); - ASSERT_EQ(1u, parser.includes_.size()); - ASSERT_EQ("path.h", *parser.includes_.begin()); -} - -TEST(CLParserTest, DuplicatedHeader) { - CLParser parser; - string output, err; - ASSERT_TRUE(parser.Parse( - "Note: including file: foo.h\r\n" - "Note: including file: bar.h\r\n" - "Note: including file: foo.h\r\n", - "", &output, &err)); - // We should have dropped one copy of foo.h. - ASSERT_EQ("", output); - ASSERT_EQ(2u, parser.includes_.size()); -} - -TEST(CLParserTest, DuplicatedHeaderPathConverted) { - CLParser parser; - string output, err; - ASSERT_TRUE(parser.Parse( - "Note: including file: sub/foo.h\r\n" - "Note: including file: bar.h\r\n" - "Note: including file: sub\\foo.h\r\n", - "", &output, &err)); - // We should have dropped one copy of foo.h. - ASSERT_EQ("", output); - ASSERT_EQ(2u, parser.includes_.size()); -} - -TEST(CLParserTest, SpacesInFilename) { +TEST(EscapeForDepfileTest, SpacesInFilename) { ASSERT_EQ("sub\\some\\ sdk\\foo.h", EscapeForDepfile("sub\\some sdk\\foo.h")); } -- cgit v0.12 From 7d21426c56306917ebaf16671a75215672d79755 Mon Sep 17 00:00:00 2001 From: Fredrik Medley Date: Sun, 12 Jul 2015 08:53:19 +0200 Subject: Release the pool slot held by an edge whether it succeeds or fails When an edge finishes building, it should be release from its pool. Make sure that this also is the case when an edge fails to build. The bug can be shown with a pool has size N, then `ninja -k N+1` will still stop after N failing commands for that pool, even if there are many more jobs to be done for that pool: pool mypool depth = 1 rule bad_rule command = false pool = mypool build a : bad_rule build b : bad_rule Current behaviour: $ ninja -k 0 [1/2] false FAILED: false ninja: build stopped: cannot make progress due to previous errors. Expected behaviour: $ ninja -k 0 [1/2] false FAILED: false [2/2] false FAILED: false ninja: build stopped: cannot make progress due to previous errors. Signed-off-by: Fredrik Medley --- src/build.cc | 25 ++++++++++------ src/build.h | 5 ++-- src/build_test.cc | 90 ++++++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 84 insertions(+), 36 deletions(-) diff --git a/src/build.cc b/src/build.cc index 2df5c1a..dae1e18 100644 --- a/src/build.cc +++ b/src/build.cc @@ -382,20 +382,25 @@ void Plan::ScheduleWork(Edge* edge) { } } -void Plan::EdgeFinished(Edge* edge) { +void Plan::EdgeFinished(Edge* edge, bool success) { map::iterator e = want_.find(edge); assert(e != want_.end()); bool directly_wanted = e->second; - if (directly_wanted) - --wanted_edges_; - want_.erase(e); - edge->outputs_ready_ = true; // See if this job frees up any delayed jobs. if (directly_wanted) edge->pool()->EdgeFinished(*edge); edge->pool()->RetrieveReadyEdges(&ready_); + // The rest of this function only applies to successful commands. + if (!success) + return; + + if (directly_wanted) + --wanted_edges_; + want_.erase(e); + edge->outputs_ready_ = true; + // Check off any nodes we were waiting for with this edge. for (vector::iterator o = edge->outputs_.begin(); o != edge->outputs_.end(); ++o) { @@ -418,7 +423,7 @@ void Plan::NodeFinished(Node* node) { } else { // We do not need to build this edge, but we might need to build one of // its dependents. - EdgeFinished(*oe); + EdgeFinished(*oe, true); } } } @@ -651,7 +656,7 @@ bool Builder::Build(string* err) { } if (edge->is_phony()) { - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); } else { ++pending_commands; } @@ -770,8 +775,10 @@ bool Builder::FinishCommand(CommandRunner::Result* result, string* err) { &start_time, &end_time); // The rest of this function only applies to successful commands. - if (!result->success()) + if (!result->success()) { + plan_.EdgeFinished(edge, false); return true; + } // Restat the edge outputs, if necessary. TimeStamp restat_mtime = 0; @@ -820,7 +827,7 @@ bool Builder::FinishCommand(CommandRunner::Result* result, string* err) { } } - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); // Delete any left over response file. string rspfile = edge->GetUnescapedRspfile(); diff --git a/src/build.h b/src/build.h index 8106faa..3a7183f 100644 --- a/src/build.h +++ b/src/build.h @@ -56,9 +56,8 @@ struct Plan { /// Dumps the current state of the plan. void Dump(); - /// Mark an edge as done building. Used internally and by - /// tests. - void EdgeFinished(Edge* edge); + /// Mark an edge as done building (whether it succeeded or failed). + void EdgeFinished(Edge* edge, bool success); /// Clean the given node during the build. /// Return false on error. diff --git a/src/build_test.cc b/src/build_test.cc index 7c6060d..261268a 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -68,14 +68,14 @@ TEST_F(PlanTest, Basic) { ASSERT_FALSE(plan_.FindWork()); - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); edge = plan_.FindWork(); ASSERT_TRUE(edge); ASSERT_EQ("mid", edge->inputs_[0]->path()); ASSERT_EQ("out", edge->outputs_[0]->path()); - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); ASSERT_FALSE(plan_.more_to_do()); edge = plan_.FindWork(); @@ -99,11 +99,11 @@ TEST_F(PlanTest, DoubleOutputDirect) { Edge* edge; edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat in - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat mid1 mid2 - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); edge = plan_.FindWork(); ASSERT_FALSE(edge); // done @@ -129,19 +129,19 @@ TEST_F(PlanTest, DoubleOutputIndirect) { Edge* edge; edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat in - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat a1 - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat a2 - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat b1 b2 - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); edge = plan_.FindWork(); ASSERT_FALSE(edge); // done @@ -167,19 +167,19 @@ TEST_F(PlanTest, DoubleDependent) { Edge* edge; edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat in - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat mid - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat mid - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat a1 a2 - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); edge = plan_.FindWork(); ASSERT_FALSE(edge); // done @@ -257,7 +257,7 @@ void PlanTest::TestPoolWithDepthOne(const char* test_case) { // This will be false since poolcat is serialized ASSERT_FALSE(plan_.FindWork()); - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); edge = plan_.FindWork(); ASSERT_TRUE(edge); @@ -266,7 +266,7 @@ void PlanTest::TestPoolWithDepthOne(const char* test_case) { ASSERT_FALSE(plan_.FindWork()); - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); ASSERT_FALSE(plan_.more_to_do()); edge = plan_.FindWork(); @@ -342,7 +342,7 @@ TEST_F(PlanTest, PoolsWithDepthTwo) { ASSERT_EQ("outb3", edge->outputs_[0]->path()); // finish out1 - plan_.EdgeFinished(edges.front()); + plan_.EdgeFinished(edges.front(), true); edges.pop_front(); // out3 should be available @@ -353,19 +353,19 @@ TEST_F(PlanTest, PoolsWithDepthTwo) { ASSERT_FALSE(plan_.FindWork()); - plan_.EdgeFinished(out3); + plan_.EdgeFinished(out3, true); ASSERT_FALSE(plan_.FindWork()); for (deque::iterator it = edges.begin(); it != edges.end(); ++it) { - plan_.EdgeFinished(*it); + plan_.EdgeFinished(*it, true); } Edge* last = plan_.FindWork(); ASSERT_TRUE(last); ASSERT_EQ("allTheThings", last->outputs_[0]->path()); - plan_.EdgeFinished(last); + plan_.EdgeFinished(last, true); ASSERT_FALSE(plan_.more_to_do()); ASSERT_FALSE(plan_.FindWork()); @@ -407,7 +407,7 @@ TEST_F(PlanTest, PoolWithRedundantEdges) { edge = initial_edges[1]; // Foo first ASSERT_EQ("foo.cpp", edge->outputs_[0]->path()); - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); edge = plan_.FindWork(); ASSERT_TRUE(edge); @@ -415,11 +415,11 @@ TEST_F(PlanTest, PoolWithRedundantEdges) { ASSERT_EQ("foo.cpp", edge->inputs_[0]->path()); ASSERT_EQ("foo.cpp", edge->inputs_[1]->path()); ASSERT_EQ("foo.cpp.obj", edge->outputs_[0]->path()); - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); edge = initial_edges[0]; // Now for bar ASSERT_EQ("bar.cpp", edge->outputs_[0]->path()); - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); edge = plan_.FindWork(); ASSERT_TRUE(edge); @@ -427,7 +427,7 @@ TEST_F(PlanTest, PoolWithRedundantEdges) { ASSERT_EQ("bar.cpp", edge->inputs_[0]->path()); ASSERT_EQ("bar.cpp", edge->inputs_[1]->path()); ASSERT_EQ("bar.cpp.obj", edge->outputs_[0]->path()); - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); edge = plan_.FindWork(); ASSERT_TRUE(edge); @@ -435,20 +435,62 @@ TEST_F(PlanTest, PoolWithRedundantEdges) { ASSERT_EQ("foo.cpp.obj", edge->inputs_[0]->path()); ASSERT_EQ("bar.cpp.obj", edge->inputs_[1]->path()); ASSERT_EQ("libfoo.a", edge->outputs_[0]->path()); - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); edge = plan_.FindWork(); ASSERT_TRUE(edge); ASSERT_FALSE(plan_.FindWork()); ASSERT_EQ("libfoo.a", edge->inputs_[0]->path()); ASSERT_EQ("all", edge->outputs_[0]->path()); - plan_.EdgeFinished(edge); + plan_.EdgeFinished(edge, true); edge = plan_.FindWork(); ASSERT_FALSE(edge); ASSERT_FALSE(plan_.more_to_do()); } +TEST_F(PlanTest, PoolWithFailingEdge) { + 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, false); + + 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, false); + + ASSERT_TRUE(plan_.more_to_do()); // Jobs have failed + edge = plan_.FindWork(); + ASSERT_EQ(0, edge); +} + /// Fake implementation of CommandRunner, useful for tests. struct FakeCommandRunner : public CommandRunner { explicit FakeCommandRunner(VirtualFileSystem* fs) : -- cgit v0.12 From ff6eedb968ddd6f0f9ba252a31e5a77967edddb5 Mon Sep 17 00:00:00 2001 From: David Emett Date: Sun, 20 Sep 2015 14:43:01 +0100 Subject: Add another test case covering pool release on edge failure With this build file: pool failpool depth = 1 rule fail command = fail pool = failpool build out1: fail build out2: fail build out3: fail build final: phony out1 out2 out3 Running `ninja -k 0` should run out1..3 sequentially before failing, but until recently we would fail after just running out1. Add a test covering this case. --- src/build_test.cc | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/build_test.cc b/src/build_test.cc index 261268a..90d500c 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -1153,6 +1153,30 @@ TEST_F(BuildTest, SwallowFailuresLimit) { ASSERT_EQ("cannot make progress due to previous errors", err); } +TEST_F(BuildTest, SwallowFailuresPool) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, +"pool failpool\n" +" depth = 1\n" +"rule fail\n" +" command = fail\n" +" pool = failpool\n" +"build out1: fail\n" +"build out2: fail\n" +"build out3: fail\n" +"build final: cat out1 out2 out3\n")); + + // Swallow ten failures; we should stop before building final. + config_.failures_allowed = 11; + + string err; + EXPECT_TRUE(builder_.AddTarget("final", &err)); + ASSERT_EQ("", err); + + EXPECT_FALSE(builder_.Build(&err)); + ASSERT_EQ(3u, command_runner_.commands_ran_.size()); + ASSERT_EQ("cannot make progress due to previous errors", err); +} + TEST_F(BuildTest, PoolEdgesReadyButNotWanted) { fs_.Create("x", ""); -- cgit v0.12 From 50d85fc61dfe34a757fe5689327bc072783b8d22 Mon Sep 17 00:00:00 2001 From: James Johnston Date: Mon, 18 Apr 2016 20:25:40 +0000 Subject: ninja_test: Fix Visual C++ 2015 warnings/errors about name hiding. Visual C++ 2015 warns if a local variable hides visibility of another variable in a higher scope. Since this project declares warnings as errors, ninja_test simply won't build on Visual C++ 2015. The variables have been renamed and scope limited as appropriate, so that ninja_test will build without error now on Visual C++ 2015. --- src/build_log_test.cc | 16 +++-- src/deps_log_test.cc | 10 +-- src/manifest_parser_test.cc | 156 ++++++++++++++++++++++---------------------- src/test.h | 16 ++--- 4 files changed, 101 insertions(+), 97 deletions(-) diff --git a/src/build_log_test.cc b/src/build_log_test.cc index 2c41ba6..f4c9044 100644 --- a/src/build_log_test.cc +++ b/src/build_log_test.cc @@ -121,13 +121,15 @@ TEST_F(BuildLogTest, Truncate) { "build out: cat mid\n" "build mid: cat in\n"); - BuildLog log1; - string err; - EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err)); - ASSERT_EQ("", err); - log1.RecordCommand(state_.edges_[0], 15, 18); - log1.RecordCommand(state_.edges_[1], 20, 25); - log1.Close(); + { + BuildLog log1; + string err; + EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err)); + ASSERT_EQ("", err); + log1.RecordCommand(state_.edges_[0], 15, 18); + log1.RecordCommand(state_.edges_[1], 20, 25); + log1.Close(); + } struct stat statbuf; ASSERT_EQ(0, stat(kTestFilename, &statbuf)); diff --git a/src/deps_log_test.cc b/src/deps_log_test.cc index cab06fb..89f7be1 100644 --- a/src/deps_log_test.cc +++ b/src/deps_log_test.cc @@ -431,10 +431,12 @@ TEST_F(DepsLogTest, TruncatedRecovery) { } // Shorten the file, corrupting the last record. - struct stat st; - ASSERT_EQ(0, stat(kTestFilename, &st)); - string err; - ASSERT_TRUE(Truncate(kTestFilename, st.st_size - 2, &err)); + { + struct stat st; + ASSERT_EQ(0, stat(kTestFilename, &st)); + string err; + ASSERT_TRUE(Truncate(kTestFilename, st.st_size - 2, &err)); + } // Load the file again, add an entry. { diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc index e8c1907..ba83a67 100644 --- a/src/manifest_parser_test.cc +++ b/src/manifest_parser_test.cc @@ -390,8 +390,8 @@ TEST_F(ParserTest, ReservedWords) { TEST_F(ParserTest, Errors) { { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest(string("subn", 4), &err)); EXPECT_EQ("input:1: expected '=', got eof\n" @@ -401,8 +401,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("foobar", &err)); EXPECT_EQ("input:1: expected '=', got eof\n" @@ -412,8 +412,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("x 3", &err)); EXPECT_EQ("input:1: expected '=', got identifier\n" @@ -423,8 +423,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("x = 3", &err)); EXPECT_EQ("input:1: unexpected EOF\n" @@ -434,8 +434,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("x = 3\ny 2", &err)); EXPECT_EQ("input:2: expected '=', got identifier\n" @@ -445,8 +445,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("x = $", &err)); EXPECT_EQ("input:1: bad $-escape (literal $ must be written as $$)\n" @@ -456,8 +456,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("x = $\n $[\n", &err)); EXPECT_EQ("input:2: bad $-escape (literal $ must be written as $$)\n" @@ -467,8 +467,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("x = a$\n b$\n $\n", &err)); EXPECT_EQ("input:4: unexpected EOF\n" @@ -476,8 +476,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("build\n", &err)); EXPECT_EQ("input:1: expected path\n" @@ -487,8 +487,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("build x: y z\n", &err)); EXPECT_EQ("input:1: unknown build rule 'y'\n" @@ -498,8 +498,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("build x:: y z\n", &err)); EXPECT_EQ("input:1: expected build command name\n" @@ -509,8 +509,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n command = cat ok\n" "build x: cat $\n :\n", @@ -522,8 +522,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n", &err)); @@ -531,8 +531,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n" " command = echo\n" @@ -545,8 +545,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n" " command = echo\n" @@ -557,8 +557,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n" " command = ${fafsd\n" @@ -572,8 +572,8 @@ TEST_F(ParserTest, Errors) { { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n" " command = cat\n" @@ -587,8 +587,8 @@ TEST_F(ParserTest, Errors) { { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cat\n" " command = cat\n" @@ -601,8 +601,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule %foo\n", &err)); @@ -610,8 +610,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cc\n" " command = foo\n" @@ -624,8 +624,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n" "build $.: cc bar.cc\n", @@ -637,8 +637,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n && bar", &err)); @@ -646,8 +646,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n" "build $: cc bar.cc\n", @@ -659,8 +659,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("default\n", &err)); @@ -671,8 +671,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("default nonexistent\n", &err)); @@ -683,8 +683,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule r\n command = r\n" "build b: r\n" @@ -697,8 +697,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("default $a\n", &err)); EXPECT_EQ("input:1: empty path\n" @@ -708,8 +708,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule r\n" " command = r\n" @@ -720,8 +720,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; // the indented blank line must terminate the rule // this also verifies that "unexpected (token)" errors are correct @@ -733,24 +733,24 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("pool\n", &err)); EXPECT_EQ("input:1: expected pool name\n", err); } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("pool foo\n", &err)); EXPECT_EQ("input:2: expected 'depth =' line\n", err); } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("pool foo\n" " depth = 4\n" @@ -762,8 +762,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("pool foo\n" " depth = -1\n", &err)); @@ -774,8 +774,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("pool foo\n" " bar = 1\n", &err)); @@ -786,8 +786,8 @@ TEST_F(ParserTest, Errors) { } { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; // Pool names are dereferenced at edge parsing time. EXPECT_FALSE(parser.ParseTest("rule run\n" @@ -799,16 +799,16 @@ TEST_F(ParserTest, Errors) { } TEST_F(ParserTest, MissingInput) { - State state; - ManifestParser parser(&state, &fs_, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, &fs_, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.Load("build.ninja", &err)); EXPECT_EQ("loading 'build.ninja': No such file or directory", err); } TEST_F(ParserTest, MultipleOutputs) { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_TRUE(parser.ParseTest("rule cc\n command = foo\n depfile = bar\n" "build a.o b.o: cc c.cc\n", @@ -817,8 +817,8 @@ TEST_F(ParserTest, MultipleOutputs) { } TEST_F(ParserTest, MultipleOutputsWithDeps) { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n deps = gcc\n" "build a.o b.o: cc c.cc\n", @@ -1012,8 +1012,8 @@ TEST_F(ParserTest, UTF8) { } TEST_F(ParserTest, CRLF) { - State state; - ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); + State local_state; + ManifestParser parser(&local_state, NULL, kDupeEdgeActionWarn); string err; EXPECT_TRUE(parser.ParseTest("# comment with crlf\r\n", &err)); diff --git a/src/test.h b/src/test.h index 488c243..02ed929 100644 --- a/src/test.h +++ b/src/test.h @@ -93,14 +93,14 @@ extern testing::Test* g_current_test; if (!EXPECT_TRUE(a)) { g_current_test->AddAssertionFailure(); return; } #define ASSERT_FALSE(a) \ if (!EXPECT_FALSE(a)) { g_current_test->AddAssertionFailure(); return; } -#define ASSERT_NO_FATAL_FAILURE(a) \ - { \ - int f = g_current_test->AssertionFailures(); \ - a; \ - if (f != g_current_test->AssertionFailures()) { \ - g_current_test->AddAssertionFailure(); \ - return; \ - } \ +#define ASSERT_NO_FATAL_FAILURE(a) \ + { \ + int fail_count = g_current_test->AssertionFailures(); \ + a; \ + if (fail_count != g_current_test->AssertionFailures()) { \ + g_current_test->AddAssertionFailure(); \ + return; \ + } \ } // Support utilites for tests. -- cgit v0.12 From 87b57e88755f130159b759b8a9954c362bc463d4 Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 19 Apr 2016 15:57:49 -0400 Subject: Improve Plan::EdgeFinished signature Use an enumeration instead of a boolean to clarify the purpose of arguments at call sites. Suggested-by: Nico Weber --- src/build.cc | 12 ++++++------ src/build.h | 7 ++++++- src/build_test.cc | 52 ++++++++++++++++++++++++++-------------------------- 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/build.cc b/src/build.cc index 0971a87..3a17bdb 100644 --- a/src/build.cc +++ b/src/build.cc @@ -385,7 +385,7 @@ void Plan::ScheduleWork(Edge* edge) { } } -void Plan::EdgeFinished(Edge* edge, bool success) { +void Plan::EdgeFinished(Edge* edge, EdgeResult result) { map::iterator e = want_.find(edge); assert(e != want_.end()); bool directly_wanted = e->second; @@ -396,7 +396,7 @@ void Plan::EdgeFinished(Edge* edge, bool success) { edge->pool()->RetrieveReadyEdges(&ready_); // The rest of this function only applies to successful commands. - if (!success) + if (result != kEdgeSucceeded) return; if (directly_wanted) @@ -426,7 +426,7 @@ void Plan::NodeFinished(Node* node) { } else { // We do not need to build this edge, but we might need to build one of // its dependents. - EdgeFinished(*oe, true); + EdgeFinished(*oe, kEdgeSucceeded); } } } @@ -659,7 +659,7 @@ bool Builder::Build(string* err) { } if (edge->is_phony()) { - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); } else { ++pending_commands; } @@ -779,7 +779,7 @@ bool Builder::FinishCommand(CommandRunner::Result* result, string* err) { // The rest of this function only applies to successful commands. if (!result->success()) { - plan_.EdgeFinished(edge, false); + plan_.EdgeFinished(edge, Plan::kEdgeFailed); return true; } @@ -830,7 +830,7 @@ bool Builder::FinishCommand(CommandRunner::Result* result, string* err) { } } - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); // Delete any left over response file. string rspfile = edge->GetUnescapedRspfile(); diff --git a/src/build.h b/src/build.h index 3a7183f..51589ef 100644 --- a/src/build.h +++ b/src/build.h @@ -56,8 +56,13 @@ struct Plan { /// Dumps the current state of the plan. void Dump(); + enum EdgeResult { + kEdgeFailed, + kEdgeSucceeded + }; + /// Mark an edge as done building (whether it succeeded or failed). - void EdgeFinished(Edge* edge, bool success); + void EdgeFinished(Edge* edge, EdgeResult result); /// Clean the given node during the build. /// Return false on error. diff --git a/src/build_test.cc b/src/build_test.cc index 32f2690..55d093e 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -68,14 +68,14 @@ TEST_F(PlanTest, Basic) { ASSERT_FALSE(plan_.FindWork()); - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); edge = plan_.FindWork(); ASSERT_TRUE(edge); ASSERT_EQ("mid", edge->inputs_[0]->path()); ASSERT_EQ("out", edge->outputs_[0]->path()); - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); ASSERT_FALSE(plan_.more_to_do()); edge = plan_.FindWork(); @@ -99,11 +99,11 @@ TEST_F(PlanTest, DoubleOutputDirect) { Edge* edge; edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat in - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat mid1 mid2 - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); edge = plan_.FindWork(); ASSERT_FALSE(edge); // done @@ -129,19 +129,19 @@ TEST_F(PlanTest, DoubleOutputIndirect) { Edge* edge; edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat in - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat a1 - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat a2 - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat b1 b2 - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); edge = plan_.FindWork(); ASSERT_FALSE(edge); // done @@ -167,19 +167,19 @@ TEST_F(PlanTest, DoubleDependent) { Edge* edge; edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat in - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat mid - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat mid - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); edge = plan_.FindWork(); ASSERT_TRUE(edge); // cat a1 a2 - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); edge = plan_.FindWork(); ASSERT_FALSE(edge); // done @@ -257,7 +257,7 @@ void PlanTest::TestPoolWithDepthOne(const char* test_case) { // This will be false since poolcat is serialized ASSERT_FALSE(plan_.FindWork()); - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); edge = plan_.FindWork(); ASSERT_TRUE(edge); @@ -266,7 +266,7 @@ void PlanTest::TestPoolWithDepthOne(const char* test_case) { ASSERT_FALSE(plan_.FindWork()); - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); ASSERT_FALSE(plan_.more_to_do()); edge = plan_.FindWork(); @@ -342,7 +342,7 @@ TEST_F(PlanTest, PoolsWithDepthTwo) { ASSERT_EQ("outb3", edge->outputs_[0]->path()); // finish out1 - plan_.EdgeFinished(edges.front(), true); + plan_.EdgeFinished(edges.front(), Plan::kEdgeSucceeded); edges.pop_front(); // out3 should be available @@ -353,19 +353,19 @@ TEST_F(PlanTest, PoolsWithDepthTwo) { ASSERT_FALSE(plan_.FindWork()); - plan_.EdgeFinished(out3, true); + plan_.EdgeFinished(out3, Plan::kEdgeSucceeded); ASSERT_FALSE(plan_.FindWork()); for (deque::iterator it = edges.begin(); it != edges.end(); ++it) { - plan_.EdgeFinished(*it, true); + plan_.EdgeFinished(*it, Plan::kEdgeSucceeded); } Edge* last = plan_.FindWork(); ASSERT_TRUE(last); ASSERT_EQ("allTheThings", last->outputs_[0]->path()); - plan_.EdgeFinished(last, true); + plan_.EdgeFinished(last, Plan::kEdgeSucceeded); ASSERT_FALSE(plan_.more_to_do()); ASSERT_FALSE(plan_.FindWork()); @@ -407,7 +407,7 @@ TEST_F(PlanTest, PoolWithRedundantEdges) { edge = initial_edges[1]; // Foo first ASSERT_EQ("foo.cpp", edge->outputs_[0]->path()); - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); edge = plan_.FindWork(); ASSERT_TRUE(edge); @@ -415,11 +415,11 @@ TEST_F(PlanTest, PoolWithRedundantEdges) { ASSERT_EQ("foo.cpp", edge->inputs_[0]->path()); ASSERT_EQ("foo.cpp", edge->inputs_[1]->path()); ASSERT_EQ("foo.cpp.obj", edge->outputs_[0]->path()); - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); edge = initial_edges[0]; // Now for bar ASSERT_EQ("bar.cpp", edge->outputs_[0]->path()); - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); edge = plan_.FindWork(); ASSERT_TRUE(edge); @@ -427,7 +427,7 @@ TEST_F(PlanTest, PoolWithRedundantEdges) { ASSERT_EQ("bar.cpp", edge->inputs_[0]->path()); ASSERT_EQ("bar.cpp", edge->inputs_[1]->path()); ASSERT_EQ("bar.cpp.obj", edge->outputs_[0]->path()); - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); edge = plan_.FindWork(); ASSERT_TRUE(edge); @@ -435,14 +435,14 @@ TEST_F(PlanTest, PoolWithRedundantEdges) { ASSERT_EQ("foo.cpp.obj", edge->inputs_[0]->path()); ASSERT_EQ("bar.cpp.obj", edge->inputs_[1]->path()); ASSERT_EQ("libfoo.a", edge->outputs_[0]->path()); - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); edge = plan_.FindWork(); ASSERT_TRUE(edge); ASSERT_FALSE(plan_.FindWork()); ASSERT_EQ("libfoo.a", edge->inputs_[0]->path()); ASSERT_EQ("all", edge->outputs_[0]->path()); - plan_.EdgeFinished(edge, true); + plan_.EdgeFinished(edge, Plan::kEdgeSucceeded); edge = plan_.FindWork(); ASSERT_FALSE(edge); @@ -475,7 +475,7 @@ TEST_F(PlanTest, PoolWithFailingEdge) { // This will be false since poolcat is serialized ASSERT_FALSE(plan_.FindWork()); - plan_.EdgeFinished(edge, false); + plan_.EdgeFinished(edge, Plan::kEdgeFailed); edge = plan_.FindWork(); ASSERT_TRUE(edge); @@ -484,7 +484,7 @@ TEST_F(PlanTest, PoolWithFailingEdge) { ASSERT_FALSE(plan_.FindWork()); - plan_.EdgeFinished(edge, false); + plan_.EdgeFinished(edge, Plan::kEdgeFailed); ASSERT_TRUE(plan_.more_to_do()); // Jobs have failed edge = plan_.FindWork(); -- cgit v0.12 From bff4010e561b7d5f366262ef3d1415dcd50a3d94 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 7 Mar 2016 13:37:11 -0800 Subject: Pass location of build.ninja file to browse script Pass the value of -f to the browse python script so it can be passed back to ninja -t query. --- src/browse.cc | 4 +++- src/browse.h | 2 +- src/browse.py | 6 ++++-- src/ninja.cc | 54 +++++++++++++++++++++++++++--------------------------- 4 files changed, 35 insertions(+), 31 deletions(-) diff --git a/src/browse.cc b/src/browse.cc index 46434d7..14900f8 100644 --- a/src/browse.cc +++ b/src/browse.cc @@ -22,7 +22,7 @@ #include "build/browse_py.h" void RunBrowsePython(State* state, const char* ninja_command, - int argc, char* argv[]) { + const char* input_file, int argc, char* argv[]) { // Fork off a Python process and have it run our code via its stdin. // (Actually the Python process becomes the parent.) int pipefd[2]; @@ -50,6 +50,8 @@ void RunBrowsePython(State* state, const char* ninja_command, command.push_back("-"); command.push_back("--ninja-command"); command.push_back(ninja_command); + command.push_back("-f"); + command.push_back(input_file); for (int i = 0; i < argc; i++) { command.push_back(argv[i]); } diff --git a/src/browse.h b/src/browse.h index 842c6ff..8d6d285 100644 --- a/src/browse.h +++ b/src/browse.h @@ -23,6 +23,6 @@ struct State; /// \a argv are arguments to be passed to the Python script. /// This function does not return if it runs successfully. void RunBrowsePython(State* state, const char* ninja_command, - int argc, char* argv[]); + const char* input_file, int argc, char* argv[]); #endif // NINJA_BROWSE_H_ diff --git a/src/browse.py b/src/browse.py index 63c60aa..32792f3 100755 --- a/src/browse.py +++ b/src/browse.py @@ -150,8 +150,8 @@ def generate_html(node): return '\n'.join(document) def ninja_dump(target): - proc = subprocess.Popen([args.ninja_command, '-t', 'query', target], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, + cmd = [args.ninja_command, '-f', args.f, '-t', 'query', target] + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) return proc.communicate() + (proc.returncode,) @@ -194,6 +194,8 @@ parser.add_argument('--no-browser', action='store_true', parser.add_argument('--ninja-command', default='ninja', help='Path to ninja binary (default %(default)s)') +parser.add_argument('-f', default='build.ninja', + help='Path to build.ninja file (default %(default)s)') parser.add_argument('initial_target', default='all', nargs='?', help='Initial target to show (default %(default)s)') diff --git a/src/ninja.cc b/src/ninja.cc index b3b9bed..25eafe8 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -97,7 +97,7 @@ struct NinjaMain : public BuildLogUser { DepsLog deps_log_; /// The type of functions that are the entry points to tools (subcommands). - typedef int (NinjaMain::*ToolFunc)(int, char**); + typedef int (NinjaMain::*ToolFunc)(const Options*, int, char**); /// Get the Node for a given command-line path, handling features like /// spell correction. @@ -108,17 +108,17 @@ struct NinjaMain : public BuildLogUser { vector* targets, string* err); // The various subcommands, run via "-t XXX". - int ToolGraph(int argc, char* argv[]); - int ToolQuery(int argc, char* argv[]); - int ToolDeps(int argc, char* argv[]); - int ToolBrowse(int argc, char* argv[]); - int ToolMSVC(int argc, char* argv[]); - int ToolTargets(int argc, char* argv[]); - int ToolCommands(int argc, char* argv[]); - int ToolClean(int argc, char* argv[]); - int ToolCompilationDatabase(int argc, char* argv[]); - int ToolRecompact(int argc, char* argv[]); - int ToolUrtle(int argc, char** argv); + int ToolGraph(const Options* options, int argc, char* argv[]); + int ToolQuery(const Options* options, int argc, char* argv[]); + int ToolDeps(const Options* options, int argc, char* argv[]); + int ToolBrowse(const Options* options, int argc, char* argv[]); + int ToolMSVC(const Options* options, int argc, char* argv[]); + int ToolTargets(const Options* options, int argc, char* argv[]); + int ToolCommands(const Options* options, int argc, char* argv[]); + int ToolClean(const Options* options, int argc, char* argv[]); + int ToolCompilationDatabase(const Options* options, int argc, char* argv[]); + int ToolRecompact(const Options* options, int argc, char* argv[]); + int ToolUrtle(const Options* options, int argc, char** argv); /// Open the build log. /// @return false on error. @@ -314,7 +314,7 @@ bool NinjaMain::CollectTargetsFromArgs(int argc, char* argv[], return true; } -int NinjaMain::ToolGraph(int argc, char* argv[]) { +int NinjaMain::ToolGraph(const Options* options, int argc, char* argv[]) { vector nodes; string err; if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) { @@ -331,7 +331,7 @@ int NinjaMain::ToolGraph(int argc, char* argv[]) { return 0; } -int NinjaMain::ToolQuery(int argc, char* argv[]) { +int NinjaMain::ToolQuery(const Options* options, int argc, char* argv[]) { if (argc == 0) { Error("expected a target to query"); return 1; @@ -370,15 +370,15 @@ int NinjaMain::ToolQuery(int argc, char* argv[]) { } #if defined(NINJA_HAVE_BROWSE) -int NinjaMain::ToolBrowse(int argc, char* argv[]) { - RunBrowsePython(&state_, ninja_command_, argc, argv); +int NinjaMain::ToolBrowse(const Options* options, int argc, char* argv[]) { + RunBrowsePython(&state_, ninja_command_, options->input_file, argc, argv); // If we get here, the browse failed. return 1; } #endif // _WIN32 #if defined(_MSC_VER) -int NinjaMain::ToolMSVC(int argc, char* argv[]) { +int NinjaMain::ToolMSVC(const Options* options, int argc, char* argv[]) { // Reset getopt: push one argument onto the front of argv, reset optind. argc++; argv--; @@ -453,7 +453,7 @@ int ToolTargetsList(State* state) { return 0; } -int NinjaMain::ToolDeps(int argc, char** argv) { +int NinjaMain::ToolDeps(const Options* options, int argc, char** argv) { vector nodes; if (argc == 0) { for (vector::const_iterator ni = deps_log_.nodes().begin(); @@ -493,7 +493,7 @@ int NinjaMain::ToolDeps(int argc, char** argv) { return 0; } -int NinjaMain::ToolTargets(int argc, char* argv[]) { +int NinjaMain::ToolTargets(const Options* options, int argc, char* argv[]) { int depth = 1; if (argc >= 1) { string mode = argv[0]; @@ -547,7 +547,7 @@ void PrintCommands(Edge* edge, set* seen) { puts(edge->EvaluateCommand().c_str()); } -int NinjaMain::ToolCommands(int argc, char* argv[]) { +int NinjaMain::ToolCommands(const Options* options, int argc, char* argv[]) { vector nodes; string err; if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) { @@ -562,7 +562,7 @@ int NinjaMain::ToolCommands(int argc, char* argv[]) { return 0; } -int NinjaMain::ToolClean(int argc, char* argv[]) { +int NinjaMain::ToolClean(const Options* options, int argc, char* argv[]) { // The clean tool uses getopt, and expects argv[0] to contain the name of // the tool, i.e. "clean". argc++; @@ -620,7 +620,7 @@ void EncodeJSONString(const char *str) { } } -int NinjaMain::ToolCompilationDatabase(int argc, char* argv[]) { +int NinjaMain::ToolCompilationDatabase(const Options* options, int argc, char* argv[]) { bool first = true; vector cwd; @@ -660,7 +660,7 @@ int NinjaMain::ToolCompilationDatabase(int argc, char* argv[]) { return 0; } -int NinjaMain::ToolRecompact(int argc, char* argv[]) { +int NinjaMain::ToolRecompact(const Options* options, int argc, char* argv[]) { if (!EnsureBuildDirExists()) return 1; @@ -671,7 +671,7 @@ int NinjaMain::ToolRecompact(int argc, char* argv[]) { return 0; } -int NinjaMain::ToolUrtle(int argc, char** argv) { +int NinjaMain::ToolUrtle(const Options* options, int argc, char** argv) { // RLE encoded. const char* urtle = " 13 ,3;2!2;\n8 ,;<11!;\n5 `'<10!(2`'2!\n11 ,6;, `\\. `\\9 .,c13$ec,.\n6 " @@ -1097,7 +1097,7 @@ int real_main(int argc, char** argv) { // None of the RUN_AFTER_FLAGS actually use a NinjaMain, but it's needed // by other tools. NinjaMain ninja(ninja_command, config); - return (ninja.*options.tool->func)(argc, argv); + return (ninja.*options.tool->func)(&options, argc, argv); } // Limit number of rebuilds, to prevent infinite loops. @@ -1116,7 +1116,7 @@ int real_main(int argc, char** argv) { } if (options.tool && options.tool->when == Tool::RUN_AFTER_LOAD) - return (ninja.*options.tool->func)(argc, argv); + return (ninja.*options.tool->func)(&options, argc, argv); if (!ninja.EnsureBuildDirExists()) return 1; @@ -1125,7 +1125,7 @@ int real_main(int argc, char** argv) { return 1; if (options.tool && options.tool->when == Tool::RUN_AFTER_LOGS) - return (ninja.*options.tool->func)(argc, argv); + return (ninja.*options.tool->func)(&options, argc, argv); // Attempt to rebuild the manifest before building anything else if (ninja.RebuildManifest(options.input_file, &err)) { -- cgit v0.12 From d4607eb8974a0fcd034ba6d93624544815788ff2 Mon Sep 17 00:00:00 2001 From: Dan Willemsen Date: Wed, 20 Apr 2016 17:29:00 -0700 Subject: Add includes for clang++/libc++ on Ubuntu There are a number of stdlib.h uses in these files without including stdlib.h: hash_collision_bench.cc: rand, RAND_MAX, srand manifest_parser_perftest.cc: system, exit ninja_test.cc: EXIT_SUCCESS, EXIT_FAILURE test.cc: getenv, mkdtemp, system This works on a Ubuntu g++/libstdc++ build, as the header pulls in stdlib.h, and on a OSX clang++/libc++ build the and headers pull in stdlib.h. But a Ubuntu clang++/libc++ build does not pull in stdlib.h with any of these other headers. $ apt-get install clang-3.6 libc++-dev $ CXX=clang++-3.6 CFLAGS=-stdlib=libc++ LDFLAGS=-stdlib=libc++ \ ./configure.py $ ninja ninja_test hash_collision_bench manifest_parser_perftest This was originally discovered using the host toolchain provided with Android, but the Ubuntu version is much easier to reproduce. --- src/hash_collision_bench.cc | 1 + src/manifest_parser_perftest.cc | 1 + src/ninja_test.cc | 1 + src/test.cc | 1 + 4 files changed, 4 insertions(+) diff --git a/src/hash_collision_bench.cc b/src/hash_collision_bench.cc index 5be0531..ff947dc 100644 --- a/src/hash_collision_bench.cc +++ b/src/hash_collision_bench.cc @@ -17,6 +17,7 @@ #include using namespace std; +#include #include int random(int low, int high) { diff --git a/src/manifest_parser_perftest.cc b/src/manifest_parser_perftest.cc index 572e2d5..60c2054 100644 --- a/src/manifest_parser_perftest.cc +++ b/src/manifest_parser_perftest.cc @@ -19,6 +19,7 @@ #include #include +#include #include #ifdef _WIN32 diff --git a/src/ninja_test.cc b/src/ninja_test.cc index 11087b6..d642c5c 100644 --- a/src/ninja_test.cc +++ b/src/ninja_test.cc @@ -14,6 +14,7 @@ #include #include +#include #ifdef _WIN32 #include "getopt.h" diff --git a/src/test.cc b/src/test.cc index 53bfc48..51882f0 100644 --- a/src/test.cc +++ b/src/test.cc @@ -21,6 +21,7 @@ #include #include +#include #ifdef _WIN32 #include #else -- cgit v0.12 From 29f373a66bb9d3c56a1d4cf476832c12b7e2e1ad Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Tue, 26 Apr 2016 21:55:20 -0400 Subject: Fix crash with duplicate implicit outputs. Sadly, duplicate outputs aren't an error by default in Ninja (see also a new edge has no effect. Remember to decrement the "number of implicit outputs" counter for the new edge when this happens. Fixes #1136. --- src/manifest_parser.cc | 18 +++++++++++------- src/manifest_parser_test.cc | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc index be267a3..a4f489e 100644 --- a/src/manifest_parser.cc +++ b/src/manifest_parser.cc @@ -339,8 +339,8 @@ bool ManifestParser::ParseEdge(string* err) { } edge->outputs_.reserve(outs.size()); - for (vector::iterator i = outs.begin(); i != outs.end(); ++i) { - string path = i->Evaluate(env); + for (size_t i = 0, e = outs.size(); i != e; ++i) { + string path = outs[i].Evaluate(env); string path_err; unsigned int slash_bits; if (!CanonicalizePath(&path, &slash_bits, &path_err)) @@ -350,11 +350,15 @@ bool ManifestParser::ParseEdge(string* err) { lexer_.Error("multiple rules generate " + path + " [-w dupbuild=err]", err); return false; - } else if (!quiet_) { - Warning("multiple rules generate %s. " - "builds involving this target will not be correct; " - "continuing anyway [-w dupbuild=warn]", - path.c_str()); + } else { + if (!quiet_) { + Warning("multiple rules generate %s. " + "builds involving this target will not be correct; " + "continuing anyway [-w dupbuild=warn]", + path.c_str()); + } + if (e - i <= static_cast(implicit_outs)) + --implicit_outs; } } } diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc index ba83a67..1312d26 100644 --- a/src/manifest_parser_test.cc +++ b/src/manifest_parser_test.cc @@ -949,6 +949,30 @@ TEST_F(ParserTest, ImplicitOutputEmpty) { EXPECT_FALSE(edge->is_implicit_out(0)); } +TEST_F(ParserTest, ImplicitOutputDupe) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build foo baz | foo baq foo: cat bar\n")); + + Edge* edge = state.LookupNode("foo")->in_edge(); + ASSERT_EQ(edge->outputs_.size(), 3); + EXPECT_FALSE(edge->is_implicit_out(0)); + EXPECT_FALSE(edge->is_implicit_out(1)); + EXPECT_TRUE(edge->is_implicit_out(2)); +} + +TEST_F(ParserTest, ImplicitOutputDupes) { + ASSERT_NO_FATAL_FAILURE(AssertParse( +"rule cat\n" +" command = cat $in > $out\n" +"build foo foo foo | foo foo foo foo: cat bar\n")); + + Edge* edge = state.LookupNode("foo")->in_edge(); + ASSERT_EQ(edge->outputs_.size(), 1); + EXPECT_FALSE(edge->is_implicit_out(0)); +} + TEST_F(ParserTest, NoExplicitOutput) { ManifestParser parser(&state, NULL, kDupeEdgeActionWarn); string err; -- cgit v0.12 From 06b0e568f62d228837e96c485447f55da1ae9b5d Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Wed, 27 Apr 2016 15:15:54 -0400 Subject: mark this 1.7.0.git --- src/version.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.cc b/src/version.cc index 4c2aea1..6613514 100644 --- a/src/version.cc +++ b/src/version.cc @@ -18,7 +18,7 @@ #include "util.h" -const char* kNinjaVersion = "1.6.0.git"; +const char* kNinjaVersion = "1.7.0.git"; void ParseVersion(const string& version, int* major, int* minor) { size_t end = version.find('.'); -- cgit v0.12