summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlouiscaron <caron_louis@yahoo.fr>2022-10-05 21:13:50 +0200
committerGitHub <noreply@github.com>2022-10-05 21:13:50 +0200
commitfe9c0dfa93df290e974aa2dc6c29b163496db22e (patch)
tree4c64b6e33c2b02611c1cc6d2f1b520b359141d5f
parent9d7d64cd7ff66b2af4b8b2df180cad778137c98d (diff)
downloadccache-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.cpp60
-rw-r--r--unittest/test_Depfile.cpp189
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();