diff options
author | louiscaron <caron_louis@yahoo.fr> | 2022-10-05 21:13:50 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-05 21:13:50 +0200 |
commit | fe9c0dfa93df290e974aa2dc6c29b163496db22e (patch) | |
tree | 4c64b6e33c2b02611c1cc6d2f1b520b359141d5f | |
parent | 9d7d64cd7ff66b2af4b8b2df180cad778137c98d (diff) | |
download | ccache-fe9c0dfa93df290e974aa2dc6c29b163496db22e.tar.gz |
fix: Handle spaces between target and colon in dependency files (#1166)
Support dependency files that are generated with spaces between the target and
the colon sign.
-rw-r--r-- | src/Depfile.cpp | 60 | ||||
-rw-r--r-- | unittest/test_Depfile.cpp | 189 |
2 files changed, 245 insertions, 4 deletions
diff --git a/src/Depfile.cpp b/src/Depfile.cpp index ad63a766..c5accfe1 100644 --- a/src/Depfile.cpp +++ b/src/Depfile.cpp @@ -146,6 +146,30 @@ tokenize(std::string_view file_content) { // A dependency file uses Makefile syntax. This is not perfect parser but // should be enough for parsing a regular dependency file. + // enhancement: + // - space between target and colon + // - no space between colon and first pre-requisite + // the later is pretty complex because of the windows paths which are + // identical to a target-colon-prerequisite without spaces (e.g. cat:/meow vs. + // c:/meow) here are the tests on windows gnu make 4.3 how it handles this: + // + cat:/meow -> sees "cat" and "/meow" + // + cat:\meow -> sees "cat" and "\meow" + // + cat:\ meow -> sees "cat" and " meow" + // + cat:c:/meow -> sees "cat" and "c:/meow" + // + cat:c:\meow -> sees "cat" and "c:\meow" + // + cat:c: -> target pattern contains no '%'. Stop. + // + cat:c:\ -> target pattern contains no '%'. Stop. + // + cat:c:/ -> sees "cat" and "c:/" + // + cat:c:meow -> target pattern contains no '%'. Stop. + // + c:c:/meow -> sees "c" and "c:/meow" + // + c:c:\meow -> sees "c" and "c:\meow" + // + c:z:\meow -> sees "c" and "z:\meow" + // + c:cd:\meow -> target pattern contains no '%'. Stop. + + // the logic for a windows path is: + // - if there is a colon, if the previous token is 1 char long + // and that the following char is a slash (fw or bw), then it is + // a windows path std::vector<std::string> result; const size_t length = file_content.size(); @@ -153,19 +177,47 @@ tokenize(std::string_view file_content) size_t p = 0; while (p < length) { - // Each token is separated by whitespace. - if (isspace(file_content[p])) { + char c = file_content[p]; + + if (c == ':') { + if (p + 1 < length && !is_blank(token) && token.length() == 1) { + const char next = file_content[p + 1]; + if (next == '/' || next == '\\') { + // only in this case, this is not a separator and colon is + // added to token + token.push_back(c); + ++p; + continue; + } + } + } + // Each token is separated by whitespace or a colon. + if (isspace(c) || c == ':') { + // chomp all spaces before next char while (p < length && isspace(file_content[p])) { ++p; } if (!is_blank(token)) { + // if there were spaces between a token and the : sign, the : + // must be added to the same token to make sure it is seen as + // a target and not as a dependency (ccache requirement) + if (p < length) { + const char next = file_content[p]; + if (next == ':') { + token.push_back(next); + ++p; + // chomp all spaces before next char + while (p < length && isspace(file_content[p])) { + ++p; + } + } + } result.push_back(token); } token.clear(); continue; } - char c = file_content[p]; switch (c) { case '\\': if (p + 1 < length) { @@ -182,7 +234,7 @@ tokenize(std::string_view file_content) ++p; break; // Backslash followed by newline is interpreted like a space, so simply - // the backslash. + // discard the backslash. case '\n': ++p; continue; diff --git a/unittest/test_Depfile.cpp b/unittest/test_Depfile.cpp index 692a4153..e8ae88df 100644 --- a/unittest/test_Depfile.cpp +++ b/unittest/test_Depfile.cpp @@ -202,6 +202,195 @@ TEST_CASE("Depfile::tokenize") CHECK(result[0] == "cat.o:"); CHECK(result[1] == "meow"); } + + SUBCASE("Parse depfile with a one space before colon") + { + std::vector<std::string> result = Depfile::tokenize("cat.o : meow"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "cat.o:"); + CHECK(result[1] == "meow"); + } + + SUBCASE("Parse depfile with a two spaces before colon") + { + std::vector<std::string> result = Depfile::tokenize("cat.o : meow"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "cat.o:"); + CHECK(result[1] == "meow"); + } + + SUBCASE("Parse depfile with a plenty of spaces before colon") + { + std::vector<std::string> result = Depfile::tokenize("cat.o : meow"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "cat.o:"); + CHECK(result[1] == "meow"); + } + + SUBCASE("Parse depfile with no space between colon and dependency") + { + std::vector<std::string> result = Depfile::tokenize("cat.o:meow"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "cat.o:"); + CHECK(result[1] == "meow"); + } + + SUBCASE( + "Parse depfile with windows formatted filename (with backslashes in " + "target)") + { + std::vector<std::string> result = Depfile::tokenize("e:\\cat.o: meow"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "e:\\cat.o:"); + CHECK(result[1] == "meow"); + } + + SUBCASE( + "Parse depfile with windows formatted filename (with backslashes in " + "prerequisite)") + { + std::vector<std::string> result = + Depfile::tokenize("cat.o: c:\\meow\\purr"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "cat.o:"); + CHECK(result[1] == "c:\\meow\\purr"); + } + + SUBCASE( + "Parse depfile with windows formatted filename (with slashes in target)") + { + std::vector<std::string> result = Depfile::tokenize("e:/cat.o: meow"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "e:/cat.o:"); + CHECK(result[1] == "meow"); + } + + SUBCASE( + "Parse depfile with windows formatted filename (with slashes in " + "prerequisite)") + { + std::vector<std::string> result = Depfile::tokenize("cat.o: c:/meow/purr"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "cat.o:"); + CHECK(result[1] == "c:/meow/purr"); + } + + SUBCASE( + "Parse depfile with windows formatted filename (with slashes and trailing " + "colon)") + { + std::vector<std::string> result = Depfile::tokenize("cat.o: c: /meow/purr"); + REQUIRE(result.size() == 3); + CHECK(result[0] == "cat.o:"); + CHECK(result[1] == "c:"); + CHECK(result[2] == "/meow/purr"); + } + + SUBCASE("Parse depfile with windows formatted filename (subtest1)") + { + std::vector<std::string> result = Depfile::tokenize("cat:/meow"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "cat:"); + CHECK(result[1] == "/meow"); + } + + SUBCASE("Parse depfile with windows formatted filename (subtest2)") + { + std::vector<std::string> result = Depfile::tokenize("cat:\\meow"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "cat:"); + CHECK(result[1] == "\\meow"); + } + + SUBCASE("Parse depfile with windows formatted filename (subtest3)") + { + std::vector<std::string> result = Depfile::tokenize("cat:\\ meow"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "cat:"); + CHECK(result[1] == " meow"); + } + + SUBCASE("Parse depfile with windows formatted filename (subtest4)") + { + std::vector<std::string> result = Depfile::tokenize("cat:c:/meow"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "cat:"); + CHECK(result[1] == "c:/meow"); + } + + SUBCASE("Parse depfile with windows formatted filename (subtest5)") + { + std::vector<std::string> result = Depfile::tokenize("cat:c:\\meow"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "cat:"); + CHECK(result[1] == "c:\\meow"); + } + + SUBCASE("Parse depfile with windows formatted filename (subtest6)") + { + std::vector<std::string> result = Depfile::tokenize("cat:c:"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "cat:"); + CHECK(result[1] == "c:"); + } + + SUBCASE("Parse depfile with windows formatted filename (subtest7)") + { + std::vector<std::string> result = Depfile::tokenize("cat:c:\\"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "cat:"); + CHECK(result[1] == "c:\\"); + } + + SUBCASE("Parse depfile with windows formatted filename (subtest8)") + { + std::vector<std::string> result = Depfile::tokenize("cat:c:/"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "cat:"); + CHECK(result[1] == "c:/"); + } + + SUBCASE("Parse depfile with windows formatted filename (subtest9)") + { + std::vector<std::string> result = Depfile::tokenize("cat:c:meow"); + REQUIRE(result.size() == 3); + CHECK(result[0] == "cat:"); + CHECK(result[1] == "c:"); + CHECK(result[2] == "meow"); + } + + SUBCASE("Parse depfile with windows formatted filename (subtest10)") + { + std::vector<std::string> result = Depfile::tokenize("c:c:/meow"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "c:"); + CHECK(result[1] == "c:/meow"); + } + + SUBCASE("Parse depfile with windows formatted filename (subtest11)") + { + std::vector<std::string> result = Depfile::tokenize("c:c:\\meow"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "c:"); + CHECK(result[1] == "c:\\meow"); + } + + SUBCASE("Parse depfile with windows formatted filename (subtest12)") + { + std::vector<std::string> result = Depfile::tokenize("c:z:\\meow"); + REQUIRE(result.size() == 2); + CHECK(result[0] == "c:"); + CHECK(result[1] == "z:\\meow"); + } + + SUBCASE("Parse depfile with windows formatted filename (subtest13)") + { + std::vector<std::string> result = Depfile::tokenize("c:cd:\\meow"); + REQUIRE(result.size() == 3); + CHECK(result[0] == "c:"); + CHECK(result[1] == "cd:"); + CHECK(result[2] == "\\meow"); + } } TEST_SUITE_END(); |