{
// 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
+ //
+ // Note that this is pretty complex because of Windows paths that can be
// 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
+ // c:/meow).
+ //
+ // Here are tests on Windows on how GNU Make 4.3 handles different scenarios:
+ //
+ // 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.
+ //
+ // Thus, if there is a colon and the previous token is one character long and
+ // the following character is a slash (forward or backward), then it is
+ // interpreted as a Windows path.
std::vector<std::string> result;
const size_t length = file_content.size();
while (p < length) {
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;
- }
+ if (c == ':' && p + 1 < length && !is_blank(token) && token.length() == 1) {
+ const char next = file_content[p + 1];
+ if (next == '/' || next == '\\') {
+ // It's a Windows path, so the colon is not a separator and instead
+ // added to the 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
+ // Chomp all spaces before next character.
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 there were spaces between a token and the colon, add the colon the
+ // token to make sure it is seen as a target and not as a dependency.
if (p < length) {
const char next = file_content[p];
if (next == ':') {
token.push_back(next);
++p;
- // chomp all spaces before next char
+ // Chomp all spaces before next character.
while (p < length && isspace(file_content[p])) {
++p;
}
TEST_CASE("Depfile::tokenize")
{
- SUBCASE("Parse empty depfile")
+ SUBCASE("Empty")
{
std::vector<std::string> result = Depfile::tokenize("");
CHECK(result.size() == 0);
}
- SUBCASE("Parse simple depfile")
+ SUBCASE("Simple")
{
std::vector<std::string> result =
Depfile::tokenize("cat.o: meow meow purr");
CHECK(result[3] == "purr");
}
- SUBCASE("Parse depfile with a dollar sign followed by a dollar sign")
+ SUBCASE("Dollar sign followed by a dollar sign")
{
std::vector<std::string> result = Depfile::tokenize("cat.o: meow$$");
REQUIRE(result.size() == 2);
CHECK(result[1] == "meow$");
}
- SUBCASE("Parse depfile with a dollar sign followed by an alphabet")
+ SUBCASE("Dollar sign followed by an alphabet")
{
std::vector<std::string> result = Depfile::tokenize("cat.o: meow$w");
REQUIRE(result.size() == 2);
CHECK(result[1] == "meow$w");
}
- SUBCASE("Parse depfile with a backslash followed by a number sign or a colon")
+ SUBCASE("Backslash followed by a number sign or a colon")
{
std::vector<std::string> result =
Depfile::tokenize("cat.o: meow\\# meow\\:");
CHECK(result[2] == "meow:");
}
- SUBCASE("Parse depfile with a backslash followed by an alphabet")
+ SUBCASE("Backslash followed by an alphabet")
{
std::vector<std::string> result =
Depfile::tokenize("cat.o: meow\\w purr\\r");
CHECK(result[2] == "purr\\r");
}
- SUBCASE("Parse depfile with a backslash followed by a space or a tab")
+ SUBCASE("Backslash followed by a space or a tab")
{
std::vector<std::string> result =
Depfile::tokenize("cat.o: meow\\ meow purr\\\tpurr");
CHECK(result[2] == "purr\tpurr");
}
- SUBCASE("Parse depfile with backslashes followed by a space or a tab")
+ SUBCASE("Backslashes followed by a space or a tab")
{
std::vector<std::string> result =
Depfile::tokenize("cat.o: meow\\\\\\ meow purr\\\\ purr");
CHECK(result[3] == "purr");
}
- SUBCASE("Parse depfile with a backslash newline")
+ SUBCASE("Backslash newline")
{
std::vector<std::string> result =
Depfile::tokenize("cat.o: meow\\\nmeow\\\n purr\\\n\tpurr");
CHECK(result[4] == "purr");
}
- SUBCASE("Parse depfile with a new line")
+ SUBCASE("Newlines")
{
- // This is invalid depfile because it has multiple lines without backslash,
- // which is not valid in Makefile syntax.
- // However, Depfile::tokenize is parsing it to each token, which is
- // expected.
+ // This is an invalid dependency file since it has multiple lines without
+ // backslash, which is not valid Makefile syntax. However, the
+ // Depfile::tokenize's simplistic parser accepts them.
std::vector<std::string> result =
Depfile::tokenize("cat.o: meow\nmeow\npurr\n");
REQUIRE(result.size() == 4);
CHECK(result[3] == "purr");
}
- SUBCASE("Parse depfile with a trailing dollar sign")
+ SUBCASE("Trailing dollar sign")
{
std::vector<std::string> result = Depfile::tokenize("cat.o: meow$");
REQUIRE(result.size() == 2);
CHECK(result[1] == "meow$");
}
- SUBCASE("Parse depfile with a trailing backslash")
+ SUBCASE("Trailing backslash")
{
std::vector<std::string> result = Depfile::tokenize("cat.o: meow\\");
REQUIRE(result.size() == 2);
CHECK(result[1] == "meow\\");
}
- SUBCASE("Parse depfile with a trailing backslash newline")
+ SUBCASE("Trailing backslash newline")
{
std::vector<std::string> result = Depfile::tokenize("cat.o: meow\\\n");
REQUIRE(result.size() == 2);
CHECK(result[1] == "meow");
}
- SUBCASE("Parse depfile with a one space before colon")
+ SUBCASE("Space before the colon but not after")
{
- std::vector<std::string> result = Depfile::tokenize("cat.o : meow");
+ 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")
+ SUBCASE("Space around the colon")
{
std::vector<std::string> result = Depfile::tokenize("cat.o : meow");
REQUIRE(result.size() == 2);
CHECK(result[1] == "meow");
}
- SUBCASE("Parse depfile with no space between colon and dependency")
+ SUBCASE("No space between colon and dependency")
{
std::vector<std::string> result = Depfile::tokenize("cat.o:meow");
REQUIRE(result.size() == 2);
CHECK(result[1] == "meow");
}
- SUBCASE(
- "Parse depfile with windows formatted filename (with backslashes in "
- "target)")
+ SUBCASE("Windows filename (with backslashes in target)")
{
std::vector<std::string> result = Depfile::tokenize("e:\\cat.o: meow");
REQUIRE(result.size() == 2);
CHECK(result[1] == "meow");
}
- SUBCASE(
- "Parse depfile with windows formatted filename (with backslashes in "
- "prerequisite)")
+ SUBCASE("Windows filename (with backslashes in prerequisite)")
{
std::vector<std::string> result =
Depfile::tokenize("cat.o: c:\\meow\\purr");
CHECK(result[1] == "c:\\meow\\purr");
}
- SUBCASE(
- "Parse depfile with windows formatted filename (with slashes in target)")
+ SUBCASE("Windows filename (with slashes in target)")
{
std::vector<std::string> result = Depfile::tokenize("e:/cat.o: meow");
REQUIRE(result.size() == 2);
CHECK(result[1] == "meow");
}
- SUBCASE(
- "Parse depfile with windows formatted filename (with slashes in "
- "prerequisite)")
+ SUBCASE("Windows filename (with slashes in prerequisite)")
{
std::vector<std::string> result = Depfile::tokenize("cat.o: c:/meow/purr");
REQUIRE(result.size() == 2);
CHECK(result[1] == "c:/meow/purr");
}
- SUBCASE(
- "Parse depfile with windows formatted filename (with slashes and trailing "
- "colon)")
+ SUBCASE("Windows filename (with slashes and trailing colon)")
{
std::vector<std::string> result = Depfile::tokenize("cat.o: c: /meow/purr");
REQUIRE(result.size() == 3);
CHECK(result[2] == "/meow/purr");
}
- SUBCASE("Parse depfile with windows formatted filename (subtest1)")
+ SUBCASE("Windows filename: cat:/meow")
{
std::vector<std::string> result = Depfile::tokenize("cat:/meow");
REQUIRE(result.size() == 2);
CHECK(result[1] == "/meow");
}
- SUBCASE("Parse depfile with windows formatted filename (subtest2)")
+ SUBCASE("Windows filename: cat:\\meow")
{
std::vector<std::string> result = Depfile::tokenize("cat:\\meow");
REQUIRE(result.size() == 2);
CHECK(result[1] == "\\meow");
}
- SUBCASE("Parse depfile with windows formatted filename (subtest3)")
+ SUBCASE("Windows filename: cat:\\ meow")
{
std::vector<std::string> result = Depfile::tokenize("cat:\\ meow");
REQUIRE(result.size() == 2);
CHECK(result[1] == " meow");
}
- SUBCASE("Parse depfile with windows formatted filename (subtest4)")
+ SUBCASE("Windows filename: cat:c:/meow")
{
std::vector<std::string> result = Depfile::tokenize("cat:c:/meow");
REQUIRE(result.size() == 2);
CHECK(result[1] == "c:/meow");
}
- SUBCASE("Parse depfile with windows formatted filename (subtest5)")
+ SUBCASE("Windows filename: cat:c:\\meow")
{
std::vector<std::string> result = Depfile::tokenize("cat:c:\\meow");
REQUIRE(result.size() == 2);
CHECK(result[1] == "c:\\meow");
}
- SUBCASE("Parse depfile with windows formatted filename (subtest6)")
+ SUBCASE("Windows filename: cat:c:")
{
std::vector<std::string> result = Depfile::tokenize("cat:c:");
REQUIRE(result.size() == 2);
CHECK(result[1] == "c:");
}
- SUBCASE("Parse depfile with windows formatted filename (subtest7)")
+ SUBCASE("Windows filename: cat:c:\\")
{
std::vector<std::string> result = Depfile::tokenize("cat:c:\\");
REQUIRE(result.size() == 2);
CHECK(result[1] == "c:\\");
}
- SUBCASE("Parse depfile with windows formatted filename (subtest8)")
+ SUBCASE("Windows filename: cat:c:/")
{
std::vector<std::string> result = Depfile::tokenize("cat:c:/");
REQUIRE(result.size() == 2);
CHECK(result[1] == "c:/");
}
- SUBCASE("Parse depfile with windows formatted filename (subtest9)")
+ SUBCASE("Windows filename: cat:c:meow")
{
std::vector<std::string> result = Depfile::tokenize("cat:c:meow");
REQUIRE(result.size() == 3);
CHECK(result[2] == "meow");
}
- SUBCASE("Parse depfile with windows formatted filename (subtest10)")
+ SUBCASE("Windows filename: c:c:/meow")
{
std::vector<std::string> result = Depfile::tokenize("c:c:/meow");
REQUIRE(result.size() == 2);
CHECK(result[1] == "c:/meow");
}
- SUBCASE("Parse depfile with windows formatted filename (subtest11)")
+ SUBCASE("Windows filename: c:c:\\meow")
{
std::vector<std::string> result = Depfile::tokenize("c:c:\\meow");
REQUIRE(result.size() == 2);
CHECK(result[1] == "c:\\meow");
}
- SUBCASE("Parse depfile with windows formatted filename (subtest12)")
+ SUBCASE("Windows filename: c:z:\\meow")
{
std::vector<std::string> result = Depfile::tokenize("c:z:\\meow");
REQUIRE(result.size() == 2);
CHECK(result[1] == "z:\\meow");
}
- SUBCASE("Parse depfile with windows formatted filename (subtest13)")
+ SUBCASE("Windows filename: c:cd:\\meow")
{
std::vector<std::string> result = Depfile::tokenize("c:cd:\\meow");
REQUIRE(result.size() == 3);