CanonicalizePath handles \ on Windows

This commit is contained in:
Scott Graham 2014-10-30 15:34:25 -07:00
parent b92c1a4fd7
commit 65151e7eef
10 changed files with 438 additions and 284 deletions

View File

@ -696,9 +696,7 @@ Lexical syntax
Ninja is mostly encoding agnostic, as long as the bytes Ninja cares
about (like slashes in paths) are ASCII. This means e.g. UTF-8 or
ISO-8859-1 input files ought to work. (To simplify some code, tabs
and carriage returns are currently disallowed; this could be fixed if
it really mattered to you.)
ISO-8859-1 input files ought to work.
Comments begin with `#` and extend to the end of the line.

View File

@ -182,7 +182,7 @@ def FileWriter(path):
def random_targets():
num_targets = 800
num_targets = 1500
gen = GenRandom()
# N-1 static libraries, and 1 executable depending on all of them.

View File

@ -755,7 +755,7 @@ TEST_F(BuildTest, MakeDirs) {
#ifdef _WIN32
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
"build subdir\\dir2\\file: cat in1\n"));
EXPECT_TRUE(builder_.AddTarget("subdir\\dir2\\file", &err));
EXPECT_TRUE(builder_.AddTarget("subdir/dir2/file", &err));
#else
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
"build subdir/dir2/file: cat in1\n"));
@ -767,11 +767,7 @@ TEST_F(BuildTest, MakeDirs) {
ASSERT_EQ("", err);
ASSERT_EQ(2u, fs_.directories_made_.size());
EXPECT_EQ("subdir", fs_.directories_made_[0]);
#ifdef _WIN32
EXPECT_EQ("subdir\\dir2", fs_.directories_made_[1]);
#else
EXPECT_EQ("subdir/dir2", fs_.directories_made_[1]);
#endif
}
TEST_F(BuildTest, DepFileMissing) {

View File

@ -68,12 +68,15 @@ string IncludesNormalize::ToLower(const string& s) {
string IncludesNormalize::AbsPath(StringPiece s) {
char result[_MAX_PATH];
GetFullPathName(s.AsString().c_str(), sizeof(result), result, NULL);
for (char* c = result; *c; ++c)
if (*c == '\\')
*c = '/';
return result;
}
string IncludesNormalize::Relativize(StringPiece path, const string& start) {
vector<string> start_list = Split(AbsPath(start), '\\');
vector<string> path_list = Split(AbsPath(path), '\\');
vector<string> start_list = Split(AbsPath(start), '/');
vector<string> path_list = Split(AbsPath(path), '/');
int i;
for (i = 0; i < static_cast<int>(min(start_list.size(), path_list.size()));
++i) {
@ -88,7 +91,7 @@ string IncludesNormalize::Relativize(StringPiece path, const string& start) {
rel_list.push_back(path_list[j]);
if (rel_list.size() == 0)
return ".";
return Join(rel_list, '\\');
return Join(rel_list, '/');
}
string IncludesNormalize::Normalize(const string& input,
@ -96,19 +99,16 @@ string IncludesNormalize::Normalize(const string& input,
char copy[_MAX_PATH];
size_t len = input.size();
strncpy(copy, input.c_str(), input.size() + 1);
for (size_t j = 0; j < len; ++j)
if (copy[j] == '/')
copy[j] = '\\';
string err;
if (!CanonicalizePath(copy, &len, &err)) {
Warning("couldn't canonicalize '%s: %s\n", input.c_str(), err.c_str());
}
if (!CanonicalizePath(copy, &len, &err))
Warning("couldn't canonicalize '%s': %s\n", input.c_str(), err.c_str());
StringPiece partially_fixed(copy, len);
string curdir;
if (!relative_to) {
curdir = AbsPath(".");
relative_to = curdir.c_str();
}
StringPiece partially_fixed(copy, len);
if (!SameDrive(partially_fixed, relative_to))
return partially_fixed.AsString();
return Relativize(partially_fixed, relative_to);

View File

@ -22,8 +22,8 @@
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));
EXPECT_EQ("a/b", IncludesNormalize::Normalize("a\\.\\b", NULL));
EXPECT_EQ("a/b", IncludesNormalize::Normalize("a\\./b", NULL));
}
namespace {
@ -42,21 +42,21 @@ TEST(IncludesNormalize, WithRelative) {
EXPECT_EQ("c", IncludesNormalize::Normalize("a/b/c", "a/b"));
EXPECT_EQ("a", IncludesNormalize::Normalize(IncludesNormalize::AbsPath("a"),
NULL));
EXPECT_EQ(string("..\\") + currentdir + string("\\a"),
EXPECT_EQ(string("../") + currentdir + string("/a"),
IncludesNormalize::Normalize("a", "../b"));
EXPECT_EQ(string("..\\") + currentdir + string("\\a\\b"),
EXPECT_EQ(string("../") + currentdir + string("/a/b"),
IncludesNormalize::Normalize("a/b", "../c"));
EXPECT_EQ("..\\..\\a", IncludesNormalize::Normalize("a", "b/c"));
EXPECT_EQ("../../a", IncludesNormalize::Normalize("a", "b/c"));
EXPECT_EQ(".", IncludesNormalize::Normalize("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("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));
}
TEST(IncludesNormalize, Join) {
@ -92,13 +92,13 @@ TEST(IncludesNormalize, DifferentDrive) {
IncludesNormalize::Normalize("p:\\vs08\\stuff.h", "p:\\vs08"));
EXPECT_EQ("stuff.h",
IncludesNormalize::Normalize("P:\\Vs08\\stuff.h", "p:\\vs08"));
EXPECT_EQ("p:\\vs08\\stuff.h",
EXPECT_EQ("p:/vs08/stuff.h",
IncludesNormalize::Normalize("p:\\vs08\\stuff.h", "c:\\vs08"));
EXPECT_EQ("P:\\vs08\\stufF.h",
EXPECT_EQ("P:/vs08/stufF.h",
IncludesNormalize::Normalize("P:\\vs08\\stufF.h", "D:\\stuff/things"));
EXPECT_EQ("P:\\vs08\\stuff.h",
EXPECT_EQ("P:/vs08/stuff.h",
IncludesNormalize::Normalize("P:/vs08\\stuff.h", "D:\\stuff/things"));
// TODO: this fails; fix it.
//EXPECT_EQ("P:\\wee\\stuff.h",
// IncludesNormalize::Normalize("P:/vs08\\../wee\\stuff.h", "D:\\stuff/things"));
EXPECT_EQ("P:/wee/stuff.h",
IncludesNormalize::Normalize("P:/vs08\\../wee\\stuff.h",
"D:\\stuff/things"));
}

View File

@ -103,8 +103,6 @@ const char* Lexer::TokenErrorHint(Token expected) {
string Lexer::DescribeLastError() {
if (last_token_) {
switch (last_token_[0]) {
case '\r':
return "carriage returns are not allowed, use newlines";
case '\t':
return "tabs are not allowed, use spaces";
}
@ -129,7 +127,7 @@ Lexer::Token Lexer::ReadToken() {
unsigned int yyaccept = 0;
static const unsigned char yybm[] = {
0, 64, 64, 64, 64, 64, 64, 64,
64, 64, 0, 64, 64, 0, 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,
@ -163,56 +161,66 @@ Lexer::Token Lexer::ReadToken() {
};
yych = *p;
if (yych <= '^') {
if (yych <= ',') {
if (yych <= 0x1F) {
if (yych <= 0x00) goto yy22;
if (yych == '\n') goto yy6;
goto yy24;
if (yych <= 'Z') {
if (yych <= '#') {
if (yych <= '\f') {
if (yych <= 0x00) goto yy23;
if (yych == '\n') goto yy7;
goto yy25;
} else {
if (yych <= ' ') goto yy2;
if (yych == '#') goto yy4;
goto yy24;
if (yych <= 0x1F) {
if (yych <= '\r') goto yy6;
goto yy25;
} else {
if (yych <= ' ') goto yy2;
if (yych <= '"') goto yy25;
goto yy4;
}
}
} else {
if (yych <= ':') {
if (yych == '/') goto yy24;
if (yych <= '9') goto yy21;
goto yy15;
if (yych <= '9') {
if (yych <= ',') goto yy25;
if (yych == '/') goto yy25;
goto yy22;
} else {
if (yych <= '=') {
if (yych <= '<') goto yy24;
goto yy13;
if (yych <= '<') {
if (yych <= ':') goto yy16;
goto yy25;
} else {
if (yych <= '@') goto yy24;
if (yych <= 'Z') goto yy21;
goto yy24;
if (yych <= '=') goto yy14;
if (yych <= '@') goto yy25;
goto yy22;
}
}
}
} else {
if (yych <= 'i') {
if (yych <= 'b') {
if (yych == '`') goto yy24;
if (yych <= 'a') goto yy21;
goto yy8;
if (yych <= 'a') {
if (yych == '_') goto yy22;
if (yych <= '`') goto yy25;
goto yy22;
} else {
if (yych == 'd') goto yy12;
if (yych <= 'h') goto yy21;
goto yy19;
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 yy10;
if (yych <= 'q') goto yy21;
goto yy11;
if (yych == 'p') goto yy11;
if (yych <= 'q') goto yy22;
goto yy12;
} else {
if (yych <= 'z') {
if (yych <= 's') goto yy20;
goto yy21;
if (yych <= 's') goto yy21;
goto yy22;
} else {
if (yych == '|') goto yy17;
goto yy24;
if (yych == '|') goto yy18;
goto yy25;
}
}
}
@ -220,192 +228,203 @@ Lexer::Token Lexer::ReadToken() {
yy2:
yyaccept = 0;
yych = *(q = ++p);
goto yy70;
goto yy73;
yy3:
{ token = INDENT; break; }
yy4:
yyaccept = 1;
yych = *(q = ++p);
if (yych <= 0x00) goto yy5;
if (yych != '\r') goto yy65;
if (yych >= 0x01) goto yy68;
yy5:
{ token = ERROR; break; }
yy6:
++p;
yy7:
{ token = NEWLINE; break; }
yy8:
++p;
if ((yych = *p) == 'u') goto yy59;
goto yy26;
yy9:
{ token = IDENT; break; }
yy10:
yych = *++p;
if (yych == 'o') goto yy55;
goto yy26;
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 == 'u') goto yy51;
goto yy26;
if (yych == 'o') goto yy56;
goto yy27;
yy12:
yych = *++p;
if (yych == 'e') goto yy44;
goto yy26;
if (yych == 'u') goto yy52;
goto yy27;
yy13:
yych = *++p;
if (yych == 'e') goto yy45;
goto yy27;
yy14:
++p;
{ token = EQUALS; break; }
yy15:
yy16:
++p;
{ token = COLON; break; }
yy17:
yy18:
++p;
if ((yych = *p) == '|') goto yy42;
if ((yych = *p) == '|') goto yy43;
{ token = PIPE; break; }
yy19:
yych = *++p;
if (yych == 'n') goto yy35;
goto yy26;
yy20:
yych = *++p;
if (yych == 'u') goto yy27;
goto yy26;
if (yych == 'n') goto yy36;
goto yy27;
yy21:
yych = *++p;
goto yy26;
if (yych == 'u') goto yy28;
goto yy27;
yy22:
yych = *++p;
goto yy27;
yy23:
++p;
{ token = TEOF; break; }
yy24:
yy25:
yych = *++p;
goto yy5;
yy25:
yy26:
++p;
yych = *p;
yy26:
if (yybm[0+yych] & 32) {
goto yy25;
}
goto yy9;
yy27:
if (yybm[0+yych] & 32) {
goto yy26;
}
goto yy10;
yy28:
yych = *++p;
if (yych != 'b') goto yy26;
if (yych != 'b') goto yy27;
yych = *++p;
if (yych != 'n') goto yy26;
if (yych != 'n') goto yy27;
yych = *++p;
if (yych != 'i') goto yy26;
if (yych != 'i') goto yy27;
yych = *++p;
if (yych != 'n') goto yy26;
if (yych != 'n') goto yy27;
yych = *++p;
if (yych != 'j') goto yy26;
if (yych != 'j') goto yy27;
yych = *++p;
if (yych != 'a') goto yy26;
if (yych != 'a') goto yy27;
++p;
if (yybm[0+(yych = *p)] & 32) {
goto yy25;
goto yy26;
}
{ token = SUBNINJA; break; }
yy35:
yy36:
yych = *++p;
if (yych != 'c') goto yy26;
if (yych != 'c') goto yy27;
yych = *++p;
if (yych != 'l') goto yy26;
if (yych != 'l') goto yy27;
yych = *++p;
if (yych != 'u') goto yy26;
if (yych != 'u') goto yy27;
yych = *++p;
if (yych != 'd') goto yy26;
if (yych != 'd') goto yy27;
yych = *++p;
if (yych != 'e') goto yy26;
if (yych != 'e') goto yy27;
++p;
if (yybm[0+(yych = *p)] & 32) {
goto yy25;
goto yy26;
}
{ token = INCLUDE; break; }
yy42:
yy43:
++p;
{ token = PIPE2; break; }
yy44:
yy45:
yych = *++p;
if (yych != 'f') goto yy26;
if (yych != 'f') goto yy27;
yych = *++p;
if (yych != 'a') goto yy26;
if (yych != 'a') goto yy27;
yych = *++p;
if (yych != 'u') goto yy26;
if (yych != 'u') goto yy27;
yych = *++p;
if (yych != 'l') goto yy26;
if (yych != 'l') goto yy27;
yych = *++p;
if (yych != 't') goto yy26;
if (yych != 't') goto yy27;
++p;
if (yybm[0+(yych = *p)] & 32) {
goto yy25;
goto yy26;
}
{ token = DEFAULT; break; }
yy51:
yy52:
yych = *++p;
if (yych != 'l') goto yy26;
if (yych != 'l') goto yy27;
yych = *++p;
if (yych != 'e') goto yy26;
if (yych != 'e') goto yy27;
++p;
if (yybm[0+(yych = *p)] & 32) {
goto yy25;
goto yy26;
}
{ token = RULE; break; }
yy55:
yy56:
yych = *++p;
if (yych != 'o') goto yy26;
if (yych != 'o') goto yy27;
yych = *++p;
if (yych != 'l') goto yy26;
if (yych != 'l') goto yy27;
++p;
if (yybm[0+(yych = *p)] & 32) {
goto yy25;
goto yy26;
}
{ token = POOL; break; }
yy59:
yy60:
yych = *++p;
if (yych != 'i') goto yy26;
if (yych != 'i') goto yy27;
yych = *++p;
if (yych != 'l') goto yy26;
if (yych != 'l') goto yy27;
yych = *++p;
if (yych != 'd') goto yy26;
if (yych != 'd') goto yy27;
++p;
if (yybm[0+(yych = *p)] & 32) {
goto yy25;
goto yy26;
}
{ token = BUILD; break; }
yy64:
yy65:
++p;
{ token = NEWLINE; break; }
yy67:
++p;
yych = *p;
yy65:
yy68:
if (yybm[0+yych] & 64) {
goto yy64;
goto yy67;
}
if (yych <= 0x00) goto yy66;
if (yych <= '\f') goto yy67;
yy66:
if (yych >= 0x01) goto yy70;
yy69:
p = q;
if (yyaccept <= 0) {
goto yy3;
} else {
goto yy5;
}
yy67:
yy70:
++p;
{ continue; }
yy69:
yy72:
yyaccept = 0;
q = ++p;
yych = *p;
yy70:
yy73:
if (yybm[0+yych] & 128) {
goto yy69;
goto yy72;
}
if (yych == '\n') goto yy71;
if (yych == '#') goto yy64;
goto yy3;
yy71:
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;
yych = *p;
goto yy7;
if ((yych = *p) == '\n') goto yy65;
goto yy69;
}
}
@ -427,6 +446,7 @@ bool Lexer::PeekToken(Token token) {
void Lexer::EatWhitespace() {
const char* p = ofs_;
const char* q;
for (;;) {
ofs_ = p;
@ -468,39 +488,48 @@ void Lexer::EatWhitespace() {
};
yych = *p;
if (yych <= ' ') {
if (yych <= 0x00) goto yy78;
if (yych <= 0x1F) goto yy80;
if (yych <= 0x00) goto yy82;
if (yych <= 0x1F) goto yy84;
} else {
if (yych == '$') goto yy76;
goto yy80;
if (yych == '$') goto yy80;
goto yy84;
}
++p;
yych = *p;
goto yy84;
yy75:
goto yy92;
yy79:
{ continue; }
yy76:
++p;
if ((yych = *p) == '\n') goto yy81;
yy77:
{ break; }
yy78:
++p;
{ break; }
yy80:
yych = *++p;
goto yy77;
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; }
yy83:
yy87:
yych = *++p;
if (yych == '\n') goto yy89;
p = q;
goto yy81;
yy89:
++p;
{ continue; }
yy91:
++p;
yych = *p;
yy84:
yy92:
if (yybm[0+yych] & 128) {
goto yy83;
goto yy91;
}
goto yy75;
goto yy79;
}
}
@ -550,40 +579,40 @@ bool Lexer::ReadIdent(string* out) {
yych = *p;
if (yych <= '@') {
if (yych <= '.') {
if (yych <= ',') goto yy89;
if (yych <= ',') goto yy97;
} else {
if (yych <= '/') goto yy89;
if (yych >= ':') goto yy89;
if (yych <= '/') goto yy97;
if (yych >= ':') goto yy97;
}
} else {
if (yych <= '_') {
if (yych <= 'Z') goto yy87;
if (yych <= '^') goto yy89;
if (yych <= 'Z') goto yy95;
if (yych <= '^') goto yy97;
} else {
if (yych <= '`') goto yy89;
if (yych >= '{') goto yy89;
if (yych <= '`') goto yy97;
if (yych >= '{') goto yy97;
}
}
yy87:
yy95:
++p;
yych = *p;
goto yy92;
yy88:
goto yy100;
yy96:
{
out->assign(start, p - start);
break;
}
yy89:
yy97:
++p;
{ return false; }
yy91:
yy99:
++p;
yych = *p;
yy92:
yy100:
if (yybm[0+yych] & 128) {
goto yy91;
goto yy99;
}
goto yy88;
goto yy96;
}
}
@ -638,29 +667,36 @@ bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) {
yych = *p;
if (yych <= ' ') {
if (yych <= '\n') {
if (yych <= 0x00) goto yy101;
if (yych >= '\n') goto yy97;
if (yych <= 0x00) goto yy110;
if (yych >= '\n') goto yy107;
} else {
if (yych == '\r') goto yy103;
if (yych >= ' ') goto yy97;
if (yych == '\r') goto yy105;
if (yych >= ' ') goto yy107;
}
} else {
if (yych <= '9') {
if (yych == '$') goto yy99;
if (yych == '$') goto yy109;
} else {
if (yych <= ':') goto yy97;
if (yych == '|') goto yy97;
if (yych <= ':') goto yy107;
if (yych == '|') goto yy107;
}
}
++p;
yych = *p;
goto yy126;
yy96:
goto yy140;
yy104:
{
eval->AddText(StringPiece(start, p - start));
continue;
}
yy97:
yy105:
++p;
if ((yych = *p) == '\n') goto yy137;
{
last_token_ = start;
return Error(DescribeLastError(), err);
}
yy107:
++p;
{
if (path) {
@ -673,137 +709,152 @@ yy97:
continue;
}
}
yy99:
++p;
if ((yych = *p) <= '/') {
if (yych <= ' ') {
if (yych == '\n') goto yy115;
if (yych <= 0x1F) goto yy104;
goto yy106;
} else {
if (yych <= '$') {
if (yych <= '#') goto yy104;
goto yy108;
yy109:
yych = *++p;
if (yych <= '-') {
if (yych <= 0x1F) {
if (yych <= '\n') {
if (yych <= '\t') goto yy112;
goto yy124;
} else {
if (yych == '-') goto yy110;
goto yy104;
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 <= '^') {
if (yych <= ':') {
if (yych <= '9') goto yy110;
goto yy112;
if (yych <= 'Z') {
if (yych <= '9') {
if (yych <= '/') goto yy112;
goto yy119;
} else {
if (yych <= '@') goto yy104;
if (yych <= 'Z') goto yy110;
goto yy104;
if (yych <= ':') goto yy121;
if (yych <= '@') goto yy112;
goto yy119;
}
} else {
if (yych <= '`') {
if (yych <= '_') goto yy110;
goto yy104;
if (yych == '_') goto yy119;
goto yy112;
} else {
if (yych <= 'z') goto yy110;
if (yych <= '{') goto yy114;
goto yy104;
if (yych <= 'z') goto yy119;
if (yych <= '{') goto yy123;
goto yy112;
}
}
}
yy100:
{
last_token_ = start;
return Error(DescribeLastError(), err);
}
yy101:
yy110:
++p;
{
last_token_ = start;
return Error("unexpected EOF", err);
}
yy103:
yych = *++p;
goto yy100;
yy104:
yy112:
++p;
yy105:
yy113:
{
last_token_ = start;
return Error("bad $-escape (literal $ must be written as $$)", err);
}
yy106:
yy114:
yych = *++p;
if (yych == '\n') goto yy134;
goto yy113;
yy115:
++p;
{
eval->AddText(StringPiece(" ", 1));
continue;
}
yy108:
yy117:
++p;
{
eval->AddText(StringPiece("$", 1));
continue;
}
yy110:
yy119:
++p;
yych = *p;
goto yy124;
yy111:
goto yy133;
yy120:
{
eval->AddSpecial(StringPiece(start + 1, p - start - 1));
continue;
}
yy112:
yy121:
++p;
{
eval->AddText(StringPiece(":", 1));
continue;
}
yy114:
yy123:
yych = *(q = ++p);
if (yybm[0+yych] & 32) {
goto yy118;
goto yy127;
}
goto yy105;
yy115:
goto yy113;
yy124:
++p;
yych = *p;
if (yybm[0+yych] & 16) {
goto yy115;
goto yy124;
}
{
continue;
}
yy118:
yy127:
++p;
yych = *p;
if (yybm[0+yych] & 32) {
goto yy118;
goto yy127;
}
if (yych == '}') goto yy121;
if (yych == '}') goto yy130;
p = q;
goto yy105;
yy121:
goto yy113;
yy130:
++p;
{
eval->AddSpecial(StringPiece(start + 2, p - start - 3));
continue;
}
yy123:
yy132:
++p;
yych = *p;
yy124:
yy133:
if (yybm[0+yych] & 64) {
goto yy123;
goto yy132;
}
goto yy111;
yy125:
goto yy120;
yy134:
++p;
yych = *p;
yy126:
if (yych == ' ') goto yy134;
{
continue;
}
yy137:
++p;
{
if (path)
p = start;
break;
}
yy139:
++p;
yych = *p;
yy140:
if (yybm[0+yych] & 128) {
goto yy125;
goto yy139;
}
goto yy96;
goto yy104;
}
}

View File

@ -102,8 +102,6 @@ const char* Lexer::TokenErrorHint(Token expected) {
string Lexer::DescribeLastError() {
if (last_token_) {
switch (last_token_[0]) {
case '\r':
return "carriage returns are not allowed, use newlines";
case '\t':
return "tabs are not allowed, use spaces";
}
@ -132,8 +130,9 @@ Lexer::Token Lexer::ReadToken() {
simple_varname = [a-zA-Z0-9_-]+;
varname = [a-zA-Z0-9_.-]+;
[ ]*"#"[^\000\r\n]*"\n" { continue; }
[ ]*[\n] { token = NEWLINE; break; }
[ ]*"#"[^\000\n]*"\n" { continue; }
[ ]*"\r\n" { token = NEWLINE; break; }
[ ]*"\n" { token = NEWLINE; break; }
[ ]+ { token = INDENT; break; }
"build" { token = BUILD; break; }
"pool" { token = POOL; break; }
@ -168,13 +167,15 @@ bool Lexer::PeekToken(Token token) {
void Lexer::EatWhitespace() {
const char* p = ofs_;
const char* q;
for (;;) {
ofs_ = p;
/*!re2c
[ ]+ { continue; }
"$\n" { continue; }
nul { break; }
[^] { break; }
[ ]+ { continue; }
"$\r\n" { continue; }
"$\n" { continue; }
nul { break; }
[^] { break; }
*/
}
}
@ -207,6 +208,11 @@ bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) {
eval->AddText(StringPiece(start, p - start));
continue;
}
"\r\n" {
if (path)
p = start;
break;
}
[ :|\n] {
if (path) {
p = start;
@ -226,6 +232,9 @@ bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) {
eval->AddText(StringPiece(" ", 1));
continue;
}
"$\r\n"[ ]* {
continue;
}
"$\n"[ ]* {
continue;
}

View File

@ -870,22 +870,18 @@ TEST_F(ParserTest, UTF8) {
" description = compilaci\xC3\xB3\n"));
}
// We might want to eventually allow CRLF to be nice to Windows developers,
// but for now just verify we error out with a nice message.
TEST_F(ParserTest, CRLF) {
State state;
ManifestParser parser(&state, NULL);
string err;
EXPECT_FALSE(parser.ParseTest("# comment with crlf\r\n",
&err));
EXPECT_EQ("input:1: lexing error\n",
err);
EXPECT_FALSE(parser.ParseTest("foo = foo\nbar = bar\r\n",
&err));
EXPECT_EQ("input:2: carriage returns are not allowed, use newlines\n"
"bar = bar\r\n"
" ^ near here",
err);
EXPECT_TRUE(parser.ParseTest("# comment with crlf\r\n", &err));
EXPECT_TRUE(parser.ParseTest("foo = foo\nbar = bar\r\n", &err));
EXPECT_TRUE(parser.ParseTest(
"pool link_pool\r\n"
" depth = 15\r\n\r\n"
"rule xyz\r\n"
" command = something$expand \r\n"
" description = YAY!\r\n",
&err));
}

View File

@ -106,6 +106,12 @@ bool CanonicalizePath(char* path, size_t* len, string* err) {
return false;
}
#ifdef _WIN32
for (char* c = path; *c; ++c)
if (*c == '\\')
*c = '/';
#endif
const int kMaxPathComponents = 30;
char* components[kMaxPathComponents];
int component_count = 0;
@ -280,7 +286,38 @@ void GetWin32EscapedString(const string& input, string* result) {
}
int ReadFile(const string& path, string* contents, string* err) {
FILE* f = fopen(path.c_str(), "r");
#ifdef _WIN32
// This makes a ninja run on a set of 1500 manifest files about 4% faster
// than using the generic fopen code below.
err->clear();
HANDLE f = ::CreateFile(path.c_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (f == INVALID_HANDLE_VALUE) {
err->assign(GetLastErrorString());
return -ENOENT;
}
for (;;) {
DWORD len;
char buf[64 << 10];
if (!::ReadFile(f, buf, sizeof(buf), &len, NULL)) {
err->assign(GetLastErrorString());
contents->clear();
return -1;
}
if (len == 0)
break;
contents->append(buf, len);
}
::CloseHandle(f);
return 0;
#else
FILE* f = fopen(path.c_str(), "rb");
if (!f) {
err->assign(strerror(errno));
return -errno;
@ -299,6 +336,7 @@ int ReadFile(const string& path, string* contents, string* err) {
}
fclose(f);
return 0;
#endif
}
void SetCloseOnExec(int fd) {

View File

@ -84,6 +84,72 @@ TEST(CanonicalizePath, PathSamples) {
EXPECT_EQ("", path);
}
#ifdef _WIN32
TEST(CanonicalizePath, PathSamplesWindows) {
string path;
string err;
EXPECT_FALSE(CanonicalizePath(&path, &err));
EXPECT_EQ("empty path", err);
path = "foo.h"; err = "";
EXPECT_TRUE(CanonicalizePath(&path, &err));
EXPECT_EQ("foo.h", path);
path = ".\\foo.h";
EXPECT_TRUE(CanonicalizePath(&path, &err));
EXPECT_EQ("foo.h", path);
path = ".\\foo\\.\\bar.h";
EXPECT_TRUE(CanonicalizePath(&path, &err));
EXPECT_EQ("foo/bar.h", path);
path = ".\\x\\foo\\..\\bar.h";
EXPECT_TRUE(CanonicalizePath(&path, &err));
EXPECT_EQ("x/bar.h", path);
path = ".\\x\\foo\\..\\..\\bar.h";
EXPECT_TRUE(CanonicalizePath(&path, &err));
EXPECT_EQ("bar.h", path);
path = "foo\\\\bar";
EXPECT_TRUE(CanonicalizePath(&path, &err));
EXPECT_EQ("foo/bar", path);
path = "foo\\\\.\\\\..\\\\\\bar";
EXPECT_TRUE(CanonicalizePath(&path, &err));
EXPECT_EQ("bar", path);
path = ".\\x\\..\\foo\\..\\..\\bar.h";
EXPECT_TRUE(CanonicalizePath(&path, &err));
EXPECT_EQ("../bar.h", path);
path = "foo\\.\\.";
EXPECT_TRUE(CanonicalizePath(&path, &err));
EXPECT_EQ("foo", path);
path = "foo\\bar\\..";
EXPECT_TRUE(CanonicalizePath(&path, &err));
EXPECT_EQ("foo", path);
path = "foo\\.hidden_bar";
EXPECT_TRUE(CanonicalizePath(&path, &err));
EXPECT_EQ("foo/.hidden_bar", path);
path = "\\foo";
EXPECT_TRUE(CanonicalizePath(&path, &err));
EXPECT_EQ("/foo", path);
path = "\\\\foo";
EXPECT_TRUE(CanonicalizePath(&path, &err));
EXPECT_EQ("//foo", path);
path = "\\";
EXPECT_TRUE(CanonicalizePath(&path, &err));
EXPECT_EQ("", path);
}
#endif
TEST(CanonicalizePath, EmptyResult) {
string path;
string err;